scripts/interop/lab.sh runs the daemon against FRR, BIRD and Routinator, each in a network namespace on one exchange LAN, and checks what every side ends up with: sessions and routes over IPv4 and IPv6 with their attributes, four-octet AS numbers, ADD-PATH, RFC 9234 roles and Only to Customer, RTR version 2 and invalid routes, route refresh, session resets, killed peers, a cut wire, the daemon stopped and killed, a table of a million routes and a restart under it, role mismatch and the prefix limit. All of it passes against FRR 10.5.1, BIRD 2.18 and Routinator 0.15.2. scripts/interop/real-rpki.sh checks the RTR client against a Routinator with the real RPKI: 713,258 IPv4 and 187,479 IPv6 VRPs and 3,379 ASPAs, the same numbers on both sides. What the lab found, fixed here: - A daemon restarted with its routes in the kernel removed them all at once, its table being empty until the peers had sent theirs again, and then put them back: no forwarding meanwhile. Routes of an earlier run are now kept for `stale_seconds` (120 by default) and only those nobody announced again are removed. A restart under a million routes takes 1.1 s to be in step, with nothing removed. - FRR ends a session it loses to a collision with a state machine error without a subcode, which was shown as "error code 5 subcode 0". CI gets a job that runs the lab. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
71 lines
3.0 KiB
Bash
Executable File
71 lines
3.0 KiB
Bash
Executable File
#!/bin/bash
|
|
# The RTR client against a validator that has the real RPKI: what the
|
|
# daemon ends up with has to be what the validator says it has.
|
|
#
|
|
# The lab (lab.sh) gives Routinator made-up data, which says nothing about
|
|
# ASPAs (Routinator does not take those from a local file) nor about data
|
|
# of the Internet's size. This does, against a Routinator you run
|
|
# yourself, which takes a quarter of an hour and some gigabytes the first
|
|
# time:
|
|
#
|
|
# routinator --enable-aspa server --rtr 127.0.0.1:3324 --http 127.0.0.1:8324
|
|
# scripts/interop/real-rpki.sh # those are the defaults
|
|
# scripts/interop/real-rpki.sh 127.0.0.1 3324 http://127.0.0.1:8324
|
|
#
|
|
# The daemon is run with no peers and nothing but the validator
|
|
# configured, on a port of its own; nothing else is touched.
|
|
set -euo pipefail
|
|
|
|
cd "$(dirname "$0")/../.."
|
|
address=${1:-127.0.0.1}
|
|
port=${2:-3324}
|
|
validator=${3:-http://127.0.0.1:8324}
|
|
bgp="${CARGO_TARGET_DIR:-target}/release/bgp"
|
|
[ -x "$bgp" ] || { echo "$bgp not found: cargo build --release -p bgp" >&2; exit 1; }
|
|
work=$(mktemp -d)
|
|
trap 'kill "$pid" 2>/dev/null || true; rm -rf "$work"' EXIT
|
|
|
|
cat > "$work/config.textpb" <<END
|
|
router_id: "192.0.2.1"
|
|
local_asn: 64601
|
|
listen_endpoints { address: "127.0.0.1" port: 11790 }
|
|
rpki { caches { address: "$address" port: $port } }
|
|
http_endpoint { address: "127.0.0.1" port: 18080 }
|
|
END
|
|
"$bgp" "$work/config.textpb" > "$work/log" 2>&1 &
|
|
pid=$!
|
|
|
|
for _ in $(seq 600); do
|
|
if curl -sf http://127.0.0.1:18080/api/rpki | jq -e '.state == "synchronised"' >/dev/null 2>&1; then
|
|
break
|
|
fi
|
|
sleep 0.2
|
|
done
|
|
ours=$(curl -sf http://127.0.0.1:18080/api/rpki)
|
|
jq -e '.state == "synchronised"' <<<"$ours" >/dev/null || {
|
|
echo "FAILED: not synchronised: $ours" >&2
|
|
tail -n 5 "$work/log" >&2
|
|
exit 1
|
|
}
|
|
|
|
# What the validator says it has: distinct VRPs, and ASes with an ASPA.
|
|
theirs=$(curl -sf "$validator/json")
|
|
their_v4=$(jq '[.roas[] | select(.prefix | contains(":") | not) | {asn, prefix, maxLength}] | unique | length' <<<"$theirs")
|
|
their_v6=$(jq '[.roas[] | select(.prefix | contains(":")) | {asn, prefix, maxLength}] | unique | length' <<<"$theirs")
|
|
their_aspas=$(jq '[(.aspas // [])[] | .customer] | unique | length' <<<"$theirs")
|
|
our_v4=$(jq .vrps_ipv4 <<<"$ours")
|
|
our_v6=$(jq .vrps_ipv6 <<<"$ours")
|
|
our_aspas=$(jq .aspas <<<"$ours")
|
|
|
|
echo "RTR version $(jq .protocol_version <<<"$ours"), serial $(jq .serial <<<"$ours")"
|
|
echo "IPv4 VRPs: validator $their_v4, daemon $our_v4"
|
|
echo "IPv6 VRPs: validator $their_v6, daemon $our_v6"
|
|
echo "ASPAs: validator $their_aspas, daemon $our_aspas"
|
|
# The validator may have moved on a serial between the two looks; a
|
|
# handful apart is that, more is not.
|
|
close() { [ $(( $1 > $2 ? $1 - $2 : $2 - $1 )) -le $(( $1 / 200 + 5 )) ]; }
|
|
close "$their_v4" "$our_v4" && close "$their_v6" "$our_v6" && close "$their_aspas" "$our_aspas" \
|
|
|| { echo "FAILED: the daemon does not have what the validator has" >&2; exit 1; }
|
|
[ "$our_v4" -gt 0 ] || { echo "FAILED: no data at all" >&2; exit 1; }
|
|
echo "ok: the daemon has what the validator has"
|