Remove policy scripts

Rhai made a decision about a route cost one to three microseconds,
some two hundred times what the same costs in Rust. That was bearable
where a function is asked once for an UPDATE, and is not where every
route is weighed against every peer, as it is when routes are passed
on. What is wanted of a policy (mark routes by where they came from,
mark what is announced, let communities say who is told what) needs no
language; it is to be configuration, done natively.

Kept from that work: route selection goes by a LOCAL_PREF that policy
set on a route from an external peer, and the lab's configuration can
be added to by a phase.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Rayhaan JaufeerallyandClaude Fable 5.1 committed 2026-10-02 22:47:57 +02:00
1 parent cbf0df1eef
commit 9367c38907
48 files changed
+21 -3435

No files matched your search

+1 -72
View File
@@ -29,7 +29,7 @@ cd "$(dirname "$0")/../.."
bgp="${CARGO_TARGET_DIR:-target}/release/bgp"
work=/tmp/ferrix-lab
pathspace=ferrixlab
all_phases="basics roles rpki refresh flaps restart scale limits hijacks nexthops policy v6only linklocal"
all_phases="basics roles rpki refresh flaps restart scale limits hijacks nexthops v6only linklocal"
phases="${PHASES:-$all_phases}"
scale_routes="${SCALE_ROUTES:-200000}"
@@ -828,77 +828,6 @@ phase_nexthops() {
ok "back to normal"
}
# The operator's own rules, as a script: routes are marked with where
# they were learned and ranked by it, which changes which of two routes
# is chosen; and what each neighbor is told is decided for each.
phase_policy() {
cat > "$work/policy.rhai" <<'END'
const LEARNED_FROM = #{ peer: "64601:2000", provider: "64601:3000" };
// Every route on its way in. Here the provider's routes are the ones to
// prefer, which is the other way about from what selection would do.
fn incoming(route, peer) {
route.remove_community("64601:*");
route.add_community(LEARNED_FROM[peer.role]);
route.local_pref = if peer.role == "provider" { 300 } else { 100 };
true
}
fn to_frr(route, peer) {
route.add_community("64601:777");
route.prepend(2);
true
}
// BIRD is not told our IPv4 prefix.
fn to_bird(route, peer) { !route.within("10.1.0.0/24") }
END
dut_down TERM
dut_config PEER 2000000 "" \
'export { function: "to_frr" }' 'export { function: "to_bird" }' \
'policy { path: "policy.rhai" import_function: "incoming" }'
"$bgp" --check "$work/dut.textpb" | grep -q "frr4: import incoming, export to_frr" \
|| fail "--check does not say which functions are whose: $("$bgp" --check "$work/dut.textpb" 2>&1)"
dut_up
# A prefix nobody has said anything about in the RPKI, from both; of
# the two, FRR's would be chosen.
echo "route 10.77.0.0/24 blackhole;" > "$work/bird-both.conf"
bird_config "$work/bird-both.conf"
bird_ctl configure >/dev/null
frr_configure "router bgp 64602" "address-family ipv4 unicast" "network 10.77.0.0/24"
wait_for "four sessions" established 4
wait_for "FRR's route for it" dut_has 10.77.0.0/24 frr4
wait_for "BIRD's route for it" dut_has 10.77.0.0/24 bird4
# In: marked by where they were learned, and ranked.
local marks
marks=$(api "/api/routes/received?prefix=10.77.0.0/24" | jq -c '[.routes[] | {peer: .peer_name, marks: (tostring | [scan("64601:[0-9]+")] | unique)}] | sort_by(.peer)')
[ "$marks" = '[{"peer":"bird4","marks":["64601:3000"]},{"peer":"frr4","marks":["64601:2000"]}]' ] \
|| fail "the routes are marked $marks"
wait_for "BIRD's route to be the one chosen" best_is 10.77.0.0/24 bird4
wait_for "it in the kernel" dut_kernel -4 "10.77.0.0/24 via 10.9.0.3 dev ix0 metric 20"
peer_is frr4 '.import_function == "incoming" and .export_function == "to_frr"' >/dev/null \
|| fail "the status does not say which functions are the peer's"
ok "routes are marked with where they were learned, and the provider's is chosen and installed"
# Out: FRR is told our route with a longer path and a mark; BIRD is not
# told it at all, and is told the IPv6 one.
wait_for "our route at FRR" bash -c "sudo ip netns exec lab-frr vtysh -N $pathspace -c 'show bgp ipv4 unicast 10.1.0.0/24 json' 2>/dev/null | jq -e '.paths[0] | .aspath.string == \"64601 64601 64601\" and (.community.string | contains(\"64601:777\")) and (.community.string | contains(\"64601:100\"))'"
wait_for "our IPv6 route at BIRD" bird_has 2001:db8:1::/48
sleep 1
bird_has 10.1.0.0/24 && fail "BIRD was told the route its function refuses"
ok "FRR is told our route with a longer path and a mark; BIRD is not told it"
frr_configure "router bgp 64602" "address-family ipv4 unicast" "no network 10.77.0.0/24"
bird_config
bird_ctl configure >/dev/null
dut_down TERM
dut_config
dut_up
converged
ok "back to normal"
}
# The exchange LAN loses its IPv4 addresses. One session over IPv6 with
# each router carries both families, and IPv4 routes have IPv6 next hops
# (RFC 8950), in BGP and in the kernel. Run after the rest: the lab stays