From 74cbebbd3334ac0063761c1a97a88ee000cc2a57 Mon Sep 17 00:00:00 2001 From: yuyr Date: Fri, 28 Aug 2026 17:42:04 +0800 Subject: [PATCH] =?UTF-8?q?20260828=20=E6=94=B6=E5=8F=A3Feature151=20TA=20?= =?UTF-8?q?constraints=E4=BC=98=E5=8C=96=E4=B8=8E=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/soak/build_portable_soak_package.sh | 1 + scripts/soak/portable-soak.env.example | 9 + .../generate_afrinic_current_holdings.py | 178 ++ src/cli.rs | 34 +- src/cli/tests.rs | 22 + src/lib.rs | 2 + src/policy.rs | 7 + src/ta_constraints.rs | 1139 ++++++++++ src/validation/objects.rs | 113 + src/validation/run_tree_from_tal.rs | 4 + src/validation/tree_parallel.rs | 15 +- src/validation/tree_runner.rs | 101 +- src/verification_only.rs | 16 + .../afrinic-current-ipv4-deny.constraints | 1842 +++++++++++++++++ .../afrinic-full-ipv4-deny.constraints | 394 ++++ .../local-custom-allow.constraints | 2 + .../local-custom-deny.constraints | 3 + .../ripe-ncc-afrinic-deny.constraints | 12 + 18 files changed, 3854 insertions(+), 40 deletions(-) create mode 100755 scripts/ta_constraints/generate_afrinic_current_holdings.py create mode 100644 src/ta_constraints.rs create mode 100644 tests/fixtures/ta_constraints/afrinic-current-ipv4-deny.constraints create mode 100644 tests/fixtures/ta_constraints/afrinic-full-ipv4-deny.constraints create mode 100644 tests/fixtures/ta_constraints/local-custom-allow.constraints create mode 100644 tests/fixtures/ta_constraints/local-custom-deny.constraints create mode 100644 tests/fixtures/ta_constraints/ripe-ncc-afrinic-deny.constraints diff --git a/scripts/soak/build_portable_soak_package.sh b/scripts/soak/build_portable_soak_package.sh index 779d7ca..0cee88c 100755 --- a/scripts/soak/build_portable_soak_package.sh +++ b/scripts/soak/build_portable_soak_package.sh @@ -121,6 +121,7 @@ done cp -a "$REPO_ROOT/tests/fixtures/tal" "$STAGE_DIR/fixtures/" cp -a "$REPO_ROOT/tests/fixtures/ta" "$STAGE_DIR/fixtures/" +cp -a "$REPO_ROOT/tests/fixtures/ta_constraints" "$STAGE_DIR/fixtures/" cp -a "$REPO_ROOT/scripts/periodic" "$STAGE_DIR/scripts/" cp -a "$REPO_ROOT/scripts/cir" "$STAGE_DIR/scripts/" cp -a "$REPO_ROOT/scripts/inter_rp" "$STAGE_DIR/scripts/" diff --git a/scripts/soak/portable-soak.env.example b/scripts/soak/portable-soak.env.example index b9c9edd..26544fc 100644 --- a/scripts/soak/portable-soak.env.example +++ b/scripts/soak/portable-soak.env.example @@ -100,6 +100,15 @@ ENABLE_CHILD_CERTIFICATE_VALIDATION_CACHE=0 # 传给 rpki 子进程的额外参数。多个参数用空格分隔。 # 示例:RPKI_EXTRA_ARGS="--enable-roa-validation-cache" # 实验性 transport 预热:RPKI_EXTRA_ARGS="--enable-transport-request-prefetch --enable-roa-validation-cache" +# TA constraints 的 RIPE 单 TA soak 示例(policy 并非默认启用): +# RIRS=ripe +# RPKI_EXTRA_ARGS="--ta-constraints ripe-ncc=${PACKAGE_ROOT}/fixtures/ta_constraints/ripe-ncc-afrinic-deny.constraints" +# all5 性能 A/B 的完整 AFRINIC IPv4 deny 示例(仅四个非 AFRINIC TA): +# RIRS=afrinic,apnic,arin,lacnic,ripe +# RPKI_EXTRA_ARGS="--ta-constraints apnic-rfc7730-https=${PACKAGE_ROOT}/fixtures/ta_constraints/afrinic-full-ipv4-deny.constraints --ta-constraints arin=${PACKAGE_ROOT}/fixtures/ta_constraints/afrinic-full-ipv4-deny.constraints --ta-constraints lacnic=${PACKAGE_ROOT}/fixtures/ta_constraints/afrinic-full-ipv4-deny.constraints --ta-constraints ripe-ncc=${PACKAGE_ROOT}/fixtures/ta_constraints/afrinic-full-ipv4-deny.constraints" +# M7 current-only AFRINIC holdings(delegated status=allocated/assigned;不含 legacy/ERX): +# RIRS=afrinic,apnic,arin,lacnic,ripe +# RPKI_EXTRA_ARGS="--ta-constraints apnic-rfc7730-https=${PACKAGE_ROOT}/fixtures/ta_constraints/afrinic-current-ipv4-deny.constraints --ta-constraints arin=${PACKAGE_ROOT}/fixtures/ta_constraints/afrinic-current-ipv4-deny.constraints --ta-constraints lacnic=${PACKAGE_ROOT}/fixtures/ta_constraints/afrinic-current-ipv4-deny.constraints --ta-constraints ripe-ncc=${PACKAGE_ROOT}/fixtures/ta_constraints/afrinic-current-ipv4-deny.constraints" RPKI_EXTRA_ARGS="" # 所有出站 HTTP 请求(RRDP / TAL / TA / 死 repo 探针)的 User-Agent。 diff --git a/scripts/ta_constraints/generate_afrinic_current_holdings.py b/scripts/ta_constraints/generate_afrinic_current_holdings.py new file mode 100755 index 0000000..622523a --- /dev/null +++ b/scripts/ta_constraints/generate_afrinic_current_holdings.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 +"""Generate a current-only AFRINIC IPv4 holdings constraints fixture. + +The AFRINIC delegated extended format contains one IPv4 allocation/assignment +per line. This tool keeps only active registry holdings (``allocated`` and +``assigned``), computes their exact union, and emits an allow-all policy with +the union as IPv4 deny entries. ``reserved`` and ``available`` records are +intentionally excluded. +""" + +from __future__ import annotations + +import argparse +import datetime as dt +import hashlib +import ipaddress +import pathlib +import sys +import urllib.error +import urllib.request +from collections import Counter +from typing import Iterable + + +DEFAULT_SOURCE_URL = ( + "https://ftp.afrinic.net/pub/stats/afrinic/" + "delegated-afrinic-extended-latest" +) +ACTIVE_STATUSES = {"allocated", "assigned"} + + +def merge_intervals( + intervals: Iterable[tuple[int, int]], +) -> list[tuple[int, int]]: + merged: list[tuple[int, int]] = [] + for start, end in sorted(intervals): + if merged and start <= merged[-1][1] + 1: + merged[-1] = (merged[-1][0], max(merged[-1][1], end)) + else: + merged.append((start, end)) + return merged + + +def parse_source(raw: bytes) -> tuple[list[tuple[int, int]], Counter[str], Counter[str]]: + status_counts: Counter[str] = Counter() + ipv4_status_counts: Counter[str] = Counter() + active: list[tuple[int, int]] = [] + for line_number, raw_line in enumerate(raw.decode("utf-8", "replace").splitlines(), 1): + if not raw_line or raw_line.startswith("#"): + continue + fields = raw_line.split("|") + if len(fields) < 7 or fields[2] != "ipv4": + continue + status = fields[6] + status_counts[status] += 1 + ipv4_status_counts[status] += 1 + if status not in ACTIVE_STATUSES: + continue + try: + start = int(ipaddress.IPv4Address(fields[3])) + count = int(fields[4]) + except (ValueError, TypeError) as error: + raise ValueError(f"line {line_number}: invalid IPv4 record: {raw_line}") from error + if count <= 0 or start + count > 1 << 32: + raise ValueError(f"line {line_number}: invalid IPv4 range: {raw_line}") + active.append((start, start + count - 1)) + return active, status_counts, ipv4_status_counts + + +def render( + source_url: str, + retrieved: str, + source_sha256: str, + source_ipv4_count: int, + status_counts: Counter[str], + active_source: list[tuple[int, int]], +) -> tuple[str, dict[str, int]]: + merged = merge_intervals(active_source) + networks = [ + network + for start, end in merged + for network in ipaddress.summarize_address_range( + ipaddress.IPv4Address(start), ipaddress.IPv4Address(end) + ) + ] + networks.sort(key=lambda network: int(network.network_address)) + active_addresses = sum(end - start + 1 for start, end in merged) + counts = { + "source_ipv4_records": source_ipv4_count, + "allocated": status_counts["allocated"], + "assigned": status_counts["assigned"], + "reserved": status_counts["reserved"], + "available": status_counts["available"], + "selected_source_records": len(active_source), + "selected_addresses": active_addresses, + "merged_intervals": len(merged), + "cidr_rules": len(networks), + } + lines = [ + "# Feature #151 M7 current AFRINIC IPv4 holdings deny fixture.", + "#", + "# Source: AFRINIC delegated extended latest.", + f"# URL: {source_url}", + f"# Retrieved: {retrieved}", + f"# Source SHA-256: {source_sha256}", + "#", + "# Selection: IPv4 records with status allocated or assigned only.", + "# Excluded statuses: reserved and available (not current registry holdings).", + ( + "# Source IPv4 records: {source_ipv4_records}; allocated={allocated}; " + "assigned={assigned}; reserved={reserved}; available={available}." + ).format(**counts), + "# Selected source records: {selected_source_records}; selected addresses: {selected_addresses}.".format( + **counts + ), + "# Overlap/adjacency union intervals: {merged_intervals}; exact CIDR deny rules: {cidr_rules}.".format( + **counts + ), + "# IPv6 and ASN remain allow-all to isolate the current AFRINIC IPv4 policy.", + "allow 0.0.0.0/0", + "allow ::/0", + "allow 0 - 4294967295", + "", + ] + lines.extend(f"deny {network}" for network in networks) + return "\n".join(lines) + "\n", counts + + +def fetch_source(url: str, timeout: float) -> bytes: + request = urllib.request.Request( + url, + headers={"User-Agent": "rpki-dev-feature151-afrinic-holdings/1.0"}, + ) + with urllib.request.urlopen(request, timeout=timeout) as response: + return response.read() + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + source = parser.add_mutually_exclusive_group() + source.add_argument("--url", default=DEFAULT_SOURCE_URL, help="AFRINIC source URL") + source.add_argument("--input", type=pathlib.Path, help="local delegated file for replay") + parser.add_argument("--output", type=pathlib.Path, required=True) + parser.add_argument("--retrieved", help="UTC date recorded in the fixture header") + parser.add_argument("--timeout", type=float, default=60.0) + args = parser.parse_args() + + source_url = args.url if args.input is None else f"file://{args.input.resolve()}" + raw = args.input.read_bytes() if args.input is not None else fetch_source(args.url, args.timeout) + retrieved = args.retrieved or dt.datetime.now(dt.timezone.utc).date().isoformat() + active, status_counts, ipv4_status_counts = parse_source(raw) + rendered, counts = render( + source_url, + retrieved, + hashlib.sha256(raw).hexdigest(), + sum(ipv4_status_counts.values()), + status_counts, + active, + ) + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(rendered, encoding="utf-8") + print( + "generated {output}: {cidr_rules} CIDRs from {selected_source_records} active " + "records ({selected_addresses} addresses); source_sha256={sha256}".format( + output=args.output, + sha256=hashlib.sha256(raw).hexdigest(), + **counts, + ) + ) + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except (OSError, ValueError, urllib.error.URLError) as error: + print(f"error: {error}", file=sys.stderr) + raise SystemExit(2) diff --git a/src/cli.rs b/src/cli.rs index 61be2f1..a30084b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -26,6 +26,7 @@ use crate::parallel::config::{ParallelPhase1Config, ParallelPhase2Config}; use crate::parallel::types::TalInputSpec; use crate::policy::{Policy, ResourceValidationMode, StrictPolicy}; use crate::storage::{RocksStore, VcirStorageSummary}; +use crate::ta_constraints::TaConstraintsByTal; use crate::validation::run_tree_from_tal::{ RunTreeFromTalAuditOutput, run_tree_from_multiple_tals_parallel_phase2_audit, run_tree_from_multiple_tals_parallel_phase2_audit_with_timing, @@ -131,6 +132,7 @@ pub struct CliArgs { pub parallel_phase1_config: ParallelPhase1Config, pub parallel_phase2_config: ParallelPhase2Config, pub tal_inputs: Vec, + pub ta_constraints: TaConstraintsByTal, pub db_path: PathBuf, pub raw_store_db: Option, @@ -210,6 +212,8 @@ Options: --raw-store-db External raw-by-hash store DB path (optional) --repo-bytes-db External repo object bytes DB path (optional) --policy Policy TOML path (optional) + --ta-constraints = + Apply local EE-resource constraints to one TAL (repeatable); adjacent .constraints files are auto-discovered --strict [policies] Enable strict policies (default all; comma list: name,cms-der,signed-attrs; none disables) --resource-validation-mode Resource certificate validation mode (default: validation-update-03) @@ -318,6 +322,7 @@ pub fn parse_args(argv: &[String]) -> Result { let mut tal_urls: Vec = Vec::new(); let mut tal_paths: Vec = Vec::new(); let mut ta_paths: Vec = Vec::new(); + let mut ta_constraint_specs: Vec = Vec::new(); let mut parallel_phase1_cfg = ParallelPhase1Config::default(); let mut parallel_phase2_cfg = ParallelPhase2Config::default(); let mut dead_repo_blacklist_path: Option = None; @@ -427,6 +432,13 @@ pub fn parse_args(argv: &[String]) -> Result { let v = argv.get(i).ok_or("--ta-path requires a value")?; ta_paths.push(PathBuf::from(v)); } + "--ta-constraints" => { + i += 1; + let v = argv + .get(i) + .ok_or("--ta-constraints requires =")?; + ta_constraint_specs.push(v.clone()); + } "--parallel-max-repo-sync-workers-global" => { i += 1; let v = argv @@ -989,6 +1001,12 @@ pub fn parse_args(argv: &[String]) -> Result { )); } } + if verification_only && !ta_constraint_specs.is_empty() { + return Err(format!( + "--ta-constraints is not supported with --verification-only\\n\\n{}", + usage() + )); + } let tal_url = tal_urls.first().cloned(); let tal_path = tal_paths.first().cloned(); let ta_path = ta_paths.first().cloned(); @@ -1147,6 +1165,7 @@ pub fn parse_args(argv: &[String]) -> Result { tal_inputs.extend(tal_paths.iter().cloned().map(TalInputSpec::from_file_path)); } } + let ta_constraints = TaConstraintsByTal::load_for_tals(&tal_inputs, &ta_constraint_specs)?; if dead_repo_blacklist_fail_threshold.is_some() && dead_repo_blacklist_path.is_none() { return Err( @@ -1183,6 +1202,7 @@ pub fn parse_args(argv: &[String]) -> Result { parallel_phase1_config: parallel_phase1_cfg, parallel_phase2_config: parallel_phase2_cfg, tal_inputs, + ta_constraints, db_path, raw_store_db, repo_bytes_db, @@ -2090,7 +2110,11 @@ where H: crate::sync::rrdp::Fetcher + Clone + 'static, R: crate::fetch::rsync::RsyncFetcher + Clone + 'static, { - if args.verification_only || args.tal_inputs.len() > 1 { + // The multi-TAL entry point preserves the TAL id supplied by the CLI. + // A single-file TAL may otherwise derive its id from the embedded TA URI, + // which is intentionally different from the local filename used for + // adjacent .constraints discovery. + if args.verification_only || args.tal_inputs.len() > 1 || !policy.ta_constraints.is_empty() { return if let Some(t) = timing { run_tree_from_multiple_tals_parallel_phase2_audit_with_timing( store, @@ -2322,6 +2346,10 @@ pub fn run(argv: &[String]) -> Result<(), String> { if args.disable_rrdp { policy.sync_preference = crate::policy::SyncPreference::RsyncOnly; } + policy.ta_constraints = args.ta_constraints.clone(); + for warning in policy.ta_constraints.configuration_warnings() { + eprintln!("warning: {warning}"); + } } let validation_time = args .validation_time @@ -3050,6 +3078,10 @@ pub fn run(argv: &[String]) -> Result<(), String> { }, args.rsync_scope_policy, )?; + if !policy.ta_constraints.is_empty() { + contract.ta_constraints_fingerprint = + Some(policy.ta_constraints.fingerprint_sha256_hex()); + } contract.fallback_replay_selections = crate::verification_only::collect_fallback_replay_selections( store.as_ref(), diff --git a/src/cli/tests.rs b/src/cli/tests.rs index 4253ea5..0ae1ea7 100644 --- a/src/cli/tests.rs +++ b/src/cli/tests.rs @@ -23,6 +23,7 @@ fn parse_help_returns_usage() { assert!(err.contains("--memory-trim-after-validation"), "{err}"); assert!(err.contains("--enable-roa-validation-cache"), "{err}"); assert!(err.contains("--resource-validation-mode"), "{err}"); + assert!(err.contains("--ta-constraints"), "{err}"); assert!(err.contains("--max-ca-depth"), "{err}"); assert!(err.contains("default: 32"), "{err}"); assert!( @@ -37,6 +38,26 @@ fn parse_help_returns_usage() { assert!(!err.contains("--parallel-phase2 "), "{err}"); } +#[test] +fn parse_accepts_explicit_ta_constraints_for_known_tal() { + let dir = tempfile::tempdir().expect("tmpdir"); + let constraints_path = dir.path().join("example.constraints"); + std::fs::write(&constraints_path, "allow 192.0.2.0/24\n").expect("write constraints"); + let argv = vec![ + "rpki".to_string(), + "--db".to_string(), + "db".to_string(), + "--tal-path".to_string(), + "example.tal".to_string(), + "--ta-path".to_string(), + "example.cer".to_string(), + "--ta-constraints".to_string(), + format!("example={}", constraints_path.display()), + ]; + let args = parse_args(&argv).expect("parse args"); + assert!(!args.ta_constraints.is_empty()); +} + #[test] fn parse_rejects_unknown_argument() { let argv = vec![ @@ -119,6 +140,7 @@ fn verification_policy_preserves_contract_identity_and_selects_only_bound_fallba sync_preference: crate::policy::SyncPreference::RrdpThenRsync, ..Policy::default() }, + ta_constraints_fingerprint: None, max_ca_depth: 32, max_instances: None, cache: crate::verification_only::ValidationCacheContract { diff --git a/src/lib.rs b/src/lib.rs index 3aa36d6..f5d56c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,8 @@ pub mod storage; #[cfg(feature = "full")] pub mod sync; #[cfg(feature = "full")] +pub mod ta_constraints; +#[cfg(feature = "full")] pub mod tools; #[cfg(feature = "full")] pub mod validation; diff --git a/src/policy.rs b/src/policy.rs index 556a683..67142ed 100644 --- a/src/policy.rs +++ b/src/policy.rs @@ -1,6 +1,8 @@ use serde::{Deserialize, Serialize}; use std::collections::BTreeSet; +use crate::ta_constraints::TaConstraintsByTal; + #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum SyncPreference { @@ -133,6 +135,10 @@ pub struct Policy { /// the already contract-bound VCIR fallback branch while replaying a run. #[serde(skip)] pub verification_forced_vcir_reuse_manifest_uris: BTreeSet, + /// Locally configured, per-TAL EE certificate resource constraints. They + /// are intentionally CLI/runtime-only rather than policy-file input. + #[serde(skip)] + pub ta_constraints: TaConstraintsByTal, } impl Default for Policy { @@ -144,6 +150,7 @@ impl Default for Policy { resource_validation_mode: ResourceValidationMode::default(), strict: StrictPolicy::default(), verification_forced_vcir_reuse_manifest_uris: BTreeSet::new(), + ta_constraints: TaConstraintsByTal::default(), } } } diff --git a/src/ta_constraints.rs b/src/ta_constraints.rs new file mode 100644 index 0000000..cf9dea1 --- /dev/null +++ b/src/ta_constraints.rs @@ -0,0 +1,1139 @@ +//! Locally configured constraints for the resources carried by RPKI EE +//! certificates. The configuration format follows +//! draft-ietf-sidrops-constraining-rpki-trust-anchors. + +use std::collections::{BTreeMap, BTreeSet}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::ops::Deref; +use std::path::{Path, PathBuf}; + +use sha2::Digest; + +use crate::data_model::rc::{ + Afi, AsIdOrRange, AsIdentifierChoice, IpAddressChoice, IpAddressOrRange, IpResourceSet, + ResourceCertificate, +}; +use crate::parallel::types::{TalInputSpec, TalSource}; + +const LINEAR_INTERVAL_THRESHOLD: usize = 10; + +/// A normalized rule set with a small-set linear path and a large-set tree path. +/// +/// The parser constructs this only after sorting and merging overlapping or +/// adjacent intervals. The tree lookup therefore only needs to inspect the +/// predecessor of a target interval; normalized intervals are disjoint and +/// sorted by their lower bound. +#[derive(Clone)] +struct IntervalIndex { + rules: Vec, + lookup: IntervalLookup, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +enum IntervalLookup { + Linear, + Tree(BTreeMap), +} + +impl std::fmt::Debug for IntervalIndex { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // Keep derived acceleration data out of Debug so diagnostics continue + // to describe canonical rules rather than their index layout. + self.rules.fmt(formatter) + } +} + +impl PartialEq for IntervalIndex { + fn eq(&self, other: &Self) -> bool { + self.rules == other.rules + } +} + +impl Eq for IntervalIndex {} + +impl Deref for IntervalIndex { + type Target = [I]; + + fn deref(&self) -> &Self::Target { + &self.rules + } +} + +trait IntervalValue { + fn start(&self) -> u128; + fn end(&self) -> u128; + fn overlaps(&self, other: &Self) -> bool; +} + +impl IntervalIndex { + fn new(rules: Vec) -> Self { + debug_assert!( + rules + .windows(2) + .all(|window| { window[0].end().saturating_add(1) < window[1].start() }) + ); + let lookup = if rules.len() > LINEAR_INTERVAL_THRESHOLD { + let tree = rules + .iter() + .map(|rule| (rule.start(), rule.end())) + .collect(); + IntervalLookup::Tree(tree) + } else { + IntervalLookup::Linear + }; + Self { rules, lookup } + } + + fn any_overlaps(&self, target: &I) -> bool { + match &self.lookup { + IntervalLookup::Linear => self.rules.iter().any(|entry| entry.overlaps(target)), + IntervalLookup::Tree(tree) => tree + .range(..=target.end()) + .next_back() + .map(|(_, end)| *end >= target.start()) + .unwrap_or(false), + } + } + + fn fully_covers(&self, target: &I) -> bool { + match &self.lookup { + IntervalLookup::Linear => self.fully_covers_linear(target), + IntervalLookup::Tree(tree) => tree + .range(..=target.start()) + .next_back() + .map(|(_, end)| *end >= target.end()) + .unwrap_or(false), + } + } + + fn fully_covers_linear(&self, target: &I) -> bool { + let mut cursor = target.start(); + for entry in &self.rules { + if entry.end() < cursor { + continue; + } + if entry.start() > cursor { + return false; + } + if entry.end() >= target.end() { + return true; + } + cursor = entry.end().saturating_add(1); + } + false + } + + #[cfg(test)] + fn uses_tree(&self) -> bool { + matches!(self.lookup, IntervalLookup::Tree(_)) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TaConstraintsByTal { + by_tal_id: BTreeMap>, + /// Stable semantic digest of the normalized rules. This is computed once + /// while loading the run policy so cache identity checks do not serialize + /// every rule for every publication point. + fingerprint: [u8; 32], +} + +impl TaConstraintsByTal { + pub fn load_for_tals( + tal_inputs: &[TalInputSpec], + explicit_specs: &[String], + ) -> Result { + let tal_ids = tal_inputs + .iter() + .map(|input| input.tal_id.as_str()) + .collect::>(); + let mut explicit_paths = BTreeMap::::new(); + for spec in explicit_specs { + let (tal_id, path) = spec + .split_once('=') + .ok_or_else(|| format!("--ta-constraints expects =, got '{spec}'"))?; + let tal_id = tal_id.trim(); + let path = path.trim(); + if tal_id.is_empty() || path.is_empty() { + return Err(format!( + "--ta-constraints expects non-empty =, got '{spec}'" + )); + } + if !tal_ids.contains(tal_id) { + return Err(format!( + "--ta-constraints references unknown TAL id '{tal_id}'" + )); + } + if explicit_paths + .insert(tal_id.to_string(), PathBuf::from(path)) + .is_some() + { + return Err(format!( + "--ta-constraints specifies TAL id '{tal_id}' more than once" + )); + } + } + + let mut by_tal_id = BTreeMap::new(); + for input in tal_inputs { + let path = explicit_paths + .get(&input.tal_id) + .cloned() + .or_else(|| adjacent_constraints_path(&input.source).filter(|path| path.is_file())); + if let Some(path) = path { + let constraints = TaConstraints::from_file(&path).map_err(|error| { + format!( + "load TA constraints for '{}' from {} failed: {error}", + input.tal_id, + path.display() + ) + })?; + by_tal_id.insert(input.tal_id.clone(), std::sync::Arc::new(constraints)); + } + } + let fingerprint = constraints_fingerprint(&by_tal_id); + Ok(Self { + by_tal_id, + fingerprint, + }) + } + + pub fn for_tal(&self, tal_id: &str) -> Option<&TaConstraints> { + self.by_tal_id.get(tal_id).map(std::sync::Arc::as_ref) + } + + /// Return the immutable, process-local snapshot for a TAL without cloning + /// the rule trees. Phase-2 object workers own this `Arc` in their task + /// payload, while scoped stage workers borrow the same policy map. + pub(crate) fn shared_for_tal(&self, tal_id: &str) -> Option> { + self.by_tal_id.get(tal_id).cloned() + } + + pub fn is_empty(&self) -> bool { + self.by_tal_id.is_empty() + } + + pub fn configuration_warnings(&self) -> Vec { + self.by_tal_id + .iter() + .flat_map(|(tal_id, constraints)| { + constraints.warnings().iter().map(move |warning| { + format!( + "TA constraints for TAL '{tal_id}' ({}): {warning}", + constraints.source().display() + ) + }) + }) + .collect() + } + + /// Return the precomputed SHA-256 digest of the normalized semantic rules. + /// + /// The returned bytes are stable across source paths and derived index + /// layouts, and are intentionally borrowed so hot-path cache lookups do + /// not allocate. + pub fn fingerprint_bytes(&self) -> &[u8] { + &self.fingerprint + } + + pub fn fingerprint_sha256_hex(&self) -> String { + hex::encode(self.fingerprint) + } +} + +impl Default for TaConstraintsByTal { + fn default() -> Self { + let by_tal_id = BTreeMap::new(); + let fingerprint = constraints_fingerprint(&by_tal_id); + Self { + by_tal_id, + fingerprint, + } + } +} + +const CONSTRAINTS_FINGERPRINT_VERSION: &[u8] = b"ta-constraints-semantic-v1"; + +fn constraints_fingerprint( + by_tal_id: &BTreeMap>, +) -> [u8; 32] { + let mut hasher = sha2::Sha256::new(); + hasher.update(CONSTRAINTS_FINGERPRINT_VERSION); + hasher.update((by_tal_id.len() as u64).to_be_bytes()); + for (tal_id, constraints) in by_tal_id { + update_length_prefixed(&mut hasher, tal_id.as_bytes()); + update_ip_intervals(&mut hasher, b"allow-v4", &constraints.allow_v4); + update_ip_intervals(&mut hasher, b"deny-v4", &constraints.deny_v4); + update_ip_intervals(&mut hasher, b"allow-v6", &constraints.allow_v6); + update_ip_intervals(&mut hasher, b"deny-v6", &constraints.deny_v6); + update_as_intervals(&mut hasher, b"allow-asn", &constraints.allow_asn); + update_as_intervals(&mut hasher, b"deny-asn", &constraints.deny_asn); + } + let digest = hasher.finalize(); + let mut out = [0u8; 32]; + out.copy_from_slice(&digest); + out +} + +fn update_length_prefixed(hasher: &mut sha2::Sha256, value: &[u8]) { + hasher.update((value.len() as u64).to_be_bytes()); + hasher.update(value); +} + +fn update_ip_intervals( + hasher: &mut sha2::Sha256, + label: &[u8], + intervals: &IntervalIndex, +) { + update_length_prefixed(hasher, label); + hasher.update((intervals.rules.len() as u64).to_be_bytes()); + for interval in &intervals.rules { + hasher.update([match interval.afi { + Afi::Ipv4 => 4, + Afi::Ipv6 => 6, + }]); + hasher.update(interval.min.to_be_bytes()); + hasher.update(interval.max.to_be_bytes()); + } +} + +fn update_as_intervals( + hasher: &mut sha2::Sha256, + label: &[u8], + intervals: &IntervalIndex, +) { + update_length_prefixed(hasher, label); + hasher.update((intervals.rules.len() as u64).to_be_bytes()); + for interval in &intervals.rules { + hasher.update(interval.min.to_be_bytes()); + hasher.update(interval.max.to_be_bytes()); + } +} + +fn adjacent_constraints_path(source: &TalSource) -> Option { + match source { + TalSource::FilePath(path) => Some(path.with_extension("constraints")), + TalSource::FilePathWithTa { tal_path, .. } => Some(tal_path.with_extension("constraints")), + TalSource::Url(_) | TalSource::DerBytes { .. } => None, + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TaConstraints { + source: PathBuf, + allow_v4: IntervalIndex, + deny_v4: IntervalIndex, + allow_v6: IntervalIndex, + deny_v6: IntervalIndex, + allow_asn: IntervalIndex, + deny_asn: IntervalIndex, + warnings: Vec, +} + +impl TaConstraints { + pub fn from_file(path: &Path) -> Result { + let contents = std::fs::read_to_string(path) + .map_err(|error| format!("read {}: {error}", path.display()))?; + Self::parse(path.to_path_buf(), &contents) + } + + pub fn warnings(&self) -> &[String] { + &self.warnings + } + + pub fn source(&self) -> &Path { + &self.source + } + + pub fn validate_ee_certificate( + &self, + certificate: &ResourceCertificate, + ) -> Result<(), TaConstraintsViolation> { + if let Some(ip_resources) = certificate.tbs.extensions.ip_resources.as_ref() { + self.validate_ip_resources(ip_resources)?; + } + if let Some(as_resources) = certificate.tbs.extensions.as_resources.as_ref() { + self.validate_as_choice("AS", as_resources.asnum.as_ref())?; + self.validate_as_choice("RDI", as_resources.rdi.as_ref())?; + } + Ok(()) + } + + fn parse(source: PathBuf, contents: &str) -> Result { + let mut allow_v4 = Vec::new(); + let mut deny_v4 = Vec::new(); + let mut allow_v6 = Vec::new(); + let mut deny_v6 = Vec::new(); + let mut allow_asn = Vec::new(); + let mut deny_asn = Vec::new(); + + for (index, raw_line) in contents.lines().enumerate() { + let line_number = index + 1; + let line = raw_line.split('#').next().unwrap_or("").trim(); + if line.is_empty() { + continue; + } + let mut words = line.split_whitespace(); + let action = words.next().expect("non-empty line has first word"); + let resource = words.collect::>().join(" "); + if resource.is_empty() { + return Err(format!( + "line {line_number}: missing resource after '{action}'" + )); + } + let allow = match action { + "allow" => true, + "deny" => false, + _ => { + return Err(format!( + "line {line_number}: expected 'allow' or 'deny', got '{action}'" + )); + } + }; + if looks_like_ip_resource(&resource) { + let interval = parse_ip_interval(&resource).map_err(|error| { + format!("line {line_number}: invalid IP resource '{resource}': {error}") + })?; + match (interval.afi, allow) { + (Afi::Ipv4, true) => allow_v4.push(interval), + (Afi::Ipv4, false) => deny_v4.push(interval), + (Afi::Ipv6, true) => allow_v6.push(interval), + (Afi::Ipv6, false) => deny_v6.push(interval), + } + } else { + let interval = parse_as_interval(&resource).map_err(|error| { + format!("line {line_number}: invalid AS resource '{resource}': {error}") + })?; + if allow { + allow_asn.push(interval); + } else { + deny_asn.push(interval); + } + } + } + + let mut warnings = Vec::new(); + normalize_ip_intervals("allow IPv4", &mut allow_v4, &mut warnings); + normalize_ip_intervals("deny IPv4", &mut deny_v4, &mut warnings); + normalize_ip_intervals("allow IPv6", &mut allow_v6, &mut warnings); + normalize_ip_intervals("deny IPv6", &mut deny_v6, &mut warnings); + normalize_as_intervals("allow AS", &mut allow_asn, &mut warnings); + normalize_as_intervals("deny AS", &mut deny_asn, &mut warnings); + + Ok(Self { + source, + allow_v4: IntervalIndex::new(allow_v4), + deny_v4: IntervalIndex::new(deny_v4), + allow_v6: IntervalIndex::new(allow_v6), + deny_v6: IntervalIndex::new(deny_v6), + allow_asn: IntervalIndex::new(allow_asn), + deny_asn: IntervalIndex::new(deny_asn), + warnings, + }) + } + + fn validate_ip_resources( + &self, + resources: &IpResourceSet, + ) -> Result<(), TaConstraintsViolation> { + for family in &resources.families { + let items = match &family.choice { + // Constraints apply to explicit INR listings. EE profiles for + // constrained signed objects already reject inappropriate inherit. + IpAddressChoice::Inherit => continue, + IpAddressChoice::AddressesOrRanges(items) => items, + }; + let (allow, deny) = match family.afi { + Afi::Ipv4 => (&self.allow_v4, &self.deny_v4), + Afi::Ipv6 => (&self.allow_v6, &self.deny_v6), + }; + for item in items { + let interval = ip_item_to_interval(family.afi, item)?; + if deny.any_overlaps(&interval) { + return Err(TaConstraintsViolation(format!( + "{} {} intersects a deny rule in {}", + afi_name(family.afi), + interval, + self.source.display() + ))); + } + if !allow.fully_covers(&interval) { + return Err(TaConstraintsViolation(format!( + "{} {} is not fully contained in allow rules in {}", + afi_name(family.afi), + interval, + self.source.display() + ))); + } + } + } + Ok(()) + } + + fn validate_as_choice( + &self, + kind: &str, + choice: Option<&AsIdentifierChoice>, + ) -> Result<(), TaConstraintsViolation> { + let Some(choice) = choice else { + return Ok(()); + }; + let items = match choice { + AsIdentifierChoice::Inherit => return Ok(()), + AsIdentifierChoice::AsIdsOrRanges(items) => items, + }; + for item in items { + let interval = match item { + AsIdOrRange::Id(value) => AsInterval::new(*value, *value), + AsIdOrRange::Range { min, max } => AsInterval::new(*min, *max), + }; + if self.deny_asn.any_overlaps(&interval) { + return Err(TaConstraintsViolation(format!( + "{kind} {interval} intersects a deny rule in {}", + self.source.display() + ))); + } + if !self.allow_asn.fully_covers(&interval) { + return Err(TaConstraintsViolation(format!( + "{kind} {interval} is not fully contained in allow rules in {}", + self.source.display() + ))); + } + } + Ok(()) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TaConstraintsViolation(pub String); + +impl std::fmt::Display for TaConstraintsViolation { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(formatter) + } +} + +impl std::error::Error for TaConstraintsViolation {} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct IpInterval { + afi: Afi, + min: u128, + max: u128, +} + +impl IpInterval { + fn new(afi: Afi, min: u128, max: u128) -> Self { + Self { afi, min, max } + } + + fn overlaps(&self, other: &Self) -> bool { + self.afi == other.afi && self.min <= other.max && other.min <= self.max + } +} + +impl IntervalValue for IpInterval { + fn start(&self) -> u128 { + self.min + } + + fn end(&self) -> u128 { + self.max + } + + fn overlaps(&self, other: &Self) -> bool { + IpInterval::overlaps(self, other) + } +} + +impl std::fmt::Display for IpInterval { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let min = u128_to_ip(self.afi, self.min); + let max = u128_to_ip(self.afi, self.max); + if min == max { + write!(formatter, "{min}") + } else { + write!(formatter, "{min} - {max}") + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct AsInterval { + min: u32, + max: u32, +} + +impl AsInterval { + fn new(min: u32, max: u32) -> Self { + Self { min, max } + } + + fn overlaps(&self, other: &Self) -> bool { + self.min <= other.max && other.min <= self.max + } +} + +impl IntervalValue for AsInterval { + fn start(&self) -> u128 { + self.min.into() + } + + fn end(&self) -> u128 { + self.max.into() + } + + fn overlaps(&self, other: &Self) -> bool { + AsInterval::overlaps(self, other) + } +} + +impl std::fmt::Display for AsInterval { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.min == self.max { + write!(formatter, "{}", self.min) + } else { + write!(formatter, "{} - {}", self.min, self.max) + } + } +} + +fn looks_like_ip_resource(resource: &str) -> bool { + resource.contains('.') || resource.contains(':') || resource.contains('/') +} + +fn parse_ip_interval(resource: &str) -> Result { + if let Some((raw_min, raw_max)) = resource.split_once('-') { + let min: IpAddr = raw_min.trim().parse().map_err(|_| "invalid range start")?; + let max: IpAddr = raw_max.trim().parse().map_err(|_| "invalid range end")?; + let (afi, min) = ip_to_u128(min); + let (max_afi, max) = ip_to_u128(max); + if afi != max_afi { + return Err("range endpoints use different address families".to_string()); + } + if min > max { + return Err("range start is greater than range end".to_string()); + } + return Ok(IpInterval::new(afi, min, max)); + } + + let (raw_address, raw_prefix_len) = resource + .split_once('/') + .ok_or_else(|| "expected CIDR prefix or range".to_string())?; + let address: IpAddr = raw_address.trim().parse().map_err(|_| "invalid address")?; + let prefix_len: u16 = raw_prefix_len + .trim() + .parse() + .map_err(|_| "invalid prefix length")?; + let (afi, address) = ip_to_u128(address); + let width = match afi { + Afi::Ipv4 => 32, + Afi::Ipv6 => 128, + }; + if prefix_len > width { + return Err(format!("prefix length must be <= {width}")); + } + let host_bits = width - prefix_len; + let mask = if prefix_len == 0 { + 0 + } else { + width_mask(width) << host_bits + }; + let min = address & mask; + let max = min | (!mask & width_mask(width)); + Ok(IpInterval::new(afi, min, max)) +} + +fn parse_as_interval(resource: &str) -> Result { + let parse_asn = |raw: &str| -> Result { + raw.trim() + .strip_prefix("AS") + .or_else(|| raw.trim().strip_prefix("as")) + .unwrap_or(raw.trim()) + .parse::() + .map_err(|_| "expected an ASN in the range 0..4294967295".to_string()) + }; + if let Some((raw_min, raw_max)) = resource.split_once('-') { + let min = parse_asn(raw_min)?; + let max = parse_asn(raw_max)?; + if min > max { + return Err("range start is greater than range end".to_string()); + } + Ok(AsInterval::new(min, max)) + } else { + let value = parse_asn(resource)?; + Ok(AsInterval::new(value, value)) + } +} + +fn normalize_ip_intervals(label: &str, entries: &mut Vec, warnings: &mut Vec) { + entries.sort_by_key(|entry| (entry.min, entry.max)); + let mut normalized = Vec::with_capacity(entries.len()); + for entry in entries.drain(..) { + let Some(last) = normalized.last_mut() else { + normalized.push(entry); + continue; + }; + if entry.min <= last.max { + warnings.push(format!( + "TA constraints {label} rules overlap; normalized without blocking startup" + )); + last.max = last.max.max(entry.max); + } else if entry.min == last.max.saturating_add(1) { + last.max = entry.max; + } else { + normalized.push(entry); + } + } + warnings.sort(); + warnings.dedup(); + *entries = normalized; +} + +fn normalize_as_intervals(label: &str, entries: &mut Vec, warnings: &mut Vec) { + entries.sort_by_key(|entry| (entry.min, entry.max)); + let mut normalized = Vec::with_capacity(entries.len()); + for entry in entries.drain(..) { + let Some(last) = normalized.last_mut() else { + normalized.push(entry); + continue; + }; + if entry.min <= last.max { + warnings.push(format!( + "TA constraints {label} rules overlap; normalized without blocking startup" + )); + last.max = last.max.max(entry.max); + } else if entry.min == last.max.saturating_add(1) { + last.max = entry.max; + } else { + normalized.push(entry); + } + } + warnings.sort(); + warnings.dedup(); + *entries = normalized; +} + +fn ip_item_to_interval( + afi: Afi, + item: &IpAddressOrRange, +) -> Result { + let (min, max) = match item { + IpAddressOrRange::Prefix(prefix) => { + let width = prefix.afi.ub(); + let address = ip_bytes_to_u128(&prefix.addr); + let prefix_len = prefix.prefix_len.min(width); + let host_bits = width - prefix_len; + let mask = if prefix_len == 0 { + 0 + } else { + width_mask(width) << host_bits + }; + let min = address & mask; + (min, min | (!mask & width_mask(width))) + } + IpAddressOrRange::Range(range) => { + (ip_bytes_to_u128(&range.min), ip_bytes_to_u128(&range.max)) + } + }; + if min > max { + return Err(TaConstraintsViolation( + "EE certificate carries an invalid IP range".to_string(), + )); + } + Ok(IpInterval::new(afi, min, max)) +} + +fn ip_to_u128(address: IpAddr) -> (Afi, u128) { + match address { + IpAddr::V4(address) => (Afi::Ipv4, u32::from(address) as u128), + IpAddr::V6(address) => (Afi::Ipv6, u128::from(address)), + } +} + +fn ip_bytes_to_u128(bytes: &[u8]) -> u128 { + bytes + .iter() + .fold(0u128, |value, byte| (value << 8) | u128::from(*byte)) +} + +fn width_mask(width: u16) -> u128 { + if width == 128 { + u128::MAX + } else { + (1u128 << width) - 1 + } +} + +fn u128_to_ip(afi: Afi, value: u128) -> IpAddr { + match afi { + Afi::Ipv4 => IpAddr::V4(Ipv4Addr::from(value as u32)), + Afi::Ipv6 => IpAddr::V6(Ipv6Addr::from(value)), + } +} + +fn afi_name(afi: Afi) -> &'static str { + match afi { + Afi::Ipv4 => "IPv4", + Afi::Ipv6 => "IPv6", + } +} + +#[cfg(test)] +mod tests { + use super::{IntervalIndex, IntervalLookup, TaConstraints, TaConstraintsByTal}; + + fn parse(body: &str) -> TaConstraints { + TaConstraints::parse("test.constraints".into(), body).expect("parse constraints") + } + + fn indexed_ipv4_rules(action: &str, count: usize) -> String { + (0..count) + .map(|index| format!("{action} 10.{}.0.0/16\n", index * 2)) + .collect() + } + + fn indexed_ipv6_rules(action: &str, count: usize) -> String { + (0..count) + .map(|index| format!("{action} 2001:db8:{}::/48\n", index * 2)) + .collect() + } + + fn indexed_asn_rules(action: &str, count: usize) -> String { + (0..count) + .map(|index| format!("{action} {}\n", 65000 + index * 2)) + .collect() + } + + #[test] + fn deny_has_precedence_and_unlisted_resources_are_denied() { + let constraints = parse("allow 192.0.2.0/24\ndeny 192.0.2.128/25\n"); + assert!( + constraints + .allow_v4 + .fully_covers(&super::parse_ip_interval("192.0.2.0/25").unwrap()) + ); + assert!( + constraints + .deny_v4 + .iter() + .any(|entry| entry.overlaps(&super::parse_ip_interval("192.0.2.128/25").unwrap())) + ); + assert!( + !constraints + .allow_v4 + .fully_covers(&super::parse_ip_interval("198.51.100.0/24").unwrap()) + ); + } + + #[test] + fn same_list_overlap_warns_and_normalizes() { + let constraints = parse("allow 10.0.0.0/8\nallow 10.1.0.0/16\n"); + assert_eq!(constraints.allow_v4.len(), 1); + assert_eq!(constraints.warnings.len(), 1); + assert!(constraints.warnings[0].contains("overlap")); + } + + #[test] + fn adjacent_allow_ranges_cover_one_interval() { + let constraints = parse("allow 64496 - 64500\nallow 64501 - 64511\n"); + assert!( + constraints + .allow_asn + .fully_covers(&super::parse_as_interval("64496 - 64511").unwrap()) + ); + } + + #[test] + fn interval_index_uses_linear_for_at_most_ten_and_tree_above_ten() { + for count in [0, 1, 9, 10] { + let constraints = parse(&indexed_ipv4_rules("deny", count)); + assert_eq!(constraints.deny_v4.len(), count); + assert!(!constraints.deny_v4.uses_tree(), "count={count}"); + } + + let constraints = parse(&indexed_ipv4_rules("deny", 11)); + assert_eq!(constraints.deny_v4.len(), 11); + assert!(constraints.deny_v4.uses_tree()); + } + + #[test] + fn tree_ip_queries_match_linear_reference() { + let body = format!( + "{}{}", + indexed_ipv4_rules("allow", 11), + indexed_ipv4_rules("deny", 11) + ); + let constraints = parse(&body); + assert!(constraints.allow_v4.uses_tree()); + assert!(constraints.deny_v4.uses_tree()); + + let linear_allow = IntervalIndex { + rules: constraints.allow_v4.rules.clone(), + lookup: IntervalLookup::Linear, + }; + let linear_deny = IntervalIndex { + rules: constraints.deny_v4.rules.clone(), + lookup: IntervalLookup::Linear, + }; + let targets = [ + super::parse_ip_interval("10.0.0.0/16").unwrap(), + super::parse_ip_interval("10.0.1.0/24").unwrap(), + super::parse_ip_interval("10.1.0.0/16").unwrap(), + super::parse_ip_interval("10.20.0.0/16").unwrap(), + super::parse_ip_interval("10.21.0.0/16").unwrap(), + super::parse_ip_interval("10.0.0.0 - 10.2.255.255").unwrap(), + super::parse_ip_interval("9.0.0.0/8").unwrap(), + super::parse_ip_interval("11.0.0.0/8").unwrap(), + ]; + for target in targets { + assert_eq!( + constraints.allow_v4.any_overlaps(&target), + linear_allow.any_overlaps(&target), + "allow overlap for {target}" + ); + assert_eq!( + constraints.allow_v4.fully_covers(&target), + linear_allow.fully_covers(&target), + "allow coverage for {target}" + ); + assert_eq!( + constraints.deny_v4.any_overlaps(&target), + linear_deny.any_overlaps(&target), + "deny overlap for {target}" + ); + } + } + + #[test] + fn tree_ipv6_queries_match_linear_reference() { + let body = format!( + "{}{}", + indexed_ipv6_rules("allow", 11), + indexed_ipv6_rules("deny", 11) + ); + let constraints = parse(&body); + assert!(constraints.allow_v6.uses_tree()); + assert!(constraints.deny_v6.uses_tree()); + + let linear_allow = IntervalIndex { + rules: constraints.allow_v6.rules.clone(), + lookup: IntervalLookup::Linear, + }; + let linear_deny = IntervalIndex { + rules: constraints.deny_v6.rules.clone(), + lookup: IntervalLookup::Linear, + }; + let targets = [ + super::parse_ip_interval("2001:db8:0::/48").unwrap(), + super::parse_ip_interval("2001:db8:1::/64").unwrap(), + super::parse_ip_interval("2001:db8:2::/48").unwrap(), + super::parse_ip_interval("2001:db8:20::/48").unwrap(), + super::parse_ip_interval("2001:db8:21::/48").unwrap(), + super::parse_ip_interval("2001:db8:0:: - 2001:db8:2:ffff:ffff:ffff:ffff:ffff").unwrap(), + super::parse_ip_interval("2001:db7::/32").unwrap(), + super::parse_ip_interval("2001:db9::/32").unwrap(), + ]; + for target in targets { + assert_eq!( + constraints.allow_v6.any_overlaps(&target), + linear_allow.any_overlaps(&target), + "allow overlap for {target}" + ); + assert_eq!( + constraints.allow_v6.fully_covers(&target), + linear_allow.fully_covers(&target), + "allow coverage for {target}" + ); + assert_eq!( + constraints.deny_v6.any_overlaps(&target), + linear_deny.any_overlaps(&target), + "deny overlap for {target}" + ); + } + } + + #[test] + fn tree_asn_queries_match_linear_reference() { + let body = format!( + "{}{}", + indexed_asn_rules("allow", 11), + indexed_asn_rules("deny", 11) + ); + let constraints = parse(&body); + assert!(constraints.allow_asn.uses_tree()); + assert!(constraints.deny_asn.uses_tree()); + + let linear_allow = IntervalIndex { + rules: constraints.allow_asn.rules.clone(), + lookup: IntervalLookup::Linear, + }; + let linear_deny = IntervalIndex { + rules: constraints.deny_asn.rules.clone(), + lookup: IntervalLookup::Linear, + }; + let targets = [ + super::AsInterval::new(65000, 65000), + super::AsInterval::new(65001, 65001), + super::AsInterval::new(65000, 65002), + super::AsInterval::new(65020, 65020), + super::AsInterval::new(65021, 65021), + super::AsInterval::new(64999, 65000), + ]; + for target in targets { + assert_eq!( + constraints.allow_asn.any_overlaps(&target), + linear_allow.any_overlaps(&target), + "allow overlap for {target}" + ); + assert_eq!( + constraints.allow_asn.fully_covers(&target), + linear_allow.fully_covers(&target), + "allow coverage for {target}" + ); + assert_eq!( + constraints.deny_asn.any_overlaps(&target), + linear_deny.any_overlaps(&target), + "deny overlap for {target}" + ); + } + } + + #[test] + fn index_equality_and_debug_ignore_derived_lookup() { + let constraints = parse(&indexed_ipv4_rules("deny", 11)); + let tree = constraints.deny_v4.clone(); + let linear = IntervalIndex { + rules: tree.rules.clone(), + lookup: IntervalLookup::Linear, + }; + assert_eq!(tree, linear); + assert_eq!(format!("{tree:?}"), format!("{linear:?}")); + } + + #[test] + fn adjacent_file_is_discovered_by_tal_stem() { + let dir = tempfile::tempdir().expect("tmpdir"); + let tal_path = dir.path().join("example.tal"); + std::fs::write(&tal_path, "placeholder").expect("write TAL"); + std::fs::write( + tal_path.with_extension("constraints"), + "allow 192.0.2.0/24\n", + ) + .expect("write constraints"); + let inputs = vec![crate::parallel::types::TalInputSpec::from_file_path( + tal_path, + )]; + let loaded = TaConstraintsByTal::load_for_tals(&inputs, &[]).expect("load constraints"); + assert!(loaded.for_tal("example").is_some()); + let first = loaded + .shared_for_tal("example") + .expect("shared constraints snapshot"); + let second = loaded + .shared_for_tal("example") + .expect("shared constraints snapshot"); + assert!(std::sync::Arc::ptr_eq(&first, &second)); + } + + #[test] + fn shared_snapshots_remain_tal_specific() { + let dir = tempfile::tempdir().expect("tmpdir"); + let tal_a = dir.path().join("alpha.tal"); + let tal_b = dir.path().join("bravo.tal"); + std::fs::write(&tal_a, "placeholder").expect("write alpha TAL"); + std::fs::write(&tal_b, "placeholder").expect("write bravo TAL"); + std::fs::write(tal_a.with_extension("constraints"), "allow 192.0.2.0/24\n") + .expect("write alpha constraints"); + std::fs::write( + tal_b.with_extension("constraints"), + "allow 198.51.100.0/24\n", + ) + .expect("write bravo constraints"); + + let inputs = vec![ + crate::parallel::types::TalInputSpec::from_file_path(tal_a), + crate::parallel::types::TalInputSpec::from_file_path(tal_b), + ]; + let loaded = TaConstraintsByTal::load_for_tals(&inputs, &[]).expect("load constraints"); + let alpha = loaded.shared_for_tal("alpha").expect("alpha snapshot"); + let bravo = loaded.shared_for_tal("bravo").expect("bravo snapshot"); + assert!(!std::sync::Arc::ptr_eq(&alpha, &bravo)); + assert_ne!(alpha.as_ref(), bravo.as_ref()); + } + + #[test] + fn semantic_fingerprint_is_cached_and_ignores_source_path() { + let first_dir = tempfile::tempdir().expect("first tmpdir"); + let second_dir = tempfile::tempdir().expect("second tmpdir"); + let first_tal = first_dir.path().join("example.tal"); + let second_tal = second_dir.path().join("example.tal"); + for tal_path in [&first_tal, &second_tal] { + std::fs::write(tal_path, "placeholder").expect("write TAL"); + std::fs::write( + tal_path.with_extension("constraints"), + "allow 192.0.2.0/24\ndeny 192.0.2.128/25\n", + ) + .expect("write constraints"); + } + + let first = TaConstraintsByTal::load_for_tals( + &[crate::parallel::types::TalInputSpec::from_file_path( + first_tal, + )], + &[], + ) + .expect("load first constraints"); + let second = TaConstraintsByTal::load_for_tals( + &[crate::parallel::types::TalInputSpec::from_file_path( + second_tal, + )], + &[], + ) + .expect("load second constraints"); + + assert_eq!(first.fingerprint_bytes().len(), 32); + assert_eq!(first.fingerprint_bytes(), second.fingerprint_bytes()); + assert_eq!(first.fingerprint_sha256_hex().len(), 64); + } + + #[test] + fn full_afrinic_ipv4_fixture_is_parseable() { + let path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/ta_constraints/afrinic-full-ipv4-deny.constraints"); + let raw = std::fs::read_to_string(&path).expect("read full AFRINIC fixture"); + let source_ipv4_rules = raw + .lines() + .filter(|line| line.trim_start().starts_with("deny ")) + .count(); + assert_eq!(source_ipv4_rules, 383); + + let constraints = TaConstraints::from_file(&path).expect("parse full AFRINIC fixture"); + assert_eq!(constraints.allow_v4.len(), 1); + assert_eq!(constraints.allow_v6.len(), 1); + assert_eq!(constraints.allow_asn.len(), 1); + assert!(!constraints.deny_v4.is_empty()); + } + + #[test] + fn current_afrinic_ipv4_fixture_is_parseable_and_canonical() { + let path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/ta_constraints/afrinic-current-ipv4-deny.constraints"); + let raw = std::fs::read_to_string(&path).expect("read current AFRINIC fixture"); + let source_ipv4_rules = raw + .lines() + .filter(|line| line.trim_start().starts_with("deny ")) + .count(); + assert_eq!(source_ipv4_rules, 1825); + + let constraints = TaConstraints::from_file(&path).expect("parse current AFRINIC fixture"); + assert_eq!(constraints.allow_v4.len(), 1); + assert_eq!(constraints.allow_v6.len(), 1); + assert_eq!(constraints.allow_asn.len(), 1); + assert_eq!(constraints.deny_v4.len(), 815); + assert!(constraints.deny_v4.uses_tree()); + assert!(!constraints.allow_v4.uses_tree()); + assert!(!constraints.allow_v6.uses_tree()); + assert!(!constraints.allow_asn.uses_tree()); + assert!(constraints.warnings.is_empty()); + } +} diff --git a/src/validation/objects.rs b/src/validation/objects.rs index 7a60cd7..f7ef122 100644 --- a/src/validation/objects.rs +++ b/src/validation/objects.rs @@ -961,6 +961,38 @@ pub fn process_publication_point_for_issuer_with_cache_options, collect_vcir_local_outputs: bool, roa_cache: RoaValidationCacheInput<'_>, +) -> ObjectsOutput { + process_publication_point_for_issuer_with_cache_options_and_ta_constraints( + publication_point, + policy, + issuer_ca_der, + issuer_ca_rsync_uri, + issuer_effective_ip, + issuer_effective_as, + validation_time, + timing, + collect_vcir_local_outputs, + roa_cache, + None, + ) +} + +/// Serial signed-object processing with an optional, locally configured +/// constraint set for the TA that owns this publication-point tree. +pub fn process_publication_point_for_issuer_with_cache_options_and_ta_constraints< + P: PublicationPointData, +>( + publication_point: &P, + policy: &Policy, + issuer_ca_der: &[u8], + issuer_ca_rsync_uri: Option<&str>, + issuer_effective_ip: Option<&crate::data_model::rc::IpResourceSet>, + issuer_effective_as: Option<&crate::data_model::rc::AsResourceSet>, + validation_time: time::OffsetDateTime, + timing: Option<&TimingHandle>, + collect_vcir_local_outputs: bool, + roa_cache: RoaValidationCacheInput<'_>, + ta_constraints: Option<&crate::ta_constraints::TaConstraints>, ) -> ObjectsOutput { let manifest_rsync_uri = publication_point.manifest_rsync_uri(); let manifest_bytes = publication_point.manifest_bytes(); @@ -1271,6 +1303,7 @@ pub fn process_publication_point_for_issuer_with_cache_options { stats.aspa_ok += 1; @@ -1790,6 +1826,10 @@ pub(crate) struct RoaTaskShared { issuer_effective_ip: Option>, issuer_effective_as: Option>, resource_validation_mode: ResourceValidationMode, + /// Immutable constraints snapshot selected by the owning TAL. Every + /// ROA task for a publication point shares this Arc, so workers do not + /// need a mutable/global policy lookup or a copy of the interval rules. + ta_constraints: Option>, } #[derive(Clone)] @@ -1911,6 +1951,7 @@ fn validate_owned_roa_task(worker_index: usize, task: OwnedRoaTask) -> RoaTaskRe task.strict_cms_der, task.strict_name, task.resource_validation_mode, + shared.ta_constraints.as_deref(), ) .map(|(vrps, local_outputs, cache_object_meta)| RoaTaskOk { vrps, @@ -2009,6 +2050,40 @@ pub(crate) fn prepare_publication_point_for_parallel_roa_with_cache, +) -> ParallelObjectsPrepare { + prepare_publication_point_for_parallel_roa_with_cache_and_ta_constraints( + publication_point_id, + publication_point, + policy, + issuer_ca_der, + issuer_ca_rsync_uri, + issuer_effective_ip, + issuer_effective_as, + validation_time, + collect_vcir_local_outputs, + roa_cache, + None, + ) +} + +/// Prepare a publication point for parallel ROA validation with the +/// immutable constraints snapshot belonging to its TAL. The snapshot is +/// moved into the stage-owned shared payload and therefore remains available +/// to detached ROA workers after the scoped phase-2 stage worker returns. +pub(crate) fn prepare_publication_point_for_parallel_roa_with_cache_and_ta_constraints< + P: PublicationPointData, +>( + publication_point_id: u64, + publication_point: &P, + policy: &Policy, + issuer_ca_der: &[u8], + issuer_ca_rsync_uri: Option<&str>, + issuer_effective_ip: Option<&crate::data_model::rc::IpResourceSet>, + issuer_effective_as: Option<&crate::data_model::rc::AsResourceSet>, + validation_time: time::OffsetDateTime, + collect_vcir_local_outputs: bool, + roa_cache: RoaValidationCacheInput<'_>, + ta_constraints: Option>, ) -> ParallelObjectsPrepare { let manifest_rsync_uri = publication_point.manifest_rsync_uri(); let manifest_bytes = publication_point.manifest_bytes(); @@ -2323,6 +2398,7 @@ pub(crate) fn prepare_publication_point_for_parallel_roa_with_cache { stats.aspa_ok += 1; @@ -2660,6 +2737,9 @@ pub(crate) enum ObjectValidateError { "EE certificate resources are not a subset of issuer effective resources (RFC 6487 §7.2; RFC 3779)" )] EeResourcesNotSubset, + + #[error("EE certificate violates locally configured TA constraints: {0}")] + TaConstraints(#[from] crate::ta_constraints::TaConstraintsViolation), } pub(crate) fn validate_roa_task_serial( @@ -2679,6 +2759,7 @@ pub(crate) fn validate_roa_task_serial( strict_cms_der: bool, strict_name: bool, resource_validation_mode: ResourceValidationMode, + ta_constraints: Option<&crate::ta_constraints::TaConstraints>, ) -> RoaTaskResult { let outcome = process_roa_with_issuer( task.file, @@ -2697,6 +2778,7 @@ pub(crate) fn validate_roa_task_serial( strict_cms_der, strict_name, resource_validation_mode, + ta_constraints, ) .map(|(vrps, local_outputs, cache_object_meta)| RoaTaskOk { vrps, @@ -2732,6 +2814,7 @@ fn process_roa_with_issuer( strict_cms_der: bool, strict_name: bool, resource_validation_mode: ResourceValidationMode, + ta_constraints: Option<&crate::ta_constraints::TaConstraints>, ) -> Result<(Vec, Vec, Option), ObjectValidateError> { let _decode = timing .as_ref() @@ -2792,6 +2875,10 @@ fn process_roa_with_issuer( )?; drop(_subset); + if let Some(ta_constraints) = ta_constraints { + ta_constraints.validate_ee_certificate(&ee.resource_cert)?; + } + let vrps = roa_to_vrps_with_vrs(&roa, ee_vrs.ip.as_ref())?; let cache_object_meta = RoaCacheObjectMeta { source_object_uri: file.rsync_uri.clone(), @@ -2861,6 +2948,7 @@ fn process_roa_with_issuer_parallel_cached( strict_cms_der: bool, strict_name: bool, resource_validation_mode: ResourceValidationMode, + ta_constraints: Option<&crate::ta_constraints::TaConstraints>, ) -> Result<(Vec, Vec, Option), ObjectValidateError> { let _decode = timing .as_ref() @@ -2927,6 +3015,10 @@ fn process_roa_with_issuer_parallel_cached( )?; drop(_subset); + if let Some(ta_constraints) = ta_constraints { + ta_constraints.validate_ee_certificate(&ee.resource_cert)?; + } + let vrps = roa_to_vrps_with_vrs(&roa, ee_vrs.ip.as_ref())?; let cache_object_meta = RoaCacheObjectMeta { source_object_uri: file.rsync_uri.clone(), @@ -2996,6 +3088,7 @@ fn process_aspa_with_issuer( strict_cms_der: bool, strict_name: bool, resource_validation_mode: ResourceValidationMode, + ta_constraints: Option<&crate::ta_constraints::TaConstraints>, ) -> Result<(AspaAttestation, Option), ObjectValidateError> { let _decode = timing .as_ref() @@ -3056,6 +3149,10 @@ fn process_aspa_with_issuer( )?; drop(_subset); + if let Some(ta_constraints) = ta_constraints { + ta_constraints.validate_ee_certificate(&ee.resource_cert)?; + } + validate_aspa_customer_in_vrs(&aspa, ee_vrs.asn.as_ref())?; let attestation = AspaAttestation { @@ -5505,6 +5602,13 @@ mod tests { #[test] fn parallel_stage_roa_tasks_share_stage_owned_payloads() { + let ta_constraints = Arc::new( + crate::ta_constraints::TaConstraints::from_file( + &std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/ta_constraints/afrinic-current-ipv4-deny.constraints"), + ) + .expect("load constraints fixture"), + ); let stage = ParallelObjectsStage { publication_point_id: 7, shared: Arc::new(RoaTaskShared { @@ -5535,6 +5639,7 @@ mod tests { issuer_effective_ip: None, issuer_effective_as: None, resource_validation_mode: ResourceValidationMode::default(), + ta_constraints: Some(Arc::clone(&ta_constraints)), }), validation_time: OffsetDateTime::now_utc(), collect_vcir_local_outputs: false, @@ -5554,6 +5659,14 @@ mod tests { let tasks = stage.build_roa_tasks(); assert_eq!(tasks.len(), 2); assert!(Arc::ptr_eq(&tasks[0].shared, &tasks[1].shared)); + assert!(Arc::ptr_eq( + tasks[0] + .shared + .ta_constraints + .as_ref() + .expect("task constraints snapshot"), + &ta_constraints + )); } #[test] diff --git a/src/validation/run_tree_from_tal.rs b/src/validation/run_tree_from_tal.rs index 09524c6..1cf2605 100644 --- a/src/validation/run_tree_from_tal.rs +++ b/src/validation/run_tree_from_tal.rs @@ -901,6 +901,10 @@ where H: Fetcher + Clone + 'static, R: crate::fetch::rsync::RsyncFetcher + Clone + 'static, { + // Constraints are an immutable per-run policy snapshot. The phase-2 + // ready-stage binds the snapshot to each CA's TAL and moves an Arc into + // ROA/ASPA worker state, so constrained multi-TAL runs retain the same + // parallel scheduler as unconstrained runs. let phase2_enabled = phase2_config.is_some(); if tal_inputs.is_empty() { return Err(RunTreeFromTalError::Replay( diff --git a/src/validation/tree_parallel.rs b/src/validation/tree_parallel.rs index 53982d0..46b0544 100644 --- a/src/validation/tree_parallel.rs +++ b/src/validation/tree_parallel.rs @@ -16,7 +16,8 @@ use crate::validation::manifest::PublicationPointData; use crate::validation::manifest::PublicationPointSource; use crate::validation::objects::{ ObjectsOutput, OwnedRoaTask, ParallelObjectsPrepare, ParallelObjectsStage, - RoaValidationCacheInput, prepare_publication_point_for_parallel_roa_with_cache, + RoaValidationCacheInput, + prepare_publication_point_for_parallel_roa_with_cache_and_ta_constraints, reduce_parallel_roa_stage, }; use crate::validation::tree::{ @@ -1367,7 +1368,16 @@ fn compute_ready_publication_point_stage( } else { RoaValidationCacheInput::disabled() }; - match prepare_publication_point_for_parallel_roa_with_cache( + let ta_constraints = runner + .policy + .ta_constraints + .shared_for_tal(&ready.node.handle.tal_id); + if ta_constraints.is_some() { + if let Some(timing) = runner.timing.as_ref() { + timing.record_count("ta_constraints_parallel_publication_points", 1); + } + } + match prepare_publication_point_for_parallel_roa_with_cache_and_ta_constraints( ready.node.id, &fresh_stage.fresh_point, runner.policy, @@ -1378,6 +1388,7 @@ fn compute_ready_publication_point_stage( runner.validation_time, runner.persist_vcir, roa_cache, + ta_constraints, ) { ParallelObjectsPrepare::Complete(mut objects) => { metrics.prepare_ms = elapsed_ms(prepare_started); diff --git a/src/validation/tree_runner.rs b/src/validation/tree_runner.rs index d63c7db..8a92701 100644 --- a/src/validation/tree_runner.rs +++ b/src/validation/tree_runner.rs @@ -53,6 +53,7 @@ use crate::validation::objects::{ AspaAttestation, ParallelRoaWorkerPool, RoaValidationCacheInput, RoaValidationCacheView, RouterKeyPayload, Vrp, process_publication_point_for_issuer_parallel_roa_with_cache_options, process_publication_point_for_issuer_parallel_roa_with_pool_cache_options, + process_publication_point_for_issuer_with_cache_options_and_ta_constraints, }; use crate::validation::publication_point::PublicationPointSnapshot; use crate::validation::tree::{ @@ -1551,12 +1552,30 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> { RoaValidationCacheInput::disabled() }; let objects_processing_started = std::time::Instant::now(); + let ta_constraints = self.policy.ta_constraints.for_tal(&ca.tal_id); let mut objects = { let _objects_total = self .timing .as_ref() .map(|t| t.span_phase("objects_processing_total")); - if let Some(phase2_pool) = self.parallel_roa_worker_pool.as_ref() { + if let Some(ta_constraints) = ta_constraints { + // This method is the serial/fallback publication-point path. The + // phase-2 scheduler uses the stage-specific parallel prepare path, + // which carries the same immutable per-TAL snapshot into workers. + process_publication_point_for_issuer_with_cache_options_and_ta_constraints( + &fresh_point, + self.policy, + issuer_ca_der.as_ref(), + ca.ca_certificate_rsync_uri.as_deref(), + ca.effective_ip_resources.as_ref(), + ca.effective_as_resources.as_ref(), + self.validation_time, + self.timing.as_ref(), + false, + roa_cache, + Some(ta_constraints), + ) + } else if let Some(phase2_pool) = self.parallel_roa_worker_pool.as_ref() { process_publication_point_for_issuer_parallel_roa_with_pool_cache_options( &fresh_point, self.policy, @@ -1971,13 +1990,13 @@ fn sha256_digest_32(bytes: impl AsRef<[u8]>) -> [u8; 32] { out } -fn hash_serialized_parts(parts: &[(&str, Vec)]) -> [u8; 32] { +fn hash_serialized_parts(parts: &[(&str, &[u8])]) -> [u8; 32] { let mut hasher = sha2::Sha256::new(); for (label, value) in parts { hasher.update((label.len() as u64).to_be_bytes()); hasher.update(label.as_bytes()); hasher.update((value.len() as u64).to_be_bytes()); - hasher.update(value); + hasher.update(*value); } let digest = hasher.finalize(); let mut out = [0u8; 32]; @@ -1991,41 +2010,34 @@ fn cbor_or_debug_bytes(value: &T) -> Vec< fn ta_context_digest_for_ca(ca: &CaInstanceHandle) -> [u8; 32] { hash_serialized_parts(&[ - ("version", b"publication-point-cache-ta-v1".to_vec()), - ("tal_id", ca.tal_id.as_bytes().to_vec()), + ("version", b"publication-point-cache-ta-v1"), + ("tal_id", ca.tal_id.as_bytes()), ]) } pub(crate) fn ca_validation_context_digest_for_ca(ca: &CaInstanceHandle) -> [u8; 32] { + let parent_manifest = ca + .parent_manifest_rsync_uri + .as_deref() + .unwrap_or("") + .as_bytes(); + let effective_ip = cbor_or_debug_bytes(&ca.effective_ip_resources); + let effective_as = cbor_or_debug_bytes(&ca.effective_as_resources); hash_serialized_parts(&[ - ( - "version", - b"publication-point-cache-parent-context-v1".to_vec(), - ), - ("tal_id", ca.tal_id.as_bytes().to_vec()), - ( - "parent_manifest", - ca.parent_manifest_rsync_uri - .as_deref() - .unwrap_or("") - .as_bytes() - .to_vec(), - ), - ( - "effective_ip", - cbor_or_debug_bytes(&ca.effective_ip_resources), - ), - ( - "effective_as", - cbor_or_debug_bytes(&ca.effective_as_resources), - ), + ("version", b"publication-point-cache-parent-context-v1"), + ("tal_id", ca.tal_id.as_bytes()), + ("parent_manifest", parent_manifest), + ("effective_ip", effective_ip.as_slice()), + ("effective_as", effective_as.as_slice()), ]) } pub(crate) fn publication_point_cache_policy_fingerprint(policy: &Policy) -> [u8; 32] { + let policy_bytes = cbor_or_debug_bytes(policy); hash_serialized_parts(&[ - ("version", b"publication-point-cache-policy-v1".to_vec()), - ("policy", cbor_or_debug_bytes(policy)), + ("version", b"publication-point-cache-policy-v3"), + ("policy", policy_bytes.as_slice()), + ("ta_constraints", policy.ta_constraints.fingerprint_bytes()), ]) } @@ -2084,16 +2096,13 @@ fn child_certificate_cache_key_sha256_hex( policy_fingerprint: &[u8; 32], ) -> String { let digest = hash_serialized_parts(&[ - ("version", b"child-certificate-cache-key-v1".to_vec()), - ("child_cert_uri", child_cert_uri.as_bytes().to_vec()), - ("child_cert_sha256", child_cert_sha256.to_vec()), - ("issuer_ca_sha256", issuer_ca_sha256.to_vec()), + ("version", b"child-certificate-cache-key-v1"), + ("child_cert_uri", child_cert_uri.as_bytes()), + ("child_cert_sha256", child_cert_sha256), + ("issuer_ca_sha256", issuer_ca_sha256), // Keep the persisted cache-key label stable; this change only renames Rust identifiers. - ( - "parent_context_digest", - ca_validation_context_digest.to_vec(), - ), - ("policy_fingerprint", policy_fingerprint.to_vec()), + ("parent_context_digest", ca_validation_context_digest), + ("policy_fingerprint", policy_fingerprint), ]); sha256_hex_from_32(&digest) } @@ -2986,6 +2995,24 @@ fn discover_children_from_fresh_snapshot_with_audit_cached_with_issuer_der< match router_result { Ok(router) => { + if let Some(ta_constraints) = policy.ta_constraints.for_tal(&issuer.tal_id) + { + if let Err(error) = + ta_constraints.validate_ee_certificate(&router.resource_cert) + { + router_error = router_error.saturating_add(1); + audits.push(ObjectAuditEntry { + rsync_uri: f.rsync_uri.clone(), + sha256_hex: sha256_hex_from_32(&f.sha256), + kind: AuditObjectKind::RouterCertificate, + result: AuditObjectResult::Error, + detail: Some(format!( + "router certificate violates TA constraints: {error}" + )), + }); + continue; + } + } let router_asns = match router_asns_for_resource_mode( &router.asns, issuer.effective_as_resources.as_ref(), diff --git a/src/verification_only.rs b/src/verification_only.rs index 1ef4a1b..4f86b6b 100644 --- a/src/verification_only.rs +++ b/src/verification_only.rs @@ -73,6 +73,11 @@ pub struct ValidationContract { pub validation_time: String, pub binary_sha256: String, pub policy: Policy, + /// Constraints are currently normal-run-only. Persist their semantic + /// fingerprint so verification-only can fail closed rather than replay a + /// constrained run with an empty runtime-only policy map. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ta_constraints_fingerprint: Option, pub max_ca_depth: usize, pub max_instances: Option, pub cache: ValidationCacheContract, @@ -140,6 +145,7 @@ impl ValidationContract { validation_time: format_validation_time(validation_time)?, binary_sha256: current_binary_sha256()?, policy, + ta_constraints_fingerprint: None, max_ca_depth, max_instances, cache, @@ -163,6 +169,9 @@ impl ValidationContract { } self.validation_time()?; validate_sha256_hex("validation contract binarySha256", &self.binary_sha256)?; + if let Some(fingerprint) = self.ta_constraints_fingerprint.as_deref() { + validate_sha256_hex("validation contract taConstraintsFingerprint", fingerprint)?; + } if self.max_ca_depth == 0 { return Err("validation contract maxCaDepth must be greater than zero".to_string()); } @@ -600,6 +609,12 @@ pub fn prepare_verification( let cir = read_cir(&artifacts.source_cir)?; let contract = read_validation_contract(&artifacts.source_contract)?; + if contract.ta_constraints_fingerprint.is_some() { + return Err( + "verification-only is not supported for a run with TA constraints; rerun normal validation with the same constraints" + .to_string(), + ); + } if cir.validation_time.to_offset(time::UtcOffset::UTC) != contract.validation_time()? { return Err(format!( "source CIR validation time differs from validation contract: cir={}, contract={}", @@ -895,6 +910,7 @@ mod tests { validation_time: "2026-07-16T00:00:00Z".to_string(), binary_sha256: "ab".repeat(32), policy: Policy::default(), + ta_constraints_fingerprint: None, max_ca_depth: 32, max_instances: None, cache: ValidationCacheContract { diff --git a/tests/fixtures/ta_constraints/afrinic-current-ipv4-deny.constraints b/tests/fixtures/ta_constraints/afrinic-current-ipv4-deny.constraints new file mode 100644 index 0000000..26ce0a9 --- /dev/null +++ b/tests/fixtures/ta_constraints/afrinic-current-ipv4-deny.constraints @@ -0,0 +1,1842 @@ +# Feature #151 M7 current AFRINIC IPv4 holdings deny fixture. +# +# Source: AFRINIC delegated extended latest. +# URL: https://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-extended-latest +# Retrieved: 2026-08-27 +# Source SHA-256: 45ee1d198dc29640f04f3e858f05d2a357c08c93482243529f8f7864d2cd6708 +# +# Selection: IPv4 records with status allocated or assigned only. +# Excluded statuses: reserved and available (not current registry holdings). +# Source IPv4 records: 6051; allocated=3838; assigned=1651; reserved=547; available=15. +# Selected source records: 5489; selected addresses: 116118784. +# Overlap/adjacency union intervals: 815; exact CIDR deny rules: 1825. +# IPv6 and ASN remain allow-all to isolate the current AFRINIC IPv4 policy. +allow 0.0.0.0/0 +allow ::/0 +allow 0 - 4294967295 + +deny 41.0.0.0/11 +deny 41.32.0.0/12 +deny 41.48.0.0/13 +deny 41.56.0.0/16 +deny 41.57.0.0/18 +deny 41.57.64.0/19 +deny 41.57.96.0/20 +deny 41.57.120.0/22 +deny 41.57.128.0/18 +deny 41.58.0.0/15 +deny 41.60.0.0/14 +deny 41.64.0.0/15 +deny 41.66.0.0/16 +deny 41.67.0.0/18 +deny 41.67.64.0/20 +deny 41.67.88.0/21 +deny 41.67.96.0/19 +deny 41.67.128.0/18 +deny 41.68.0.0/14 +deny 41.72.96.0/19 +deny 41.72.128.0/18 +deny 41.72.192.0/19 +deny 41.73.0.0/18 +deny 41.73.96.0/19 +deny 41.73.128.0/17 +deny 41.74.0.0/19 +deny 41.74.48.0/20 +deny 41.74.64.0/18 +deny 41.74.128.0/18 +deny 41.74.192.0/19 +deny 41.74.240.0/20 +deny 41.75.0.0/17 +deny 41.75.128.0/18 +deny 41.75.192.0/19 +deny 41.76.0.0/20 +deny 41.76.16.0/21 +deny 41.76.32.0/20 +deny 41.76.56.0/21 +deny 41.76.64.0/20 +deny 41.76.80.0/21 +deny 41.76.96.0/20 +deny 41.76.112.0/21 +deny 41.76.128.0/17 +deny 41.77.0.0/18 +deny 41.77.72.0/21 +deny 41.77.80.0/20 +deny 41.77.96.0/20 +deny 41.77.112.0/21 +deny 41.77.128.0/19 +deny 41.77.160.0/20 +deny 41.77.184.0/21 +deny 41.77.200.0/21 +deny 41.77.216.0/21 +deny 41.77.232.0/21 +deny 41.77.248.0/21 +deny 41.78.4.0/22 +deny 41.78.8.0/21 +deny 41.78.16.0/20 +deny 41.78.36.0/22 +deny 41.78.44.0/22 +deny 41.78.48.0/21 +deny 41.78.56.0/22 +deny 41.78.64.0/19 +deny 41.78.100.0/22 +deny 41.78.104.0/21 +deny 41.78.116.0/22 +deny 41.78.120.0/21 +deny 41.78.128.0/19 +deny 41.78.160.0/22 +deny 41.78.168.0/21 +deny 41.78.176.0/20 +deny 41.78.192.0/21 +deny 41.78.200.0/22 +deny 41.78.208.0/20 +deny 41.78.224.0/19 +deny 41.79.0.0/19 +deny 41.79.32.0/21 +deny 41.79.44.0/22 +deny 41.79.48.0/22 +deny 41.79.56.0/21 +deny 41.79.64.0/18 +deny 41.79.132.0/22 +deny 41.79.136.0/21 +deny 41.79.148.0/22 +deny 41.79.152.0/21 +deny 41.79.160.0/22 +deny 41.79.168.0/21 +deny 41.79.180.0/22 +deny 41.79.184.0/21 +deny 41.79.192.0/18 +deny 41.80.0.0/14 +deny 41.84.0.0/15 +deny 41.86.0.0/18 +deny 41.86.96.0/19 +deny 41.86.128.0/18 +deny 41.86.224.0/19 +deny 41.87.0.0/16 +deny 41.89.0.0/16 +deny 41.90.0.0/15 +deny 41.92.0.0/17 +deny 41.92.128.0/21 +deny 41.92.152.0/21 +deny 41.92.180.0/22 +deny 41.92.184.0/22 +deny 41.92.196.0/22 +deny 41.92.200.0/22 +deny 41.92.208.0/21 +deny 41.92.216.0/22 +deny 41.92.224.0/24 +deny 41.92.248.0/21 +deny 41.93.0.0/17 +deny 41.94.0.0/15 +deny 41.96.0.0/11 +deny 41.128.0.0/13 +deny 41.136.0.0/15 +deny 41.138.32.0/19 +deny 41.138.64.0/18 +deny 41.138.128.0/18 +deny 41.138.224.0/19 +deny 41.139.0.0/16 +deny 41.140.0.0/14 +deny 41.144.0.0/13 +deny 41.152.0.0/15 +deny 41.155.0.0/16 +deny 41.156.0.0/14 +deny 41.160.0.0/12 +deny 41.176.0.0/13 +deny 41.184.0.0/14 +deny 41.188.0.0/15 +deny 41.190.0.0/18 +deny 41.190.68.0/22 +deny 41.190.80.0/20 +deny 41.190.96.0/19 +deny 41.190.128.0/18 +deny 41.190.192.0/19 +deny 41.190.224.0/22 +deny 41.190.232.0/21 +deny 41.190.240.0/20 +deny 41.191.0.0/17 +deny 41.191.128.0/19 +deny 41.191.192.0/19 +deny 41.191.224.0/20 +deny 41.191.248.0/22 +deny 41.192.0.0/13 +deny 41.200.0.0/15 +deny 41.202.32.0/19 +deny 41.202.64.0/18 +deny 41.202.160.0/19 +deny 41.202.192.0/18 +deny 41.203.0.0/16 +deny 41.204.0.0/17 +deny 41.204.128.0/18 +deny 41.204.192.0/19 +deny 41.205.0.0/17 +deny 41.205.128.0/18 +deny 41.205.192.0/19 +deny 41.206.0.0/16 +deny 41.207.0.0/17 +deny 41.207.128.0/18 +deny 41.207.192.0/19 +deny 41.207.224.0/22 +deny 41.207.232.0/21 +deny 41.207.240.0/20 +deny 41.208.0.0/16 +deny 41.209.0.0/17 +deny 41.209.128.0/18 +deny 41.210.0.0/18 +deny 41.210.128.0/17 +deny 41.211.0.0/16 +deny 41.212.0.0/14 +deny 41.216.0.0/16 +deny 41.217.0.0/17 +deny 41.217.148.0/22 +deny 41.217.192.0/18 +deny 41.218.0.0/15 +deny 41.220.0.0/19 +deny 41.220.32.0/20 +deny 41.220.64.0/18 +deny 41.220.128.0/17 +deny 41.221.0.0/17 +deny 41.221.144.0/20 +deny 41.221.160.0/19 +deny 41.221.192.0/18 +deny 41.222.0.0/20 +deny 41.222.16.0/21 +deny 41.222.32.0/19 +deny 41.222.64.0/18 +deny 41.222.128.0/18 +deny 41.222.192.0/20 +deny 41.222.208.0/21 +deny 41.222.216.0/22 +deny 41.222.224.0/20 +deny 41.222.244.0/22 +deny 41.222.248.0/21 +deny 41.223.0.0/20 +deny 41.223.20.0/22 +deny 41.223.24.0/21 +deny 41.223.32.0/19 +deny 41.223.64.0/20 +deny 41.223.80.0/21 +deny 41.223.96.0/20 +deny 41.223.116.0/22 +deny 41.223.120.0/21 +deny 41.223.128.0/19 +deny 41.223.160.0/21 +deny 41.223.172.0/22 +deny 41.223.180.0/22 +deny 41.223.184.0/21 +deny 41.223.192.0/18 +deny 41.224.0.0/12 +deny 41.240.0.0/15 +deny 41.242.0.0/21 +deny 41.242.16.0/20 +deny 41.242.40.0/21 +deny 41.242.48.0/20 +deny 41.242.64.0/20 +deny 41.242.80.0/22 +deny 41.242.88.0/22 +deny 41.242.96.0/20 +deny 41.242.112.0/21 +deny 41.242.124.0/22 +deny 41.242.132.0/22 +deny 41.242.140.0/22 +deny 41.242.144.0/20 +deny 41.242.160.0/19 +deny 41.242.192.0/18 +deny 41.243.0.0/16 +deny 41.245.0.0/17 +deny 41.246.0.0/15 +deny 41.248.0.0/13 +deny 45.96.0.0/12 +deny 45.192.0.0/12 +deny 45.208.0.0/13 +deny 45.216.0.0/14 +deny 45.220.0.0/19 +deny 45.220.48.0/23 +deny 45.220.52.0/22 +deny 45.220.56.0/21 +deny 45.220.128.0/17 +deny 45.221.0.0/20 +deny 45.221.16.0/22 +deny 45.221.24.0/22 +deny 45.221.32.0/19 +deny 45.221.64.0/18 +deny 45.221.128.0/17 +deny 45.222.0.0/16 +deny 45.240.0.0/13 +deny 62.8.64.0/19 +deny 62.12.96.0/19 +deny 62.24.96.0/19 +deny 62.61.192.0/18 +deny 62.68.32.0/19 +deny 62.68.224.0/19 +deny 62.114.0.0/16 +deny 62.117.32.0/19 +deny 62.135.0.0/17 +deny 62.139.0.0/16 +deny 62.140.64.0/18 +deny 62.173.32.0/19 +deny 62.193.64.0/18 +deny 62.193.160.0/19 +deny 62.240.32.0/19 +deny 62.240.96.0/19 +deny 62.241.128.0/19 +deny 62.251.128.0/17 +deny 64.57.112.0/20 +deny 66.8.0.0/17 +deny 66.18.64.0/19 +deny 66.251.128.0/18 +deny 69.63.64.0/20 +deny 69.67.32.0/20 +deny 77.220.0.0/19 +deny 80.67.128.0/20 +deny 80.72.96.0/20 +deny 80.75.160.0/19 +deny 80.87.64.0/19 +deny 80.88.0.0/22 +deny 80.88.4.0/23 +deny 80.88.7.0/24 +deny 80.88.8.0/21 +deny 80.95.0.0/20 +deny 80.240.192.0/20 +deny 80.246.0.0/20 +deny 80.248.0.0/20 +deny 80.248.64.0/20 +deny 80.249.64.0/20 +deny 80.250.32.0/20 +deny 81.10.0.0/17 +deny 81.21.96.0/20 +deny 81.22.64.0/19 +deny 81.26.72.0/21 +deny 81.29.96.0/20 +deny 81.91.224.0/20 +deny 81.192.0.0/16 +deny 82.101.128.0/18 +deny 82.129.128.0/17 +deny 82.151.64.0/19 +deny 82.201.128.0/17 +deny 83.143.24.0/21 +deny 84.36.0.0/16 +deny 84.205.96.0/19 +deny 84.233.0.0/17 +deny 87.255.96.0/19 +deny 102.0.0.0/12 +deny 102.16.0.0/14 +deny 102.20.0.0/15 +deny 102.22.0.0/18 +deny 102.22.64.0/20 +deny 102.22.80.0/22 +deny 102.22.88.0/21 +deny 102.22.100.0/22 +deny 102.22.104.0/21 +deny 102.22.112.0/20 +deny 102.22.128.0/17 +deny 102.23.0.0/18 +deny 102.23.80.0/20 +deny 102.23.96.0/22 +deny 102.23.112.0/21 +deny 102.23.120.0/22 +deny 102.23.132.0/22 +deny 102.23.136.0/21 +deny 102.23.144.0/20 +deny 102.23.160.0/19 +deny 102.23.192.0/20 +deny 102.23.208.0/21 +deny 102.23.218.0/23 +deny 102.23.220.0/22 +deny 102.23.224.0/19 +deny 102.24.0.0/13 +deny 102.32.0.0/14 +deny 102.36.0.0/17 +deny 102.36.132.0/22 +deny 102.36.136.0/21 +deny 102.36.144.0/20 +deny 102.36.160.0/20 +deny 102.36.176.0/21 +deny 102.36.184.0/22 +deny 102.36.192.0/23 +deny 102.36.196.0/22 +deny 102.36.200.0/21 +deny 102.36.208.0/20 +deny 102.36.224.0/21 +deny 102.36.236.0/22 +deny 102.36.240.0/22 +deny 102.36.248.0/21 +deny 102.37.0.0/16 +deny 102.38.0.0/19 +deny 102.38.32.0/20 +deny 102.38.48.0/22 +deny 102.38.56.0/21 +deny 102.38.64.0/18 +deny 102.38.128.0/17 +deny 102.39.0.0/16 +deny 102.40.0.0/13 +deny 102.48.0.0/12 +deny 102.64.0.0/18 +deny 102.64.64.0/20 +deny 102.64.92.0/22 +deny 102.64.96.0/19 +deny 102.64.128.0/17 +deny 102.65.0.0/16 +deny 102.66.0.0/15 +deny 102.68.0.0/20 +deny 102.68.16.0/22 +deny 102.68.20.0/23 +deny 102.68.22.0/24 +deny 102.68.24.0/21 +deny 102.68.32.0/20 +deny 102.68.48.0/22 +deny 102.68.56.0/21 +deny 102.68.64.0/20 +deny 102.68.80.0/21 +deny 102.68.96.0/20 +deny 102.68.112.0/21 +deny 102.68.120.0/22 +deny 102.68.124.0/23 +deny 102.68.126.0/24 +deny 102.68.128.0/21 +deny 102.68.136.0/22 +deny 102.68.141.0/24 +deny 102.68.142.0/23 +deny 102.68.144.0/20 +deny 102.68.160.0/20 +deny 102.68.176.0/21 +deny 102.68.188.0/22 +deny 102.68.192.0/18 +deny 102.69.0.0/16 +deny 102.70.0.0/15 +deny 102.72.0.0/13 +deny 102.80.0.0/12 +deny 102.96.0.0/11 +deny 102.128.0.0/18 +deny 102.128.76.0/22 +deny 102.128.80.0/20 +deny 102.128.96.0/19 +deny 102.128.128.0/19 +deny 102.128.160.0/20 +deny 102.128.176.0/21 +deny 102.128.184.0/22 +deny 102.128.192.0/18 +deny 102.129.32.0/22 +deny 102.129.40.0/21 +deny 102.129.48.0/20 +deny 102.129.64.0/18 +deny 102.129.128.0/17 +deny 102.130.0.0/21 +deny 102.130.12.0/22 +deny 102.130.16.0/20 +deny 102.130.32.0/20 +deny 102.130.48.0/21 +deny 102.130.64.0/19 +deny 102.130.96.0/23 +deny 102.130.100.0/22 +deny 102.130.104.0/21 +deny 102.130.112.0/20 +deny 102.130.128.0/17 +deny 102.131.16.0/24 +deny 102.131.18.0/23 +deny 102.131.20.0/22 +deny 102.131.28.0/22 +deny 102.131.32.0/21 +deny 102.131.48.0/20 +deny 102.131.64.0/18 +deny 102.131.128.0/17 +deny 102.132.0.0/18 +deny 102.132.64.0/20 +deny 102.132.80.0/22 +deny 102.132.96.0/19 +deny 102.132.128.0/17 +deny 102.133.0.0/16 +deny 102.134.0.0/18 +deny 102.134.64.0/21 +deny 102.134.72.0/22 +deny 102.134.80.0/20 +deny 102.134.96.0/19 +deny 102.134.128.0/21 +deny 102.134.140.0/22 +deny 102.134.144.0/20 +deny 102.134.160.0/19 +deny 102.134.192.0/18 +deny 102.135.0.0/17 +deny 102.135.128.0/19 +deny 102.135.160.0/22 +deny 102.135.168.0/21 +deny 102.135.176.0/20 +deny 102.135.192.0/18 +deny 102.136.0.0/14 +deny 102.140.0.0/18 +deny 102.140.64.0/19 +deny 102.140.96.0/21 +deny 102.140.112.0/21 +deny 102.140.128.0/17 +deny 102.141.0.0/17 +deny 102.141.128.0/18 +deny 102.141.192.0/20 +deny 102.141.208.0/21 +deny 102.141.224.0/22 +deny 102.141.232.0/21 +deny 102.141.240.0/20 +deny 102.142.0.0/15 +deny 102.144.0.0/12 +deny 102.160.0.0/14 +deny 102.164.0.0/19 +deny 102.164.32.0/21 +deny 102.164.44.0/22 +deny 102.164.52.0/22 +deny 102.164.56.0/21 +deny 102.164.64.0/18 +deny 102.164.128.0/17 +deny 102.165.0.0/17 +deny 102.165.128.0/19 +deny 102.165.160.0/20 +deny 102.165.176.0/21 +deny 102.165.184.0/22 +deny 102.165.188.0/23 +deny 102.165.191.0/24 +deny 102.165.192.0/18 +deny 102.166.0.0/15 +deny 102.168.0.0/13 +deny 102.176.0.0/16 +deny 102.177.0.0/18 +deny 102.177.64.0/21 +deny 102.177.72.0/22 +deny 102.177.80.0/21 +deny 102.177.100.0/22 +deny 102.177.112.0/20 +deny 102.177.128.0/18 +deny 102.177.192.0/21 +deny 102.178.0.0/15 +deny 102.180.0.0/16 +deny 102.181.0.0/17 +deny 102.181.128.0/18 +deny 102.181.192.0/19 +deny 102.182.0.0/16 +deny 102.184.0.0/13 +deny 102.201.116.0/22 +deny 102.201.120.0/21 +deny 102.201.128.0/18 +deny 102.201.192.0/20 +deny 102.201.208.0/21 +deny 102.201.228.0/22 +deny 102.201.232.0/21 +deny 102.201.240.0/20 +deny 102.202.0.0/16 +deny 102.203.0.0/20 +deny 102.203.16.0/22 +deny 102.203.32.0/20 +deny 102.203.48.0/22 +deny 102.203.56.0/21 +deny 102.203.64.0/21 +deny 102.203.72.0/22 +deny 102.203.80.0/20 +deny 102.203.96.0/19 +deny 102.203.128.0/17 +deny 102.204.0.0/17 +deny 102.204.144.0/20 +deny 102.204.160.0/19 +deny 102.204.192.0/18 +deny 102.205.0.0/16 +deny 102.206.0.0/15 +deny 102.208.0.0/17 +deny 102.208.128.0/20 +deny 102.208.144.0/21 +deny 102.208.152.0/22 +deny 102.208.160.0/19 +deny 102.208.192.0/18 +deny 102.209.0.0/16 +deny 102.210.0.0/16 +deny 102.211.0.0/17 +deny 102.211.128.0/20 +deny 102.211.144.0/22 +deny 102.211.152.0/21 +deny 102.211.160.0/19 +deny 102.211.192.0/18 +deny 102.212.0.0/16 +deny 102.213.0.0/17 +deny 102.213.128.0/18 +deny 102.213.192.0/19 +deny 102.213.228.0/22 +deny 102.213.232.0/21 +deny 102.213.240.0/20 +deny 102.214.0.0/21 +deny 102.214.8.0/22 +deny 102.214.12.0/23 +deny 102.214.14.0/24 +deny 102.214.16.0/20 +deny 102.214.32.0/20 +deny 102.214.48.0/21 +deny 102.214.56.0/22 +deny 102.214.64.0/18 +deny 102.214.128.0/18 +deny 102.214.192.0/19 +deny 102.214.224.0/20 +deny 102.214.240.0/22 +deny 102.214.244.0/23 +deny 102.214.247.0/24 +deny 102.214.248.0/21 +deny 102.215.0.0/18 +deny 102.215.64.0/22 +deny 102.215.72.0/21 +deny 102.215.84.0/22 +deny 102.215.88.0/21 +deny 102.215.96.0/19 +deny 102.215.128.0/20 +deny 102.215.148.0/22 +deny 102.215.152.0/22 +deny 102.215.160.0/21 +deny 102.215.168.0/22 +deny 102.215.176.0/20 +deny 102.215.192.0/20 +deny 102.215.208.0/21 +deny 102.215.218.0/23 +deny 102.215.222.0/23 +deny 102.215.224.0/21 +deny 102.215.236.0/22 +deny 102.215.240.0/20 +deny 102.216.0.0/20 +deny 102.216.16.0/21 +deny 102.216.25.0/24 +deny 102.216.27.0/24 +deny 102.216.28.0/22 +deny 102.216.32.0/19 +deny 102.216.64.0/20 +deny 102.216.80.0/21 +deny 102.216.92.0/22 +deny 102.216.96.0/19 +deny 102.216.128.0/19 +deny 102.216.160.0/22 +deny 102.216.168.0/21 +deny 102.216.176.0/20 +deny 102.216.192.0/19 +deny 102.216.224.0/22 +deny 102.216.232.0/21 +deny 102.216.240.0/20 +deny 102.217.0.0/19 +deny 102.217.32.0/20 +deny 102.217.48.0/21 +deny 102.217.56.0/22 +deny 102.217.64.0/20 +deny 102.217.80.0/21 +deny 102.217.88.0/22 +deny 102.217.96.0/19 +deny 102.217.128.0/18 +deny 102.217.192.0/20 +deny 102.217.209.0/24 +deny 102.217.210.0/23 +deny 102.217.212.0/22 +deny 102.217.216.0/21 +deny 102.217.224.0/19 +deny 102.218.0.0/22 +deny 102.218.8.0/21 +deny 102.218.16.0/20 +deny 102.218.32.0/19 +deny 102.218.64.0/19 +deny 102.218.98.0/23 +deny 102.218.100.0/22 +deny 102.218.104.0/21 +deny 102.218.112.0/20 +deny 102.218.128.0/19 +deny 102.218.160.0/21 +deny 102.218.172.0/22 +deny 102.218.176.0/20 +deny 102.218.192.0/19 +deny 102.218.224.0/20 +deny 102.218.240.0/21 +deny 102.218.252.0/22 +deny 102.219.0.0/22 +deny 102.219.8.0/21 +deny 102.219.16.0/20 +deny 102.219.32.0/22 +deny 102.219.40.0/21 +deny 102.219.48.0/20 +deny 102.219.64.0/18 +deny 102.219.132.0/22 +deny 102.219.136.0/21 +deny 102.219.144.0/20 +deny 102.219.160.0/19 +deny 102.219.192.0/20 +deny 102.219.208.0/22 +deny 102.219.216.0/21 +deny 102.219.224.0/20 +deny 102.219.240.0/22 +deny 102.219.248.0/21 +deny 102.220.8.0/21 +deny 102.220.16.0/20 +deny 102.220.36.0/22 +deny 102.220.40.0/21 +deny 102.220.48.0/20 +deny 102.220.64.0/18 +deny 102.220.128.0/21 +deny 102.220.140.0/22 +deny 102.220.144.0/20 +deny 102.220.160.0/20 +deny 102.220.176.0/22 +deny 102.220.184.0/21 +deny 102.220.192.0/19 +deny 102.220.224.0/20 +deny 102.220.244.0/22 +deny 102.220.248.0/21 +deny 102.221.0.0/18 +deny 102.221.64.0/19 +deny 102.221.96.0/21 +deny 102.221.108.0/22 +deny 102.221.112.0/20 +deny 102.221.128.0/20 +deny 102.221.152.0/24 +deny 102.221.154.0/23 +deny 102.221.156.0/22 +deny 102.221.160.0/19 +deny 102.221.192.0/20 +deny 102.221.208.0/22 +deny 102.221.216.0/21 +deny 102.221.224.0/19 +deny 102.222.0.0/20 +deny 102.222.16.0/21 +deny 102.222.25.0/24 +deny 102.222.27.0/24 +deny 102.222.28.0/22 +deny 102.222.36.0/22 +deny 102.222.40.0/21 +deny 102.222.48.0/22 +deny 102.222.56.0/22 +deny 102.222.61.0/24 +deny 102.222.62.0/23 +deny 102.222.64.0/18 +deny 102.222.128.0/18 +deny 102.222.192.0/19 +deny 102.222.228.0/22 +deny 102.222.232.0/22 +deny 102.222.236.0/23 +deny 102.222.240.0/22 +deny 102.222.248.0/22 +deny 102.222.252.0/23 +deny 102.222.255.0/24 +deny 102.223.0.0/22 +deny 102.223.4.0/24 +deny 102.223.6.0/23 +deny 102.223.8.0/21 +deny 102.223.16.0/20 +deny 102.223.32.0/20 +deny 102.223.48.0/21 +deny 102.223.56.0/22 +deny 102.223.62.0/23 +deny 102.223.64.0/22 +deny 102.223.72.0/21 +deny 102.223.80.0/20 +deny 102.223.96.0/22 +deny 102.223.100.0/23 +deny 102.223.103.0/24 +deny 102.223.104.0/22 +deny 102.223.110.0/23 +deny 102.223.114.0/23 +deny 102.223.116.0/22 +deny 102.223.120.0/21 +deny 102.223.128.0/21 +deny 102.223.140.0/22 +deny 102.223.144.0/21 +deny 102.223.154.0/23 +deny 102.223.156.0/22 +deny 102.223.164.0/22 +deny 102.223.168.0/22 +deny 102.223.173.0/24 +deny 102.223.174.0/23 +deny 102.223.176.0/20 +deny 102.223.192.0/19 +deny 102.223.224.0/20 +deny 102.223.240.0/22 +deny 102.223.248.0/21 +deny 102.240.0.0/12 +deny 105.0.0.0/9 +deny 105.128.0.0/11 +deny 105.160.0.0/12 +deny 105.176.0.0/15 +deny 105.178.0.0/17 +deny 105.179.0.0/19 +deny 105.180.0.0/14 +deny 105.184.0.0/13 +deny 105.192.0.0/10 +deny 129.0.0.0/16 +deny 129.18.0.0/16 +deny 129.45.0.0/16 +deny 129.56.0.0/16 +deny 129.122.0.0/16 +deny 129.140.0.0/16 +deny 129.205.0.0/18 +deny 129.205.64.0/20 +deny 129.205.96.0/19 +deny 129.205.128.0/17 +deny 129.232.0.0/16 +deny 137.63.0.0/16 +deny 137.64.0.0/16 +deny 137.115.0.0/16 +deny 137.158.0.0/16 +deny 137.196.0.0/16 +deny 137.214.0.0/15 +deny 137.255.0.0/16 +deny 139.26.0.0/16 +deny 139.53.0.0/16 +deny 143.128.0.0/16 +deny 143.160.0.0/16 +deny 146.64.0.0/16 +deny 146.141.0.0/16 +deny 146.182.0.0/16 +deny 146.196.128.0/17 +deny 146.230.0.0/15 +deny 146.232.0.0/16 +deny 147.110.0.0/16 +deny 152.106.0.0/15 +deny 152.108.0.0/14 +deny 152.112.0.0/16 +deny 154.0.0.0/18 +deny 154.0.128.0/17 +deny 154.16.0.0/16 +deny 154.65.0.0/19 +deny 154.65.32.0/21 +deny 154.65.52.0/22 +deny 154.65.56.0/21 +deny 154.65.64.0/18 +deny 154.65.128.0/17 +deny 154.66.0.0/18 +deny 154.66.64.0/20 +deny 154.66.80.0/22 +deny 154.66.88.0/21 +deny 154.66.96.0/22 +deny 154.66.104.0/21 +deny 154.66.112.0/20 +deny 154.66.128.0/18 +deny 154.66.192.0/19 +deny 154.66.224.0/22 +deny 154.66.240.0/20 +deny 154.67.0.0/16 +deny 154.68.0.0/15 +deny 154.70.0.0/18 +deny 154.70.80.0/20 +deny 154.70.96.0/19 +deny 154.70.128.0/20 +deny 154.70.144.0/21 +deny 154.70.152.0/22 +deny 154.70.160.0/19 +deny 154.70.192.0/22 +deny 154.70.200.0/21 +deny 154.70.208.0/20 +deny 154.70.224.0/19 +deny 154.71.0.0/16 +deny 154.72.0.0/20 +deny 154.72.16.0/21 +deny 154.72.24.0/22 +deny 154.72.32.0/19 +deny 154.72.64.0/18 +deny 154.72.128.0/17 +deny 154.73.0.0/21 +deny 154.73.8.0/22 +deny 154.73.16.0/20 +deny 154.73.32.0/19 +deny 154.73.64.0/22 +deny 154.73.72.0/22 +deny 154.73.80.0/20 +deny 154.73.96.0/20 +deny 154.73.112.0/22 +deny 154.73.120.0/21 +deny 154.73.128.0/20 +deny 154.73.152.0/21 +deny 154.73.160.0/20 +deny 154.73.180.0/22 +deny 154.73.184.0/21 +deny 154.73.192.0/21 +deny 154.73.208.0/20 +deny 154.73.224.0/21 +deny 154.73.232.0/22 +deny 154.73.240.0/20 +deny 154.74.0.0/15 +deny 154.76.0.0/14 +deny 154.80.0.0/12 +deny 154.96.0.0/12 +deny 154.112.0.0/15 +deny 154.114.0.0/17 +deny 154.115.0.0/17 +deny 154.115.128.0/20 +deny 154.115.156.0/22 +deny 154.115.160.0/19 +deny 154.115.192.0/18 +deny 154.116.0.0/16 +deny 154.117.64.0/18 +deny 154.117.128.0/17 +deny 154.118.0.0/17 +deny 154.118.128.0/18 +deny 154.118.192.0/19 +deny 154.118.224.0/21 +deny 154.118.240.0/21 +deny 154.118.252.0/22 +deny 154.119.0.0/16 +deny 154.120.0.0/14 +deny 154.124.0.0/15 +deny 154.126.0.0/16 +deny 154.127.0.0/18 +deny 154.127.64.0/19 +deny 154.127.112.0/20 +deny 154.127.128.0/17 +deny 154.128.0.0/9 +deny 155.0.0.0/16 +deny 155.12.0.0/17 +deny 155.12.128.0/18 +deny 155.12.224.0/19 +deny 155.89.0.0/16 +deny 155.93.0.0/16 +deny 155.159.0.0/16 +deny 155.196.0.0/16 +deny 155.232.0.0/13 +deny 155.240.0.0/16 +deny 155.251.0.0/16 +deny 156.0.0.0/18 +deny 156.0.64.0/20 +deny 156.0.80.0/22 +deny 156.0.88.0/21 +deny 156.0.96.0/24 +deny 156.0.128.0/18 +deny 156.0.192.0/22 +deny 156.0.196.0/23 +deny 156.0.198.0/24 +deny 156.0.200.0/21 +deny 156.0.208.0/21 +deny 156.0.216.0/22 +deny 156.0.224.0/20 +deny 156.0.244.0/22 +deny 156.0.248.0/22 +deny 156.0.252.0/23 +deny 156.0.255.0/24 +deny 156.8.0.0/16 +deny 156.38.4.0/22 +deny 156.38.8.0/21 +deny 156.38.16.0/20 +deny 156.38.32.0/19 +deny 156.38.64.0/18 +deny 156.38.128.0/17 +deny 156.155.0.0/16 +deny 156.156.0.0/14 +deny 156.160.0.0/11 +deny 156.192.0.0/10 +deny 160.0.0.0/17 +deny 160.0.128.0/18 +deny 160.0.192.0/19 +deny 160.19.36.0/22 +deny 160.19.96.0/21 +deny 160.19.112.0/20 +deny 160.19.128.0/20 +deny 160.19.152.0/22 +deny 160.19.188.0/22 +deny 160.19.196.0/22 +deny 160.19.232.0/22 +deny 160.20.24.0/21 +deny 160.20.112.0/22 +deny 160.20.213.0/24 +deny 160.20.217.0/24 +deny 160.20.221.0/24 +deny 160.20.226.0/23 +deny 160.77.0.0/16 +deny 160.89.0.0/16 +deny 160.90.0.0/16 +deny 160.105.0.0/16 +deny 160.113.0.0/16 +deny 160.115.0.0/16 +deny 160.116.0.0/15 +deny 160.118.0.0/16 +deny 160.119.0.0/18 +deny 160.119.64.0/19 +deny 160.119.96.0/21 +deny 160.119.108.0/22 +deny 160.119.112.0/20 +deny 160.119.128.0/18 +deny 160.119.192.0/22 +deny 160.119.196.0/23 +deny 160.119.200.0/21 +deny 160.119.212.0/22 +deny 160.119.216.0/21 +deny 160.119.224.0/19 +deny 160.120.0.0/14 +deny 160.124.0.0/16 +deny 160.152.0.0/16 +deny 160.154.0.0/15 +deny 160.156.0.0/14 +deny 160.160.0.0/12 +deny 160.176.0.0/14 +deny 160.182.0.0/15 +deny 160.224.0.0/15 +deny 160.226.0.0/17 +deny 160.226.128.0/19 +deny 160.226.160.0/20 +deny 160.226.176.0/21 +deny 160.226.184.0/22 +deny 160.226.192.0/18 +deny 160.238.11.0/24 +deny 160.238.50.0/24 +deny 160.238.57.0/24 +deny 160.238.101.0/24 +deny 160.242.0.0/17 +deny 160.242.192.0/18 +deny 161.123.0.0/16 +deny 163.121.0.0/16 +deny 163.195.0.0/16 +deny 163.196.0.0/14 +deny 163.200.0.0/14 +deny 164.88.0.0/16 +deny 164.146.0.0/15 +deny 164.148.0.0/14 +deny 164.155.0.0/16 +deny 164.160.0.0/21 +deny 164.160.8.0/22 +deny 164.160.16.0/21 +deny 164.160.24.0/22 +deny 164.160.32.0/20 +deny 164.160.48.0/21 +deny 164.160.60.0/22 +deny 164.160.64.0/19 +deny 164.160.100.0/22 +deny 164.160.104.0/21 +deny 164.160.116.0/22 +deny 164.160.120.0/21 +deny 164.160.132.0/22 +deny 164.160.136.0/21 +deny 164.160.144.0/22 +deny 164.160.176.0/21 +deny 164.160.184.0/22 +deny 164.160.224.0/19 +deny 165.0.0.0/16 +deny 165.3.0.0/16 +deny 165.4.0.0/15 +deny 165.8.0.0/14 +deny 165.16.0.0/16 +deny 165.25.0.0/16 +deny 165.49.0.0/16 +deny 165.50.0.0/15 +deny 165.56.0.0/13 +deny 165.73.0.0/17 +deny 165.73.128.0/20 +deny 165.73.160.0/19 +deny 165.73.192.0/18 +deny 165.90.0.0/17 +deny 165.90.192.0/18 +deny 165.143.0.0/16 +deny 165.144.0.0/14 +deny 165.148.0.0/15 +deny 165.165.0.0/16 +deny 165.169.0.0/16 +deny 165.180.0.0/16 +deny 165.210.0.0/15 +deny 165.231.0.0/16 +deny 165.233.0.0/16 +deny 165.255.0.0/16 +deny 166.85.0.0/16 +deny 168.76.0.0/16 +deny 168.80.0.0/15 +deny 168.89.0.0/16 +deny 168.128.0.0/16 +deny 168.142.0.0/16 +deny 168.155.0.0/16 +deny 168.164.0.0/16 +deny 168.167.0.0/16 +deny 168.172.0.0/16 +deny 168.206.0.0/16 +deny 168.209.0.0/16 +deny 168.210.0.0/16 +deny 168.253.0.0/16 +deny 169.0.0.0/15 +deny 169.129.0.0/16 +deny 169.159.0.0/16 +deny 169.202.0.0/16 +deny 169.239.0.0/19 +deny 169.239.32.0/20 +deny 169.239.48.0/22 +deny 169.239.60.0/22 +deny 169.239.64.0/20 +deny 169.239.80.0/21 +deny 169.239.92.0/22 +deny 169.239.96.0/19 +deny 169.239.128.0/20 +deny 169.239.148.0/22 +deny 169.239.156.0/22 +deny 169.239.160.0/21 +deny 169.239.168.0/22 +deny 169.239.176.0/20 +deny 169.239.192.0/19 +deny 169.239.224.0/20 +deny 169.239.240.0/21 +deny 169.239.248.0/22 +deny 169.255.0.0/21 +deny 169.255.9.0/24 +deny 169.255.12.0/22 +deny 169.255.16.0/20 +deny 169.255.32.0/19 +deny 169.255.64.0/20 +deny 169.255.80.0/21 +deny 169.255.88.0/22 +deny 169.255.104.0/22 +deny 169.255.112.0/20 +deny 169.255.128.0/21 +deny 169.255.136.0/22 +deny 169.255.144.0/20 +deny 169.255.160.0/22 +deny 169.255.168.0/21 +deny 169.255.176.0/20 +deny 169.255.192.0/21 +deny 169.255.204.0/22 +deny 169.255.208.0/22 +deny 169.255.216.0/21 +deny 169.255.228.0/22 +deny 169.255.232.0/21 +deny 169.255.240.0/20 +deny 192.12.110.0/23 +deny 192.12.116.0/23 +deny 192.33.10.0/24 +deny 192.42.99.0/24 +deny 192.47.36.0/24 +deny 192.48.253.0/24 +deny 192.51.240.0/24 +deny 192.52.232.0/24 +deny 192.68.138.0/24 +deny 192.70.200.0/23 +deny 192.70.237.0/24 +deny 192.75.236.0/24 +deny 192.82.142.0/24 +deny 192.83.208.0/22 +deny 192.84.244.0/24 +deny 192.91.200.0/24 +deny 192.94.61.0/24 +deny 192.94.210.0/24 +deny 192.94.240.0/23 +deny 192.94.246.0/24 +deny 192.96.1.0/24 +deny 192.96.2.0/23 +deny 192.96.4.0/22 +deny 192.96.8.0/21 +deny 192.96.16.0/24 +deny 192.96.19.0/24 +deny 192.96.20.0/23 +deny 192.96.22.0/24 +deny 192.96.24.0/21 +deny 192.96.32.0/22 +deny 192.96.37.0/24 +deny 192.96.38.0/23 +deny 192.96.40.0/24 +deny 192.96.43.0/24 +deny 192.96.45.0/24 +deny 192.96.46.0/23 +deny 192.96.48.0/21 +deny 192.96.56.0/23 +deny 192.96.58.0/24 +deny 192.96.60.0/23 +deny 192.96.68.0/22 +deny 192.96.72.0/24 +deny 192.96.74.0/23 +deny 192.96.76.0/23 +deny 192.96.79.0/24 +deny 192.96.80.0/20 +deny 192.96.96.0/22 +deny 192.96.100.0/23 +deny 192.96.102.0/24 +deny 192.96.104.0/21 +deny 192.96.112.0/20 +deny 192.96.128.0/22 +deny 192.96.132.0/23 +deny 192.96.134.0/24 +deny 192.96.138.0/23 +deny 192.96.140.0/23 +deny 192.96.142.0/24 +deny 192.96.144.0/24 +deny 192.96.146.0/23 +deny 192.96.148.0/22 +deny 192.96.152.0/22 +deny 192.96.158.0/24 +deny 192.96.164.0/22 +deny 192.96.177.0/24 +deny 192.96.178.0/23 +deny 192.96.180.0/22 +deny 192.96.184.0/21 +deny 192.96.193.0/24 +deny 192.96.194.0/23 +deny 192.96.196.0/22 +deny 192.96.224.0/21 +deny 192.96.232.0/24 +deny 192.96.234.0/23 +deny 192.96.236.0/22 +deny 192.96.240.0/22 +deny 192.96.245.0/24 +deny 192.96.246.0/23 +deny 192.96.248.0/22 +deny 192.96.252.0/23 +deny 192.96.254.0/24 +deny 192.100.1.0/24 +deny 192.101.142.0/24 +deny 192.102.9.0/24 +deny 192.109.242.0/24 +deny 192.133.250.0/24 +deny 192.136.55.0/24 +deny 192.136.56.0/23 +deny 192.142.0.0/15 +deny 192.145.128.0/19 +deny 192.145.160.0/22 +deny 192.145.168.0/21 +deny 192.145.176.0/20 +deny 192.145.230.0/24 +deny 192.157.190.0/24 +deny 192.188.164.0/22 +deny 192.189.75.0/24 +deny 192.189.139.0/24 +deny 192.189.140.0/24 +deny 192.231.237.0/24 +deny 192.231.254.0/24 +deny 192.245.148.0/24 +deny 192.251.202.0/24 +deny 193.17.215.0/24 +deny 193.19.232.0/22 +deny 193.41.146.0/23 +deny 193.95.0.0/17 +deny 193.108.23.0/24 +deny 193.108.28.0/24 +deny 193.108.214.0/24 +deny 193.108.252.0/22 +deny 193.109.66.0/23 +deny 193.110.104.0/23 +deny 193.189.0.0/18 +deny 193.194.32.0/19 +deny 193.194.64.0/19 +deny 193.194.128.0/24 +deny 193.194.160.0/19 +deny 193.221.218.0/24 +deny 193.227.0.0/18 +deny 193.227.128.0/24 +deny 194.9.64.0/23 +deny 194.9.82.0/23 +deny 194.79.96.0/19 +deny 194.204.192.0/18 +deny 195.24.80.0/21 +deny 195.24.192.0/19 +deny 195.39.218.0/23 +deny 195.43.0.0/19 +deny 195.166.224.0/19 +deny 195.202.64.0/19 +deny 195.234.120.0/22 +deny 195.234.168.0/24 +deny 195.234.185.0/24 +deny 195.234.252.0/22 +deny 195.246.32.0/19 +deny 196.0.0.0/16 +deny 196.1.0.0/24 +deny 196.1.4.0/24 +deny 196.1.7.0/24 +deny 196.1.8.0/21 +deny 196.1.16.0/21 +deny 196.1.24.0/23 +deny 196.1.28.0/22 +deny 196.1.32.0/20 +deny 196.1.48.0/22 +deny 196.1.56.0/21 +deny 196.1.74.0/23 +deny 196.1.76.0/22 +deny 196.1.80.0/20 +deny 196.1.96.0/21 +deny 196.1.115.0/24 +deny 196.1.116.0/22 +deny 196.1.120.0/21 +deny 196.1.128.0/23 +deny 196.1.130.0/24 +deny 196.1.132.0/23 +deny 196.1.137.0/24 +deny 196.1.143.0/24 +deny 196.1.144.0/20 +deny 196.1.176.0/20 +deny 196.1.192.0/18 +deny 196.2.2.0/23 +deny 196.2.8.0/21 +deny 196.2.16.0/20 +deny 196.2.32.0/19 +deny 196.2.64.0/18 +deny 196.2.128.0/18 +deny 196.2.192.0/19 +deny 196.3.14.0/23 +deny 196.3.58.0/23 +deny 196.3.60.0/22 +deny 196.3.64.0/24 +deny 196.3.90.0/24 +deny 196.3.92.0/23 +deny 196.3.94.0/24 +deny 196.3.96.0/21 +deny 196.3.105.0/24 +deny 196.3.107.0/24 +deny 196.3.108.0/22 +deny 196.3.112.0/20 +deny 196.3.128.0/22 +deny 196.3.148.0/22 +deny 196.3.154.0/23 +deny 196.3.156.0/22 +deny 196.3.160.0/20 +deny 196.3.176.0/21 +deny 196.3.224.0/19 +deny 196.4.0.0/19 +deny 196.4.32.0/21 +deny 196.4.40.0/22 +deny 196.4.44.0/23 +deny 196.4.71.0/24 +deny 196.4.72.0/21 +deny 196.4.80.0/20 +deny 196.4.96.0/19 +deny 196.4.128.0/17 +deny 196.5.0.0/16 +deny 196.6.0.0/18 +deny 196.6.64.0/19 +deny 196.6.96.0/20 +deny 196.6.112.0/21 +deny 196.6.121.0/24 +deny 196.6.122.0/23 +deny 196.6.124.0/22 +deny 196.6.128.0/19 +deny 196.6.160.0/20 +deny 196.6.184.0/23 +deny 196.6.188.0/22 +deny 196.6.198.0/24 +deny 196.6.200.0/21 +deny 196.6.208.0/20 +deny 196.6.224.0/23 +deny 196.6.227.0/24 +deny 196.6.228.0/23 +deny 196.6.231.0/24 +deny 196.6.232.0/21 +deny 196.6.240.0/20 +deny 196.7.0.0/16 +deny 196.8.0.0/15 +deny 196.10.0.0/19 +deny 196.10.32.0/20 +deny 196.10.48.0/23 +deny 196.10.50.0/24 +deny 196.10.52.0/22 +deny 196.10.61.0/24 +deny 196.10.62.0/23 +deny 196.10.64.0/19 +deny 196.10.96.0/22 +deny 196.10.101.0/24 +deny 196.10.102.0/23 +deny 196.10.104.0/21 +deny 196.10.112.0/20 +deny 196.10.128.0/21 +deny 196.10.136.0/22 +deny 196.10.141.0/24 +deny 196.10.142.0/23 +deny 196.10.144.0/23 +deny 196.10.146.0/24 +deny 196.10.148.0/22 +deny 196.10.152.0/21 +deny 196.10.160.0/19 +deny 196.10.192.0/19 +deny 196.10.224.0/20 +deny 196.10.240.0/21 +deny 196.10.248.0/22 +deny 196.11.0.0/20 +deny 196.11.16.0/21 +deny 196.11.24.0/22 +deny 196.11.28.0/23 +deny 196.11.30.0/24 +deny 196.11.32.0/19 +deny 196.11.64.0/21 +deny 196.11.72.0/23 +deny 196.11.74.0/24 +deny 196.11.76.0/22 +deny 196.11.80.0/20 +deny 196.11.96.0/19 +deny 196.11.128.0/18 +deny 196.11.192.0/19 +deny 196.11.224.0/20 +deny 196.11.240.0/21 +deny 196.11.248.0/23 +deny 196.11.252.0/22 +deny 196.12.10.0/23 +deny 196.12.12.0/22 +deny 196.12.16.0/20 +deny 196.12.128.0/19 +deny 196.12.192.0/18 +deny 196.13.0.0/18 +deny 196.13.64.0/19 +deny 196.13.96.0/22 +deny 196.13.100.0/23 +deny 196.13.103.0/24 +deny 196.13.104.0/23 +deny 196.13.107.0/24 +deny 196.13.108.0/22 +deny 196.13.112.0/20 +deny 196.13.128.0/19 +deny 196.13.160.0/20 +deny 196.13.184.0/21 +deny 196.13.192.0/21 +deny 196.13.200.0/24 +deny 196.13.202.0/24 +deny 196.13.204.0/22 +deny 196.13.208.0/20 +deny 196.13.224.0/19 +deny 196.14.0.0/16 +deny 196.15.0.0/20 +deny 196.15.64.0/18 +deny 196.15.128.0/17 +deny 196.16.0.0/14 +deny 196.20.0.0/19 +deny 196.20.64.0/18 +deny 196.20.128.0/17 +deny 196.21.0.0/16 +deny 196.22.0.0/21 +deny 196.22.8.0/22 +deny 196.22.16.0/20 +deny 196.22.32.0/19 +deny 196.22.64.0/18 +deny 196.22.128.0/17 +deny 196.23.0.0/16 +deny 196.24.0.0/15 +deny 196.26.0.0/16 +deny 196.27.64.0/18 +deny 196.27.128.0/17 +deny 196.28.0.0/21 +deny 196.28.8.0/24 +deny 196.28.10.0/23 +deny 196.28.16.0/20 +deny 196.28.32.0/20 +deny 196.28.64.0/18 +deny 196.28.128.0/17 +deny 196.29.0.0/18 +deny 196.29.128.0/17 +deny 196.30.0.0/15 +deny 196.32.8.0/21 +deny 196.32.96.0/19 +deny 196.32.160.0/19 +deny 196.32.192.0/21 +deny 196.32.208.0/20 +deny 196.32.224.0/19 +deny 196.33.0.0/16 +deny 196.34.0.0/15 +deny 196.36.0.0/14 +deny 196.40.96.0/19 +deny 196.40.128.0/17 +deny 196.41.0.0/18 +deny 196.41.65.0/24 +deny 196.41.67.0/24 +deny 196.41.68.0/24 +deny 196.41.72.0/23 +deny 196.41.75.0/24 +deny 196.41.76.0/23 +deny 196.41.80.0/23 +deny 196.41.83.0/24 +deny 196.41.84.0/22 +deny 196.41.88.0/23 +deny 196.41.94.0/23 +deny 196.41.96.0/19 +deny 196.41.128.0/17 +deny 196.42.64.0/18 +deny 196.43.0.0/17 +deny 196.43.128.0/18 +deny 196.43.192.0/24 +deny 196.43.194.0/23 +deny 196.43.196.0/23 +deny 196.43.199.0/24 +deny 196.43.201.0/24 +deny 196.43.202.0/24 +deny 196.43.205.0/24 +deny 196.43.206.0/23 +deny 196.43.208.0/21 +deny 196.43.217.0/24 +deny 196.43.219.0/24 +deny 196.43.220.0/23 +deny 196.43.223.0/24 +deny 196.43.225.0/24 +deny 196.43.226.0/23 +deny 196.43.228.0/22 +deny 196.43.232.0/22 +deny 196.43.236.0/23 +deny 196.43.239.0/24 +deny 196.43.240.0/21 +deny 196.43.248.0/23 +deny 196.43.251.0/24 +deny 196.43.253.0/24 +deny 196.44.0.0/16 +deny 196.45.0.0/18 +deny 196.45.64.0/19 +deny 196.45.96.0/20 +deny 196.45.128.0/17 +deny 196.46.0.0/20 +deny 196.46.16.0/23 +deny 196.46.20.0/22 +deny 196.46.24.0/23 +deny 196.46.27.0/24 +deny 196.46.28.0/24 +deny 196.46.30.0/23 +deny 196.46.32.0/19 +deny 196.46.64.0/19 +deny 196.46.96.0/20 +deny 196.46.116.0/22 +deny 196.46.120.0/21 +deny 196.46.128.0/20 +deny 196.46.144.0/21 +deny 196.46.160.0/19 +deny 196.46.192.0/19 +deny 196.46.224.0/20 +deny 196.46.240.0/21 +deny 196.47.0.0/16 +deny 196.48.0.0/16 +deny 196.49.0.0/20 +deny 196.49.16.0/22 +deny 196.49.20.0/24 +deny 196.49.22.0/24 +deny 196.49.24.0/24 +deny 196.49.26.0/24 +deny 196.49.28.0/24 +deny 196.49.30.0/24 +deny 196.49.32.0/24 +deny 196.49.34.0/24 +deny 196.49.38.0/24 +deny 196.49.40.0/24 +deny 196.49.42.0/24 +deny 196.49.44.0/24 +deny 196.49.46.0/24 +deny 196.49.48.0/24 +deny 196.49.50.0/24 +deny 196.49.52.0/24 +deny 196.49.54.0/24 +deny 196.49.56.0/24 +deny 196.49.58.0/24 +deny 196.49.60.0/24 +deny 196.49.62.0/24 +deny 196.49.64.0/24 +deny 196.49.66.0/24 +deny 196.49.68.0/23 +deny 196.49.72.0/24 +deny 196.49.74.0/24 +deny 196.49.76.0/23 +deny 196.49.82.0/24 +deny 196.49.84.0/24 +deny 196.49.86.0/24 +deny 196.49.88.0/24 +deny 196.49.90.0/24 +deny 196.49.92.0/24 +deny 196.49.96.0/24 +deny 196.49.98.0/24 +deny 196.49.100.0/24 +deny 196.49.102.0/24 +deny 196.49.104.0/24 +deny 196.49.106.0/24 +deny 196.49.108.0/24 +deny 196.49.110.0/24 +deny 196.49.112.0/24 +deny 196.49.114.0/23 +deny 196.49.116.0/24 +deny 196.49.118.0/24 +deny 196.49.120.0/23 +deny 196.49.122.0/24 +deny 196.49.124.0/24 +deny 196.49.126.0/23 +deny 196.50.0.0/20 +deny 196.50.16.0/22 +deny 196.50.20.0/24 +deny 196.50.22.0/23 +deny 196.50.24.0/21 +deny 196.50.32.0/23 +deny 196.50.64.0/18 +deny 196.50.128.0/17 +deny 196.51.0.0/16 +deny 196.56.0.0/14 +deny 196.60.0.0/24 +deny 196.60.2.0/24 +deny 196.60.4.0/24 +deny 196.60.6.0/23 +deny 196.60.8.0/22 +deny 196.60.12.0/24 +deny 196.60.14.0/24 +deny 196.60.36.0/24 +deny 196.60.38.0/24 +deny 196.60.40.0/24 +deny 196.60.42.0/24 +deny 196.60.44.0/24 +deny 196.60.46.0/24 +deny 196.60.48.0/24 +deny 196.60.50.0/24 +deny 196.60.52.0/24 +deny 196.60.54.0/24 +deny 196.60.56.0/24 +deny 196.60.58.0/24 +deny 196.60.61.0/24 +deny 196.60.64.0/24 +deny 196.60.66.0/24 +deny 196.60.68.0/24 +deny 196.60.70.0/23 +deny 196.60.74.0/24 +deny 196.60.76.0/24 +deny 196.60.78.0/24 +deny 196.60.80.0/24 +deny 196.60.82.0/24 +deny 196.60.84.0/24 +deny 196.60.86.0/24 +deny 196.60.88.0/24 +deny 196.60.90.0/24 +deny 196.60.92.0/24 +deny 196.60.94.0/24 +deny 196.60.96.0/23 +deny 196.60.104.0/24 +deny 196.60.106.0/24 +deny 196.60.110.0/24 +deny 196.60.112.0/24 +deny 196.60.114.0/24 +deny 196.60.116.0/24 +deny 196.60.118.0/24 +deny 196.60.120.0/24 +deny 196.60.122.0/24 +deny 196.60.124.0/24 +deny 196.60.126.0/24 +deny 196.60.160.0/24 +deny 196.60.162.0/23 +deny 196.60.164.0/24 +deny 196.60.166.0/24 +deny 196.60.252.0/24 +deny 196.60.254.0/24 +deny 196.61.8.0/21 +deny 196.61.16.0/20 +deny 196.61.32.0/19 +deny 196.61.64.0/20 +deny 196.61.96.0/19 +deny 196.61.128.0/18 +deny 196.61.208.0/20 +deny 196.61.224.0/19 +deny 196.64.0.0/10 +deny 196.128.0.0/11 +deny 196.168.0.0/13 +deny 196.176.0.0/12 +deny 196.192.0.0/17 +deny 196.192.128.0/20 +deny 196.192.160.0/19 +deny 196.196.0.0/14 +deny 196.200.0.0/15 +deny 196.202.0.0/17 +deny 196.202.128.0/18 +deny 196.202.192.0/19 +deny 196.202.232.0/21 +deny 196.202.240.0/20 +deny 196.203.0.0/16 +deny 196.204.0.0/15 +deny 196.206.0.0/16 +deny 196.207.0.0/18 +deny 196.207.128.0/17 +deny 196.208.0.0/13 +deny 196.216.2.0/23 +deny 196.216.4.0/22 +deny 196.216.8.0/21 +deny 196.216.24.0/21 +deny 196.216.32.0/19 +deny 196.216.64.0/18 +deny 196.216.132.0/22 +deny 196.216.136.0/21 +deny 196.216.144.0/20 +deny 196.216.161.0/24 +deny 196.216.162.0/23 +deny 196.216.164.0/22 +deny 196.216.168.0/21 +deny 196.216.176.0/20 +deny 196.216.192.0/22 +deny 196.216.200.0/21 +deny 196.216.208.0/20 +deny 196.216.224.0/21 +deny 196.216.234.0/23 +deny 196.216.236.0/22 +deny 196.216.240.0/20 +deny 196.217.0.0/16 +deny 196.218.0.0/15 +deny 196.220.0.0/17 +deny 196.220.128.0/18 +deny 196.220.192.0/20 +deny 196.220.224.0/19 +deny 196.221.0.0/16 +deny 196.222.0.0/16 +deny 196.223.0.0/23 +deny 196.223.2.0/24 +deny 196.223.4.0/22 +deny 196.223.8.0/22 +deny 196.223.12.0/23 +deny 196.223.15.0/24 +deny 196.223.16.0/20 +deny 196.223.32.0/19 +deny 196.223.64.0/18 +deny 196.223.128.0/17 +deny 196.224.0.0/12 +deny 196.240.0.0/14 +deny 196.244.0.0/15 +deny 196.247.0.0/16 +deny 196.248.0.0/15 +deny 196.250.0.0/17 +deny 196.250.128.0/19 +deny 196.250.176.0/20 +deny 196.250.192.0/19 +deny 196.250.224.0/20 +deny 196.250.240.0/21 +deny 196.250.252.0/22 +deny 196.251.0.0/16 +deny 196.252.0.0/14 +deny 197.0.0.0/9 +deny 197.128.0.0/12 +deny 197.144.0.0/14 +deny 197.148.0.0/17 +deny 197.148.128.0/18 +deny 197.148.192.0/19 +deny 197.149.0.0/17 +deny 197.149.128.0/20 +deny 197.149.144.0/21 +deny 197.149.152.0/22 +deny 197.149.160.0/20 +deny 197.149.176.0/21 +deny 197.149.184.0/22 +deny 197.149.192.0/18 +deny 197.150.0.0/15 +deny 197.152.0.0/14 +deny 197.156.0.0/16 +deny 197.157.0.0/17 +deny 197.157.128.0/18 +deny 197.157.192.0/21 +deny 197.157.204.0/22 +deny 197.157.208.0/20 +deny 197.157.228.0/22 +deny 197.157.232.0/21 +deny 197.157.240.0/20 +deny 197.158.0.0/17 +deny 197.158.192.0/18 +deny 197.159.0.0/18 +deny 197.159.64.0/20 +deny 197.159.88.0/21 +deny 197.159.96.0/20 +deny 197.159.128.0/17 +deny 197.160.0.0/11 +deny 197.192.0.0/12 +deny 197.208.0.0/13 +deny 197.216.0.0/14 +deny 197.220.0.0/17 +deny 197.220.128.0/19 +deny 197.221.0.0/16 +deny 197.222.0.0/15 +deny 197.224.0.0/14 +deny 197.228.0.0/15 +deny 197.230.0.0/16 +deny 197.231.0.0/17 +deny 197.231.128.0/21 +deny 197.231.144.0/20 +deny 197.231.160.0/19 +deny 197.231.192.0/20 +deny 197.231.212.0/22 +deny 197.231.216.0/21 +deny 197.231.224.0/20 +deny 197.231.240.0/21 +deny 197.231.252.0/22 +deny 197.232.0.0/15 +deny 197.234.0.0/17 +deny 197.234.128.0/18 +deny 197.234.192.0/20 +deny 197.234.216.0/21 +deny 197.234.224.0/19 +deny 197.235.0.0/16 +deny 197.236.0.0/14 +deny 197.240.0.0/13 +deny 197.248.0.0/14 +deny 197.252.0.0/15 +deny 197.254.0.0/16 +deny 197.255.0.0/17 +deny 197.255.128.0/18 +deny 197.255.192.0/19 +deny 197.255.224.0/20 +deny 197.255.240.0/21 +deny 197.255.252.0/22 +deny 198.54.1.0/24 +deny 198.54.2.0/23 +deny 198.54.4.0/22 +deny 198.54.8.0/23 +deny 198.54.10.0/24 +deny 198.54.15.0/24 +deny 198.54.16.0/20 +deny 198.54.32.0/20 +deny 198.54.48.0/21 +deny 198.54.56.0/22 +deny 198.54.60.0/23 +deny 198.54.64.0/20 +deny 198.54.80.0/24 +deny 198.54.83.0/24 +deny 198.54.84.0/22 +deny 198.54.91.0/24 +deny 198.54.98.0/23 +deny 198.54.141.0/24 +deny 198.54.142.0/23 +deny 198.54.144.0/21 +deny 198.54.154.0/23 +deny 198.54.156.0/22 +deny 198.54.160.0/22 +deny 198.54.165.0/24 +deny 198.54.170.0/24 +deny 198.54.173.0/24 +deny 198.54.174.0/23 +deny 198.54.176.0/22 +deny 198.54.180.0/23 +deny 198.54.183.0/24 +deny 198.54.184.0/21 +deny 198.54.192.0/21 +deny 198.54.202.0/23 +deny 198.54.204.0/22 +deny 198.54.208.0/23 +deny 198.54.210.0/24 +deny 198.54.219.0/24 +deny 198.54.220.0/24 +deny 198.54.223.0/24 +deny 198.54.225.0/24 +deny 198.54.226.0/23 +deny 198.54.228.0/23 +deny 198.54.231.0/24 +deny 198.54.232.0/24 +deny 198.54.234.0/24 +deny 198.54.247.0/24 +deny 198.54.248.0/22 +deny 198.54.252.0/23 +deny 198.54.254.0/24 +deny 200.16.8.0/21 +deny 202.123.0.0/19 +deny 204.8.204.0/22 +deny 204.12.128.0/20 +deny 204.87.179.0/24 +deny 204.152.14.0/23 +deny 204.235.32.0/21 +deny 204.235.40.0/22 +deny 205.159.79.0/24 +deny 206.223.136.0/24 +deny 208.85.156.0/22 +deny 209.203.0.0/18 +deny 209.212.96.0/19 +deny 212.0.128.0/19 +deny 212.22.160.0/19 +deny 212.49.64.0/19 +deny 212.52.128.0/19 +deny 212.60.64.0/19 +deny 212.85.192.0/19 +deny 212.88.96.0/19 +deny 212.96.0.0/19 +deny 212.100.64.0/19 +deny 212.103.160.0/19 +deny 212.217.0.0/17 +deny 213.55.64.0/18 +deny 213.131.64.0/19 +deny 213.136.96.0/19 +deny 213.150.160.0/19 +deny 213.150.192.0/19 +deny 213.152.64.0/19 +deny 213.154.32.0/19 +deny 213.154.64.0/19 +deny 213.158.160.0/19 +deny 213.172.128.0/19 +deny 213.179.160.0/19 +deny 213.181.224.0/19 +deny 213.193.32.0/19 +deny 213.212.192.0/18 +deny 213.255.128.0/19 +deny 217.14.80.0/20 +deny 217.20.224.0/20 +deny 217.21.112.0/20 +deny 217.29.128.0/20 +deny 217.29.208.0/20 +deny 217.52.0.0/14 +deny 217.64.96.0/20 +deny 217.77.64.0/20 +deny 217.117.0.0/20 +deny 217.139.0.0/16 +deny 217.170.144.0/20 +deny 217.199.144.0/20 diff --git a/tests/fixtures/ta_constraints/afrinic-full-ipv4-deny.constraints b/tests/fixtures/ta_constraints/afrinic-full-ipv4-deny.constraints new file mode 100644 index 0000000..463ba07 --- /dev/null +++ b/tests/fixtures/ta_constraints/afrinic-full-ipv4-deny.constraints @@ -0,0 +1,394 @@ +# Feature #151 remote all5 performance fixture. +# +# Source: official rpki-client 9.8 afrinic.constraints +# ($OpenBSD: afrinic.constraints,v 1.4 2026/03/11 21:46:36 job Exp $). +# Every IPv4 allow entry from that file (including recovered and transfer +# ranges) is converted to deny. IPv6 and ASN entries are intentionally not +# included because this fixture measures the full AFRINIC IPv4 policy. +allow 0.0.0.0/0 +allow ::/0 +allow 0 - 4294967295 + +deny 41.0.0.0/8 +deny 102.0.0.0/8 +deny 105.0.0.0/8 +deny 154.0.0.0/16 +deny 154.16.0.0/16 +deny 154.65.0.0 - 154.255.255.255 +deny 196.0.0.0 - 196.1.0.255 +deny 196.1.4.0/24 +deny 196.1.7.0 - 196.1.63.255 +deny 196.1.71.0/24 +deny 196.1.74.0 - 196.1.103.255 +deny 196.1.115.0 - 196.1.133.255 +deny 196.1.137.0/24 +deny 196.1.143.0 - 196.1.159.255 +deny 196.1.176.0 - 196.1.255.255 +deny 196.2.2.0/23 +deny 196.2.8.0 - 196.2.255.255 +deny 196.3.14.0/23 +deny 196.3.57.0 - 196.3.64.255 +deny 196.3.90.0/24 +deny 196.3.92.0 - 196.3.94.255 +deny 196.3.96.0/21 +deny 196.3.105.0/24 +deny 196.3.107.0 - 196.3.131.255 +deny 196.3.148.0/22 +deny 196.3.154.0 - 196.3.183.255 +deny 196.3.224.0 - 196.4.45.255 +deny 196.4.71.0 - 196.11.171.255 +deny 196.11.174.0 - 196.11.239.255 +deny 196.11.248.0/21 +deny 196.12.10.0 - 196.12.31.255 +deny 196.12.128.0/19 +deny 196.12.192.0 - 196.15.15.255 +deny 196.15.64.0 - 196.26.255.255 +deny 196.27.64.0 - 196.28.47.255 +deny 196.28.64.0 - 196.29.63.255 +deny 196.29.96.0 - 196.31.255.255 +deny 196.32.8.0 - 196.32.31.255 +deny 196.32.96.0/19 +deny 196.32.160.0 - 196.39.255.255 +deny 196.40.96.0 - 196.41.255.255 +deny 196.42.64.0 - 196.216.0.255 +deny 196.216.2.0 - 197.255.255.255 +deny 45.96.0.0 - 45.111.255.255 +deny 45.192.0.0 - 45.222.255.255 +deny 45.240.0.0 - 45.247.255.255 +deny 66.251.128.0 - 66.251.191.255 +deny 139.26.0.0 - 139.26.255.255 +deny 146.196.128.0 - 146.196.255.255 +deny 160.19.36.0 - 160.19.39.255 +deny 160.19.60.0 - 160.19.63.255 +deny 160.19.96.0 - 160.19.103.255 +deny 160.19.112.0 - 160.19.143.255 +deny 160.19.152.0 - 160.19.155.255 +deny 160.19.188.0 - 160.19.191.255 +deny 160.19.192.0 - 160.19.199.255 +deny 160.19.232.0 - 160.19.239.255 +deny 160.20.24.0 - 160.20.31.255 +deny 160.20.112.0 - 160.20.115.255 +deny 160.20.213.0 - 160.20.213.255 +deny 160.20.217.0 - 160.20.217.255 +deny 160.20.221.0 - 160.20.221.255 +deny 160.20.226.0 - 160.20.227.255 +deny 160.20.252.0 - 160.20.255.255 +deny 160.238.11.0 - 160.238.11.255 +deny 160.238.48.0 - 160.238.49.255 +deny 160.238.50.0 - 160.238.50.255 +deny 160.238.57.0 - 160.238.57.255 +deny 160.238.101.0 - 160.238.101.255 +deny 161.123.0.0 - 161.123.255.255 +deny 164.160.0.0 - 164.160.255.255 +deny 192.12.110.0 - 192.12.111.255 +deny 192.12.116.0 - 192.12.117.255 +deny 192.47.36.0 - 192.47.36.255 +deny 192.51.240.0 - 192.51.240.255 +deny 192.70.200.0 - 192.70.201.255 +deny 192.75.236.0 - 192.75.236.255 +deny 192.83.208.0 - 192.83.215.255 +deny 192.91.200.0 - 192.91.200.255 +deny 192.142.0.0 - 192.143.255.255 +deny 192.145.128.0 - 192.145.191.255 +deny 192.145.230.0 - 192.145.230.255 +deny 204.8.204.0 - 204.8.207.255 +deny 208.85.156.0 - 208.85.159.255 +deny 83.143.24.0 - 83.143.31.255 +deny 84.205.96.0 - 84.205.127.255 +deny 131.176.0.0 - 131.176.255.255 +deny 163.121.0.0 - 163.121.255.255 +deny 165.231.0.0 - 165.231.255.255 +deny 192.52.232.0 - 192.52.232.255 +deny 193.17.215.0 - 193.17.215.255 +deny 193.19.232.0 - 193.19.235.255 +deny 193.41.146.0 - 193.41.147.255 +deny 193.108.23.0 - 193.108.23.255 +deny 193.108.28.0 - 193.108.28.255 +deny 193.109.66.0 - 193.109.67.255 +deny 193.110.104.0 - 193.110.105.255 +deny 193.194.128.0 - 193.194.128.255 +deny 193.227.128.0 - 193.227.128.255 +deny 194.9.64.0 - 194.9.65.255 +deny 194.9.82.0 - 194.9.83.255 +deny 195.24.80.0 - 195.24.87.255 +deny 195.39.218.0 - 195.39.219.255 +deny 195.234.120.0 - 195.234.123.255 +deny 195.234.168.0 - 195.234.168.255 +deny 195.234.185.0 - 195.234.185.255 +deny 195.234.252.0 - 195.234.255.255 +deny 193.188.7.0/24 +deny 193.189.0.0/18 +deny 193.189.128.0/24 +deny 193.194.160.0/19 +deny 193.221.218.0/24 +deny 64.57.112.0 - 64.57.127.255 +deny 66.8.0.0 - 66.8.127.255 +deny 66.18.64.0 - 66.18.95.255 +deny 69.63.64.0 - 69.63.79.255 +deny 69.67.32.0 - 69.67.47.255 +deny 137.158.0.0 - 137.158.255.255 +deny 137.214.0.0 - 137.214.255.255 +deny 137.215.0.0 - 137.215.255.255 +deny 139.53.0.0 - 139.53.255.255 +deny 143.128.0.0 - 143.128.255.255 +deny 143.160.0.0 - 143.160.255.255 +deny 146.64.0.0 - 146.64.255.255 +deny 146.141.0.0 - 146.141.255.255 +deny 146.182.0.0 - 146.182.255.255 +deny 146.230.0.0 - 146.230.255.255 +deny 146.231.0.0 - 146.231.255.255 +deny 146.232.0.0 - 146.232.255.255 +deny 147.110.0.0 - 147.110.255.255 +deny 152.106.0.0 - 152.106.255.255 +deny 152.107.0.0 - 152.107.255.255 +deny 152.108.0.0 - 152.108.255.255 +deny 152.109.0.0 - 152.109.255.255 +deny 152.110.0.0 - 152.110.255.255 +deny 152.111.0.0 - 152.111.255.255 +deny 152.112.0.0 - 152.112.255.255 +deny 155.159.0.0 - 155.159.255.255 +deny 155.232.0.0 - 155.232.255.255 +deny 155.233.0.0 - 155.233.255.255 +deny 155.234.0.0 - 155.234.255.255 +deny 155.235.0.0 - 155.235.255.255 +deny 155.236.0.0 - 155.236.255.255 +deny 155.237.0.0 - 155.237.255.255 +deny 155.238.0.0 - 155.238.255.255 +deny 155.239.0.0 - 155.239.255.255 +deny 155.240.0.0 - 155.240.255.255 +deny 156.8.0.0 - 156.8.255.255 +deny 160.115.0.0 - 160.115.255.255 +deny 160.116.0.0 - 160.116.255.255 +deny 160.117.0.0 - 160.117.255.255 +deny 160.118.0.0 - 160.118.255.255 +deny 160.119.0.0 - 160.119.255.255 +deny 160.120.0.0 - 160.120.255.255 +deny 160.121.0.0 - 160.121.255.255 +deny 160.122.0.0 - 160.122.255.255 +deny 160.123.0.0 - 160.123.255.255 +deny 160.124.0.0 - 160.124.255.255 +deny 163.195.0.0 - 163.195.255.255 +deny 163.196.0.0 - 163.196.255.255 +deny 163.197.0.0 - 163.197.255.255 +deny 163.198.0.0 - 163.198.255.255 +deny 163.199.0.0 - 163.199.255.255 +deny 163.200.0.0 - 163.200.255.255 +deny 163.201.0.0 - 163.201.255.255 +deny 163.202.0.0 - 163.202.255.255 +deny 163.203.0.0 - 163.203.255.255 +deny 164.88.0.0 - 164.88.255.255 +deny 164.146.0.0 - 164.151.255.255 +deny 164.155.0.0 - 164.155.255.255 +deny 165.3.0.0 - 165.5.255.255 +deny 165.8.0.0 - 165.11.255.255 +deny 165.25.0.0 - 165.25.255.255 +deny 165.143.0.0 - 165.149.255.255 +deny 165.165.0.0 - 165.165.255.255 +deny 165.180.0.0 - 165.180.255.255 +deny 165.233.0.0 - 165.233.255.255 +deny 166.85.0.0 - 166.85.255.255 +deny 168.76.0.0 - 168.76.255.255 +deny 168.80.0.0 - 168.81.255.255 +deny 168.89.0.0 - 168.89.255.255 +deny 168.128.0.0 - 168.128.255.255 +deny 168.142.0.0 - 168.142.255.255 +deny 168.155.0.0 - 168.155.255.255 +deny 168.164.0.0 - 168.164.255.255 +deny 168.167.0.0 - 168.167.255.255 +deny 168.172.0.0 - 168.172.255.255 +deny 168.206.0.0 - 168.206.255.255 +deny 168.209.0.0 - 168.210.255.255 +deny 169.129.0.0 - 169.129.255.255 +deny 169.202.0.0 - 169.202.255.255 +deny 192.33.10.0 - 192.33.10.255 +deny 192.42.99.0 - 192.42.99.255 +deny 192.48.253.0 - 192.48.253.255 +deny 192.68.138.0 - 192.68.138.255 +deny 192.70.237.0 - 192.70.237.255 +deny 192.82.142.0 - 192.82.142.255 +deny 192.84.244.0 - 192.84.244.255 +deny 192.94.61.0 - 192.94.61.255 +deny 192.94.210.0 - 192.94.210.255 +deny 192.94.240.0 - 192.94.240.255 +deny 192.94.241.0 - 192.94.241.255 +deny 192.94.246.0 - 192.94.246.255 +deny 192.96.0.0 - 192.96.255.255 +deny 192.100.1.0 - 192.100.1.255 +deny 192.101.142.0 - 192.101.142.255 +deny 192.102.9.0 - 192.102.9.255 +deny 192.133.250.0 - 192.133.250.255 +deny 192.136.55.0 - 192.136.55.255 +deny 192.136.56.0 - 192.136.56.255 +deny 192.136.57.0 - 192.136.57.255 +deny 192.157.190.0 - 192.157.190.255 +deny 192.188.164.0 - 192.188.167.255 +deny 192.189.75.0 - 192.189.75.255 +deny 192.189.139.0 - 192.189.140.255 +deny 192.231.237.0 - 192.231.237.255 +deny 192.231.254.0 - 192.231.254.255 +deny 192.245.148.0 - 192.245.148.255 +deny 192.251.202.0 - 192.251.202.255 +deny 198.54.0.0 - 198.54.255.255 +deny 200.16.8.0 - 200.16.15.255 +deny 204.12.128.0 - 204.12.143.255 +deny 204.87.179.0 - 204.87.179.255 +deny 204.152.14.0 - 204.152.15.255 +deny 204.235.32.0 - 204.235.43.255 +deny 205.159.79.0 - 205.159.79.255 +deny 206.223.136.0 - 206.223.136.255 +deny 209.203.0.0 - 209.203.63.255 +deny 209.212.96.0 - 209.212.127.255 +deny 216.236.176.0 - 216.236.191.255 +deny 202.123.0.0/19 +deny 62.8.64.0/19 +deny 62.12.96.0/19 +deny 62.24.96.0/19 +deny 62.61.192.0/18 +deny 62.68.32.0/19 +deny 62.68.224.0/19 +deny 62.114.0.0/16 +deny 62.117.32.0/19 +deny 62.135.0.0/17 +deny 62.139.0.0/16 +deny 62.140.64.0/18 +deny 62.173.32.0/19 +deny 62.193.64.0/18 +deny 62.193.160.0/19 +deny 62.240.32.0/19 +deny 62.240.96.0/19 +deny 62.241.128.0/19 +deny 62.251.128.0/17 +deny 77.220.0.0/19 +deny 80.67.128.0/20 +deny 80.72.96.0/20 +deny 80.75.160.0/19 +deny 80.87.64.0/19 +deny 80.88.0.0/20 +deny 80.95.0.0/20 +deny 80.240.192.0/20 +deny 80.246.0.0/20 +deny 80.248.0.0/20 +deny 80.248.64.0/20 +deny 80.249.64.0/20 +deny 80.250.32.0/20 +deny 81.4.0.0/18 +deny 81.10.0.0/17 +deny 81.21.96.0/20 +deny 81.22.64.0/19 +deny 81.26.64.0/20 +deny 81.29.96.0/20 +deny 81.91.224.0/20 +deny 81.192.0.0/16 +deny 82.101.128.0/18 +deny 82.128.0.0/17 +deny 82.129.128.0/17 +deny 82.151.64.0/19 +deny 82.201.128.0/17 +deny 84.36.0.0/16 +deny 84.233.0.0/17 +deny 87.255.96.0/19 +deny 193.95.0.0/17 +deny 193.108.214.0/24 +deny 193.108.252.0/22 +deny 193.189.64.0 - 193.189.65.255 +deny 193.194.1.0 - 193.194.5.255 +deny 193.194.32.0 - 193.194.95.255 +deny 193.227.0.0/18 +deny 194.6.224.0/24 +deny 194.79.96.0/19 +deny 194.204.192.0/18 +deny 195.24.192.0/19 +deny 195.43.0.0/19 +deny 195.166.224.0/19 +deny 195.202.64.0/19 +deny 195.246.32.0/19 +deny 212.0.128.0/19 +deny 212.12.224.0/19 +deny 212.22.160.0/19 +deny 212.49.64.0/19 +deny 212.52.128.0/19 +deny 212.60.64.0/19 +deny 212.85.192.0/19 +deny 212.88.96.0/19 +deny 212.96.0.0/19 +deny 212.100.64.0/19 +deny 212.103.160.0/19 +deny 212.122.224.0/19 +deny 212.217.0.0/17 +deny 213.55.64.0/18 +deny 213.131.64.0/19 +deny 213.136.96.0/19 +deny 213.147.64.0/19 +deny 213.150.96.0/19 +deny 213.150.160.0 - 213.150.223.255 +deny 213.152.64.0/19 +deny 213.154.32.0 - 213.154.95.255 +deny 213.158.160.0/19 +deny 213.172.128.0/19 +deny 213.179.160.0/19 +deny 213.181.224.0/19 +deny 213.193.32.0/19 +deny 213.212.192.0/18 +deny 213.247.0.0/19 +deny 213.255.128.0/19 +deny 217.14.80.0/20 +deny 217.20.224.0/20 +deny 217.21.112.0/20 +deny 217.29.128.0/20 +deny 217.29.208.0/20 +deny 217.52.0.0/14 +deny 217.64.96.0/20 +deny 217.77.64.0/20 +deny 217.78.64.0/20 +deny 217.117.0.0/20 +deny 217.139.0.0/16 +deny 217.170.144.0/20 +deny 217.199.144.0/20 +deny 129.0.0.0/16 +deny 129.18.0.0/16 +deny 129.45.0.0/16 +deny 129.56.0.0/16 +deny 129.122.0.0/16 +deny 129.140.0.0/16 +deny 129.205.0.0/16 +deny 129.232.0.0/16 +deny 137.63.0.0 - 137.64.255.255 +deny 137.115.0.0/16 +deny 137.171.0.0/16 +deny 137.196.0.0/16 +deny 137.255.0.0/16 +deny 155.0.0.0/16 +deny 155.11.0.0 - 155.12.255.255 +deny 155.89.0.0/16 +deny 155.93.0.0/16 +deny 155.196.0.0/16 +deny 155.251.0.0/16 +deny 155.255.0.0 - 156.0.255.255 +deny 156.38.0.0/16 +deny 156.155.0.0 - 156.255.255.255 +deny 160.0.0.0/16 +deny 160.77.0.0/16 +deny 160.89.0.0 - 160.90.255.255 +deny 160.105.0.0/16 +deny 160.113.0.0/16 +deny 160.152.0.0/16 +deny 160.154.0.0 - 160.179.255.255 +deny 160.181.0.0 - 160.184.255.255 +deny 160.224.0.0 - 160.226.255.255 +deny 160.242.0.0/16 +deny 160.255.0.0/16 +deny 165.0.0.0/16 +deny 165.16.0.0/16 +deny 165.49.0.0 - 165.63.255.255 +deny 165.73.0.0/16 +deny 165.90.0.0/16 +deny 165.169.0.0/16 +deny 165.210.0.0/15 +deny 165.255.0.0/16 +deny 168.211.0.0 - 168.211.255.255 +deny 168.253.0.0/16 +deny 169.0.0.0/15 +deny 169.159.0.0/16 +deny 169.239.0.0/16 +deny 169.255.0.0/16 +deny 192.109.242.0/24 diff --git a/tests/fixtures/ta_constraints/local-custom-allow.constraints b/tests/fixtures/ta_constraints/local-custom-allow.constraints new file mode 100644 index 0000000..5b64975 --- /dev/null +++ b/tests/fixtures/ta_constraints/local-custom-allow.constraints @@ -0,0 +1,2 @@ +# local baseline-v1: the single valid ROA EE certificate has this IPv4 block +allow 203.0.113.0/24 diff --git a/tests/fixtures/ta_constraints/local-custom-deny.constraints b/tests/fixtures/ta_constraints/local-custom-deny.constraints new file mode 100644 index 0000000..9a4ac6c --- /dev/null +++ b/tests/fixtures/ta_constraints/local-custom-deny.constraints @@ -0,0 +1,3 @@ +# local baseline-v1 negative control: deny wins over a covering allow rule +allow 0.0.0.0/0 +deny 203.0.113.0/24 diff --git a/tests/fixtures/ta_constraints/ripe-ncc-afrinic-deny.constraints b/tests/fixtures/ta_constraints/ripe-ncc-afrinic-deny.constraints new file mode 100644 index 0000000..1a9384b --- /dev/null +++ b/tests/fixtures/ta_constraints/ripe-ncc-afrinic-deny.constraints @@ -0,0 +1,12 @@ +# Remote RIPE TA soak policy for feature #151. +# +# Start permissive for every INR kind, then carve out several /8 blocks +# allocated to AFRINIC. These denies should not match normal RIPE NCC +# EE resources, while exercising deny-overrides-allow in a live run. +allow 0.0.0.0/0 +allow ::/0 +allow 0 - 4294967295 + +deny 41.0.0.0/8 +deny 102.0.0.0/8 +deny 105.0.0.0/8