- scripts/in-namespace.sh set CARGO_TARGET_DIR to the empty string when it was unset, which cargo refuses, and dropped RUSTFLAGS, so the run in the namespace rebuilt what kernel-tests.sh had just built. Each is passed on only where set. - The README's list of unsupported features still had route reflection and graceful restart, both of which exist; the command-line reference lacked SIGUSR1 and exit status 75; the HTTP API reference did not say what errors look like. - The event stream said "missed 0 events" for the best-routes stream, which does not count what it missed; it leaves the count out now. - CI lints bgp-bench with its count-memory feature, which nothing built. - bgp-bench had two unused dev-dependencies; bgp-integration-tests had three dependencies that only its tests and examples use. - install.sh updates apt before installing; two-routers.sh loses an unused variable; small things in the web crate. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
144 lines
5.2 KiB
Bash
Executable File
144 lines
5.2 KiB
Bash
Executable File
#!/bin/sh
|
|
# Two daemons that route for each other, on a real kernel: the proof that
|
|
# what BGP learns ends up as forwarding.
|
|
#
|
|
# namespace a namespace b
|
|
# lan 10.1.0.1/24 lan 10.2.0.1/24
|
|
# 2001:db8:a::1/48 2001:db8:b::1/48
|
|
# a0 10.0.0.1/24 ------ veth ------ b0 10.0.0.2/24
|
|
# 2001:db8::1/64 2001:db8::2/64
|
|
#
|
|
# Each originates its own "lan" prefixes and installs what it learns in
|
|
# its kernel. The test passes when each kernel has the other's prefixes as
|
|
# BGP routes and a ping from one lan address to the other gets through,
|
|
# over IPv4 and IPv6; and when the routes go again as a daemon is stopped.
|
|
#
|
|
# Run it on Linux as a user who can sudo, after `cargo build -p bgp`;
|
|
# scripts/kernel-tests.sh does both.
|
|
set -eu
|
|
|
|
cd "$(dirname "$0")/.."
|
|
bgp="${CARGO_TARGET_DIR:-target}/debug/bgp"
|
|
[ -x "$bgp" ] || { echo "$bgp not found: cargo build -p bgp" >&2; exit 1; }
|
|
|
|
work="$(mktemp -d)"
|
|
a="ferrix-a-$$"
|
|
b="ferrix-b-$$"
|
|
pids=""
|
|
cleanup() {
|
|
status=$?
|
|
for pid in $pids; do sudo kill "$pid" 2>/dev/null || true; done
|
|
sudo ip netns del "$a" 2>/dev/null || true
|
|
sudo ip netns del "$b" 2>/dev/null || true
|
|
if [ "$status" -ne 0 ]; then
|
|
echo "--- FAILED; the daemons said:" >&2
|
|
tail -n 30 "$work"/*.log >&2 || true
|
|
fi
|
|
rm -rf "$work"
|
|
exit "$status"
|
|
}
|
|
trap cleanup EXIT INT TERM
|
|
|
|
in_a() { sudo ip netns exec "$a" "$@"; }
|
|
in_b() { sudo ip netns exec "$b" "$@"; }
|
|
|
|
sudo modprobe dummy 2>/dev/null || true
|
|
sudo ip netns add "$a"
|
|
sudo ip netns add "$b"
|
|
sudo ip link add a0 netns "$a" type veth peer name b0 netns "$b"
|
|
for side in a b; do
|
|
case $side in a) n=1; run=in_a ;; b) n=2; run=in_b ;; esac
|
|
$run ip link set lo up
|
|
$run ip addr add "10.0.0.$n/24" dev "${side}0"
|
|
$run ip -6 addr add "2001:db8::$n/64" dev "${side}0" nodad
|
|
$run ip link set "${side}0" up
|
|
$run ip link add lan type dummy
|
|
$run ip addr add "10.$n.0.1/24" dev lan
|
|
$run ip -6 addr add "2001:db8:$side::1/48" dev lan nodad
|
|
$run ip link set lan up
|
|
done
|
|
|
|
# One session for each IP version: routes are sent with the session's own
|
|
# address as next hop, which has to be of the routes' version.
|
|
config() {
|
|
side=$1; n=$2; other=$3
|
|
cat > "$work/$side.textpb" <<END
|
|
router_id: "10.0.0.$n"
|
|
local_asn: 6500$n
|
|
listen_endpoints { address: "10.0.0.$n" port: 179 }
|
|
listen_endpoints { address: "2001:db8::$n" port: 179 }
|
|
peers {
|
|
name: "v4" endpoint { address: "10.0.0.$other" } remote_asn: 6500$other
|
|
address_families: IPV4_UNICAST
|
|
import { accept_bogons: true max_prefixes: 100 }
|
|
}
|
|
peers {
|
|
name: "v6" endpoint { address: "2001:db8::$other" } remote_asn: 6500$other
|
|
address_families: IPV6_UNICAST
|
|
import { accept_bogons: true max_prefixes: 100 }
|
|
}
|
|
connect_retry_seconds: 1
|
|
originate { prefix: "10.$n.0.0/24" }
|
|
originate { prefix: "2001:db8:$side::/48" }
|
|
kernel {}
|
|
END
|
|
}
|
|
config a 1 2
|
|
config b 2 1
|
|
|
|
# As the user, with what the daemon needs and no more: CAP_NET_ADMIN for
|
|
# the routes, CAP_NET_BIND_SERVICE for port 179.
|
|
start() {
|
|
namespace=$1; side=$2
|
|
sudo ip netns exec "$namespace" \
|
|
setpriv --reuid "$(id -u)" --regid "$(id -g)" --init-groups \
|
|
--inh-caps +net_admin,+net_bind_service --ambient-caps +net_admin,+net_bind_service \
|
|
"$bgp" "$work/$side.textpb" > "$work/$side.log" 2>&1 &
|
|
pids="$pids $!"
|
|
}
|
|
start "$a" a
|
|
start "$b" b
|
|
|
|
# Waits until `ip route` in a namespace shows, or no longer shows, a route.
|
|
expect() {
|
|
run=$1; family=$2; want=$3; route=$4
|
|
for _ in $(seq 100); do
|
|
if $run ip "$family" route show proto bgp | sed 's/ */ /g; s/ nhid [0-9]*//; s/ pref medium//; s/ *$//' | grep -qxF "$route"; then
|
|
[ "$want" = present ] && return 0
|
|
else
|
|
[ "$want" = absent ] && return 0
|
|
fi
|
|
sleep 0.2
|
|
done
|
|
echo "expected the route \"$route\" to be $want; the kernel's BGP routes are:" >&2
|
|
$run ip "$family" route show proto bgp >&2
|
|
return 1
|
|
}
|
|
|
|
expect in_a -4 present "10.2.0.0/24 via 10.0.0.2 dev a0 metric 20"
|
|
expect in_b -4 present "10.1.0.0/24 via 10.0.0.1 dev b0 metric 20"
|
|
expect in_a -6 present "2001:db8:b::/48 via 2001:db8::2 dev a0 metric 20"
|
|
expect in_b -6 present "2001:db8:a::/48 via 2001:db8::1 dev b0 metric 20"
|
|
echo "ok: each kernel has the other's prefixes as BGP routes"
|
|
|
|
# From one lan address to the other: there and back by the routes BGP put
|
|
# there.
|
|
in_a ping -q -c 2 -W 2 -I 10.1.0.1 10.2.0.1 > /dev/null
|
|
in_b ping -q -c 2 -W 2 -I 10.2.0.1 10.1.0.1 > /dev/null
|
|
in_a ping -q -c 2 -W 2 -I 2001:db8:a::1 2001:db8:b::1 > /dev/null
|
|
echo "ok: packets get through, over IPv4 and IPv6"
|
|
|
|
# One daemon is stopped in good order. It takes its routes out of its
|
|
# kernel; the other, told the session is over, does the same with what it
|
|
# learned.
|
|
sudo ip netns pids "$a" | xargs sudo kill -TERM
|
|
expect in_a -4 absent "10.2.0.0/24 via 10.0.0.2 dev a0 metric 20"
|
|
expect in_a -6 absent "2001:db8:b::/48 via 2001:db8::2 dev a0 metric 20"
|
|
expect in_b -4 absent "10.1.0.0/24 via 10.0.0.1 dev b0 metric 20"
|
|
expect in_b -6 absent "2001:db8:a::/48 via 2001:db8::1 dev b0 metric 20"
|
|
if in_b ping -q -c 1 -W 1 -I 10.2.0.1 10.1.0.1 > /dev/null 2>&1; then
|
|
echo "packets still get through with the routes gone" >&2
|
|
exit 1
|
|
fi
|
|
echo "ok: the routes go when a daemon stops"
|