#!/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" < "$work/$side.log" 2>&1 & pids="$pids $!" } start "$a" a pid_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"