249 lines
7.5 KiB
Bash
Executable File
249 lines
7.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage:
|
|
./scripts/cir/run_cir_replay_rpki_client.sh \
|
|
--cir <path> \
|
|
--repo-bytes-db <path> \
|
|
--out-dir <path> \
|
|
--reference-ccr <path> \
|
|
[--build-dir <path> | --rpki-client-bin <path>] \
|
|
[--keep-db] \
|
|
[--real-rsync-bin <path>]
|
|
EOF
|
|
}
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
CIR=""
|
|
REPO_BYTES_DB=""
|
|
OUT_DIR=""
|
|
REFERENCE_CCR=""
|
|
BUILD_DIR=""
|
|
RPKI_CLIENT_BIN=""
|
|
KEEP_DB=0
|
|
REAL_RSYNC_BIN="${REAL_RSYNC_BIN:-/usr/bin/rsync}"
|
|
CIR_MATERIALIZE_BIN="${CIR_MATERIALIZE_BIN:-$ROOT_DIR/target/release/cir_materialize}"
|
|
CIR_EXTRACT_INPUTS_BIN="${CIR_EXTRACT_INPUTS_BIN:-$ROOT_DIR/target/release/cir_extract_inputs}"
|
|
CCR_TO_COMPARE_VIEWS_BIN="${CCR_TO_COMPARE_VIEWS_BIN:-$ROOT_DIR/target/release/ccr_to_compare_views}"
|
|
WRAPPER="$ROOT_DIR/scripts/cir/cir-rsync-wrapper"
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--cir) CIR="$2"; shift 2 ;;
|
|
--repo-bytes-db) REPO_BYTES_DB="$2"; shift 2 ;;
|
|
--out-dir) OUT_DIR="$2"; shift 2 ;;
|
|
--reference-ccr) REFERENCE_CCR="$2"; shift 2 ;;
|
|
--build-dir) BUILD_DIR="$2"; shift 2 ;;
|
|
--rpki-client-bin) RPKI_CLIENT_BIN="$2"; shift 2 ;;
|
|
--keep-db) KEEP_DB=1; shift ;;
|
|
--real-rsync-bin) REAL_RSYNC_BIN="$2"; shift 2 ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) echo "unknown argument: $1" >&2; usage; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
[[ -n "$CIR" && -n "$REPO_BYTES_DB" && -n "$OUT_DIR" && -n "$REFERENCE_CCR" ]] || {
|
|
usage >&2
|
|
exit 2
|
|
}
|
|
if [[ -z "$BUILD_DIR" && -z "$RPKI_CLIENT_BIN" ]]; then
|
|
usage >&2
|
|
exit 2
|
|
fi
|
|
if [[ -z "$RPKI_CLIENT_BIN" ]]; then
|
|
RPKI_CLIENT_BIN="$BUILD_DIR/src/rpki-client"
|
|
fi
|
|
if [[ ! -x "$RPKI_CLIENT_BIN" ]]; then
|
|
echo "rpki-client binary not executable: $RPKI_CLIENT_BIN" >&2
|
|
exit 2
|
|
fi
|
|
|
|
mkdir -p "$OUT_DIR"
|
|
if [[ ! -x "$CIR_MATERIALIZE_BIN" || ! -x "$CIR_EXTRACT_INPUTS_BIN" || ! -x "$CCR_TO_COMPARE_VIEWS_BIN" ]]; then
|
|
(
|
|
cd "$ROOT_DIR"
|
|
cargo build --release --bin cir_materialize --bin cir_extract_inputs --bin ccr_to_compare_views
|
|
)
|
|
fi
|
|
|
|
TMP_ROOT="$OUT_DIR/.tmp"
|
|
TALS_DIR="$TMP_ROOT/tals"
|
|
META_JSON="$TMP_ROOT/meta.json"
|
|
MIRROR_ROOT="$TMP_ROOT/mirror"
|
|
CACHE_DIR="$TMP_ROOT/cache"
|
|
OUT_CCR_DIR="$TMP_ROOT/out"
|
|
RUN_LOG="$OUT_DIR/rpki-client.log"
|
|
ACTUAL_VRPS="$OUT_DIR/actual-vrps.csv"
|
|
ACTUAL_VAPS="$OUT_DIR/actual-vaps.csv"
|
|
ACTUAL_VAPS_META="$OUT_DIR/actual-vaps-meta.json"
|
|
ACTUAL_VRPS_META="$OUT_DIR/actual-vrps-meta.json"
|
|
REF_VRPS="$OUT_DIR/reference-vrps.csv"
|
|
REF_VAPS="$OUT_DIR/reference-vaps.csv"
|
|
SUMMARY_JSON="$OUT_DIR/compare-summary.json"
|
|
|
|
rm -rf "$TMP_ROOT"
|
|
mkdir -p "$TMP_ROOT"
|
|
|
|
"$CIR_EXTRACT_INPUTS_BIN" --cir "$CIR" --tals-dir "$TALS_DIR" --meta-json "$META_JSON"
|
|
python3 - <<'PY' "$TALS_DIR"
|
|
from pathlib import Path
|
|
import sys
|
|
for tal in Path(sys.argv[1]).glob("*.tal"):
|
|
lines = tal.read_text(encoding="utf-8").splitlines()
|
|
rsync_uris = [line for line in lines if line.startswith("rsync://")]
|
|
base64_lines = []
|
|
seen_sep = False
|
|
for line in lines:
|
|
if seen_sep:
|
|
if line.strip():
|
|
base64_lines.append(line)
|
|
elif line.strip() == "":
|
|
seen_sep = True
|
|
tal.write_text("\n".join(rsync_uris) + "\n\n" + "\n".join(base64_lines) + "\n", encoding="utf-8")
|
|
PY
|
|
materialize_cmd=("$CIR_MATERIALIZE_BIN" --cir "$CIR" --repo-bytes-db "$REPO_BYTES_DB" --mirror-root "$MIRROR_ROOT")
|
|
if [[ "$KEEP_DB" -eq 1 ]]; then
|
|
materialize_cmd+=(--keep-db)
|
|
fi
|
|
"${materialize_cmd[@]}"
|
|
|
|
VALIDATION_EPOCH="$(python3 - <<'PY' "$META_JSON"
|
|
from datetime import datetime, timezone
|
|
import json, sys
|
|
vt = json.load(open(sys.argv[1]))["validationTime"]
|
|
dt = datetime.fromisoformat(vt.replace("Z", "+00:00")).astimezone(timezone.utc)
|
|
print(int(dt.timestamp()))
|
|
PY
|
|
)"
|
|
mapfile -t TAL_PATHS < <(python3 - <<'PY' "$META_JSON"
|
|
import json, sys
|
|
for item in json.load(open(sys.argv[1], encoding="utf-8"))["talFiles"]:
|
|
print(item["path"])
|
|
PY
|
|
)
|
|
CLIENT_TAL_ARGS=()
|
|
for tal_path in "${TAL_PATHS[@]}"; do
|
|
CLIENT_TAL_ARGS+=(-t "$tal_path")
|
|
done
|
|
COMPARE_TRUST_ANCHOR="unknown"
|
|
|
|
export CIR_MIRROR_ROOT="$(python3 - <<'PY' "$MIRROR_ROOT"
|
|
from pathlib import Path
|
|
import sys
|
|
print(Path(sys.argv[1]).resolve())
|
|
PY
|
|
)"
|
|
export REAL_RSYNC_BIN="$REAL_RSYNC_BIN"
|
|
export CIR_LOCAL_LINK_MODE=1
|
|
|
|
mkdir -p "$CACHE_DIR" "$OUT_CCR_DIR"
|
|
chmod -R 0777 "$TMP_ROOT"
|
|
"$RPKI_CLIENT_BIN" \
|
|
-R \
|
|
-e "$WRAPPER" \
|
|
-P "$VALIDATION_EPOCH" \
|
|
"${CLIENT_TAL_ARGS[@]}" \
|
|
-d "$CACHE_DIR" \
|
|
"$OUT_CCR_DIR" >"$RUN_LOG" 2>&1
|
|
|
|
if [[ -f "$OUT_CCR_DIR/rpki.ccr" ]]; then
|
|
"$CCR_TO_COMPARE_VIEWS_BIN" \
|
|
--ccr "$OUT_CCR_DIR/rpki.ccr" \
|
|
--vrps-out "$ACTUAL_VRPS" \
|
|
--vaps-out "$ACTUAL_VAPS" \
|
|
--trust-anchor "$COMPARE_TRUST_ANCHOR"
|
|
else
|
|
python3 - <<'PY' "$OUT_CCR_DIR/json" "$ACTUAL_VRPS" "$ACTUAL_VAPS" "$COMPARE_TRUST_ANCHOR"
|
|
import csv
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
json_path = Path(sys.argv[1])
|
|
vrps_out = Path(sys.argv[2])
|
|
vaps_out = Path(sys.argv[3])
|
|
compare_ta = sys.argv[4]
|
|
|
|
if not json_path.is_file():
|
|
raise SystemExit(f"rpki-client output has neither rpki.ccr nor json: {json_path}")
|
|
|
|
data = json.loads(json_path.read_text(encoding="utf-8"))
|
|
|
|
vrps_out.parent.mkdir(parents=True, exist_ok=True)
|
|
with vrps_out.open("w", newline="", encoding="utf-8") as fh:
|
|
writer = csv.writer(fh)
|
|
writer.writerow(["ASN", "IP Prefix", "Max Length", "Trust Anchor"])
|
|
for roa in data.get("roas", []):
|
|
writer.writerow([
|
|
f"AS{roa['asn']}",
|
|
roa["prefix"],
|
|
str(roa["maxLength"]),
|
|
compare_ta,
|
|
])
|
|
|
|
with vaps_out.open("w", newline="", encoding="utf-8") as fh:
|
|
writer = csv.writer(fh)
|
|
writer.writerow(["Customer ASN", "Providers", "Trust Anchor"])
|
|
for aspa in data.get("aspas", []):
|
|
providers = ";".join(f"AS{item}" for item in sorted(aspa.get("providers", [])))
|
|
writer.writerow([
|
|
f"AS{aspa['customer_asid']}",
|
|
providers,
|
|
compare_ta,
|
|
])
|
|
PY
|
|
fi
|
|
|
|
python3 - <<'PY' "$ACTUAL_VRPS" "$ACTUAL_VAPS" "$ACTUAL_VRPS_META" "$ACTUAL_VAPS_META"
|
|
import csv, json, sys
|
|
def count_rows(path):
|
|
with open(path, newline="") as f:
|
|
rows = list(csv.reader(f))
|
|
return max(len(rows) - 1, 0)
|
|
json.dump({"count": count_rows(sys.argv[1])}, open(sys.argv[3], "w"), indent=2)
|
|
json.dump({"count": count_rows(sys.argv[2])}, open(sys.argv[4], "w"), indent=2)
|
|
PY
|
|
|
|
"$CCR_TO_COMPARE_VIEWS_BIN" --ccr "$REFERENCE_CCR" --vrps-out "$REF_VRPS" --vaps-out "$REF_VAPS" --trust-anchor "$COMPARE_TRUST_ANCHOR"
|
|
|
|
python3 - <<'PY' "$ACTUAL_VRPS" "$REF_VRPS" "$ACTUAL_VAPS" "$REF_VAPS" "$SUMMARY_JSON" "$META_JSON"
|
|
import csv, json, sys
|
|
def rows(path):
|
|
with open(path, newline="") as f:
|
|
return list(csv.reader(f))[1:]
|
|
actual_vrps = {tuple(r) for r in rows(sys.argv[1])}
|
|
ref_vrps = {tuple(r) for r in rows(sys.argv[2])}
|
|
actual_vaps = {tuple(r) for r in rows(sys.argv[3])}
|
|
ref_vaps = {tuple(r) for r in rows(sys.argv[4])}
|
|
meta = json.load(open(sys.argv[6], encoding="utf-8"))
|
|
summary = {
|
|
"compareMode": "trust-anchor-agnostic",
|
|
"talCount": len(meta["talFiles"]),
|
|
"talPaths": [item["path"] for item in meta["talFiles"]],
|
|
"vrps": {
|
|
"actual": len(actual_vrps),
|
|
"reference": len(ref_vrps),
|
|
"match": actual_vrps == ref_vrps,
|
|
"only_in_actual": sorted(actual_vrps - ref_vrps)[:20],
|
|
"only_in_reference": sorted(ref_vrps - actual_vrps)[:20],
|
|
},
|
|
"vaps": {
|
|
"actual": len(actual_vaps),
|
|
"reference": len(ref_vaps),
|
|
"match": actual_vaps == ref_vaps,
|
|
"only_in_actual": sorted(actual_vaps - ref_vaps)[:20],
|
|
"only_in_reference": sorted(ref_vaps - actual_vaps)[:20],
|
|
}
|
|
}
|
|
with open(sys.argv[5], "w") as f:
|
|
json.dump(summary, f, indent=2)
|
|
PY
|
|
|
|
if [[ "$KEEP_DB" -ne 1 ]]; then
|
|
rm -rf "$TMP_ROOT"
|
|
fi
|
|
|
|
echo "done: $OUT_DIR"
|