Skip to content

Instantly share code, notes, and snippets.

@albb0920
Created February 15, 2026 05:30
Show Gist options
  • Select an option

  • Save albb0920/09a773369282f6ee5b83f1d2c9dcdedd to your computer and use it in GitHub Desktop.

Select an option

Save albb0920/09a773369282f6ee5b83f1d2c9dcdedd to your computer and use it in GitHub Desktop.
aqtion-freebsd-aq2 issue #5 reproduction script
#!/bin/sh
# Run as root on the test host.
#
# Both-sides-in-jail RA VLAN test:
# - Router in VNET jail on REF_IF (and REF_IF.<vlan> for tagged RA)
# - Client in VNET jail on PARENT_IF (and PARENT_IF.<vlan> as TEST_IF)
# - Compare parent promisc OFF vs ON
#
# Exit:
# 0 => FIXED (baseline works)
# 2 => BUG_PRESENT (baseline fails, promisc passes)
# 1 => INCONCLUSIVE
set -e
TEST_IF="${TEST_IF:-aq0.4}"
REF_IF="${REF_IF:-igc1}"
PARENT_IF="${PARENT_IF:-}"
RA_VLAN_MODE="${RA_VLAN_MODE:-tagged}" # tagged|untagged
RUN_BASELINE="${RUN_BASELINE:-1}"
RUN_PROMISC="${RUN_PROMISC:-1}"
STRESS_ITERS="${STRESS_ITERS:-1}"
VLAN_FLAP_COUNT="${VLAN_FLAP_COUNT:-0}"
ADDR_CHURN_COUNT="${ADDR_CHURN_COUNT:-0}"
ROUTER_JAIL="${ROUTER_JAIL:-ra6router}"
CLIENT_JAIL="${CLIENT_JAIL:-ra6client}"
PREFIX_BASE="${PREFIX_BASE:-fd00:77::}"
ROUTER_ADDR="${ROUTER_ADDR:-fd00:77::1/64}"
EXPECT_PREFIX="${EXPECT_PREFIX:-fd00:77:}"
PREFIX_BASE2="${PREFIX_BASE2:-}"
ROUTER_ADDR2="${ROUTER_ADDR2:-}"
EXPECT_PREFIX2="${EXPECT_PREFIX2:-}"
REQUIRE_BOTH_PREFIXES="${REQUIRE_BOTH_PREFIXES:-0}"
TEST_VLAN_ID=""
CLIENT_TEST_IF=""
ROUTER_RA_IF="$REF_IF"
RCONF="/tmp/rtadvd-${ROUTER_JAIL}.conf"
RLOG="/tmp/${ROUTER_JAIL}.rtadvd.log"
RPKT="/tmp/${ROUTER_JAIL}.ra.pcap.log"
CPKT="/tmp/${CLIENT_JAIL}.ra.pcap.log"
RSLOG="/tmp/${CLIENT_JAIL}.rtsol.log"
IFPLOG="/tmp/${CLIENT_JAIL}.parent.ifconfig.log"
IFVLOG="/tmp/${CLIENT_JAIL}.test.ifconfig.log"
IFPLOG_POST="/tmp/${CLIENT_JAIL}.parent.ifconfig.post.log"
IFVLOG_POST="/tmp/${CLIENT_JAIL}.test.ifconfig.post.log"
RTPID=""
RTPDUMP=""
CTPDUMP=""
if [ -z "$PARENT_IF" ]; then
case "$TEST_IF" in
*.*) PARENT_IF="${TEST_IF%%.*}" ;;
*) PARENT_IF="$TEST_IF" ;;
esac
fi
case "$TEST_IF" in
*.*)
TEST_VLAN_ID="${TEST_IF#*.}"
CLIENT_TEST_IF="${PARENT_IF}.${TEST_VLAN_ID}"
if [ "$RA_VLAN_MODE" = "untagged" ]; then
ROUTER_RA_IF="$REF_IF"
else
ROUTER_RA_IF="${REF_IF}.${TEST_VLAN_ID}"
fi
;;
*)
CLIENT_TEST_IF="$TEST_IF"
ROUTER_RA_IF="$REF_IF"
;;
esac
cleanup() {
set +e
[ -n "$CTPDUMP" ] && jexec "$CLIENT_JAIL" kill -2 "$CTPDUMP" >/dev/null 2>&1 || true
[ -n "$RTPDUMP" ] && jexec "$ROUTER_JAIL" kill -2 "$RTPDUMP" >/dev/null 2>&1 || true
[ -n "$RTPID" ] && jexec "$ROUTER_JAIL" kill -2 "$RTPID" >/dev/null 2>&1 || true
sleep 1
[ -n "$CTPDUMP" ] && jexec "$CLIENT_JAIL" kill -9 "$CTPDUMP" >/dev/null 2>&1 || true
[ -n "$RTPDUMP" ] && jexec "$ROUTER_JAIL" kill -9 "$RTPDUMP" >/dev/null 2>&1 || true
[ -n "$RTPID" ] && jexec "$ROUTER_JAIL" kill -9 "$RTPID" >/dev/null 2>&1 || true
CTPDUMP=""
RTPDUMP=""
RTPID=""
if jls -j "$CLIENT_JAIL" >/dev/null 2>&1; then
jexec "$CLIENT_JAIL" pkill tcpdump >/dev/null 2>&1 || true
jail -r "$CLIENT_JAIL" >/dev/null 2>&1 || true
fi
if jls -j "$ROUTER_JAIL" >/dev/null 2>&1; then
jexec "$ROUTER_JAIL" pkill rtadvd >/dev/null 2>&1 || true
jexec "$ROUTER_JAIL" pkill tcpdump >/dev/null 2>&1 || true
jail -r "$ROUTER_JAIL" >/dev/null 2>&1 || true
fi
}
trap cleanup EXIT INT TERM
wait_jail_iface_active() {
jailn="$1"
ifn="$2"
timeout_s="$3"
i=0
while [ "$i" -lt "$timeout_s" ]; do
if jexec "$jailn" ifconfig "$ifn" >/dev/null 2>&1; then
if jexec "$jailn" ifconfig "$ifn" | grep -q "status: active"; then
return 0
fi
fi
i=$((i + 1))
sleep 1
done
echo "WARN: link not active in jail for $ifn"
jexec "$jailn" ifconfig "$ifn" || true
return 1
}
clear_client_addrs() {
for a in $(jexec "$CLIENT_JAIL" ifconfig "$CLIENT_TEST_IF" inet6 | awk '/inet6 / {print $2}' | grep -v '^fe80:' || true); do
jexec "$CLIENT_JAIL" ifconfig "$CLIENT_TEST_IF" inet6 "$a" delete >/dev/null 2>&1 || true
done
}
flap_client_vlan() {
n=0
while [ "$n" -lt "$VLAN_FLAP_COUNT" ]; do
jexec "$CLIENT_JAIL" ifconfig "$CLIENT_TEST_IF" destroy >/dev/null 2>&1 || true
jexec "$CLIENT_JAIL" ifconfig "$CLIENT_TEST_IF" create
jexec "$CLIENT_JAIL" ifconfig "$CLIENT_TEST_IF" vlan "$TEST_VLAN_ID" vlandev "$PARENT_IF" up
n=$((n + 1))
done
}
churn_client_ipv6_mcast() {
n=0
while [ "$n" -lt "$ADDR_CHURN_COUNT" ]; do
h=$(printf '%x' $((4096 + n)))
addr="fdce:${h}::1"
jexec "$CLIENT_JAIL" ifconfig "$CLIENT_TEST_IF" inet6 "${addr}/64" add >/dev/null 2>&1 || true
jexec "$CLIENT_JAIL" ifconfig "$CLIENT_TEST_IF" inet6 "${addr}" delete >/dev/null 2>&1 || true
n=$((n + 1))
done
}
setup_router_jail() {
jail -c name="$ROUTER_JAIL" host.hostname="$ROUTER_JAIL" persist path=/ allow.raw_sockets vnet vnet.interface="$REF_IF"
jexec "$ROUTER_JAIL" ifconfig "$REF_IF" up
jexec "$ROUTER_JAIL" ifconfig "$REF_IF" inet6 -ifdisabled up
wait_jail_iface_active "$ROUTER_JAIL" "$REF_IF" 20 || true
if [ -n "$TEST_VLAN_ID" ] && [ "$RA_VLAN_MODE" != "untagged" ]; then
jexec "$ROUTER_JAIL" ifconfig "$ROUTER_RA_IF" create
jexec "$ROUTER_JAIL" ifconfig "$ROUTER_RA_IF" vlan "$TEST_VLAN_ID" vlandev "$REF_IF" up
fi
jexec "$ROUTER_JAIL" ifconfig "$ROUTER_RA_IF" inet6 -ifdisabled up
jexec "$ROUTER_JAIL" ifconfig "$ROUTER_RA_IF" inet6 "$ROUTER_ADDR"
if [ -n "$ROUTER_ADDR2" ]; then
jexec "$ROUTER_JAIL" ifconfig "$ROUTER_RA_IF" inet6 "$ROUTER_ADDR2"
fi
jexec "$ROUTER_JAIL" sysctl net.inet6.ip6.forwarding=1 >/dev/null
if [ -n "$PREFIX_BASE2" ]; then
cat > "$RCONF" <<EOF
${ROUTER_RA_IF}:\
:maxinterval#10:mininterval#3:\
:addr="${PREFIX_BASE}":prefixlen#64:\
:addr0="${PREFIX_BASE2}":prefixlen0#64:pinfoflags="la":
EOF
else
cat > "$RCONF" <<EOF
${ROUTER_RA_IF}:\
:maxinterval#10:mininterval#3:\
:addr="${PREFIX_BASE}":prefixlen#64:pinfoflags="la":
EOF
fi
jexec "$ROUTER_JAIL" sh -c "rtadvd -f -c '$RCONF' '$ROUTER_RA_IF' > '$RLOG' 2>&1 & echo \$!" > "/tmp/${ROUTER_JAIL}.rt.pid"
RTPID=$(cat "/tmp/${ROUTER_JAIL}.rt.pid")
sleep 1
if ! jexec "$ROUTER_JAIL" kill -0 "$RTPID" >/dev/null 2>&1; then
echo "ERROR: rtadvd exited early for $ROUTER_RA_IF"
cat "$RLOG" || true
return 1
fi
return 0
}
setup_client_jail() {
promisc_mode="$1" # on|off
jail -c name="$CLIENT_JAIL" host.hostname="$CLIENT_JAIL" persist path=/ allow.raw_sockets vnet vnet.interface="$PARENT_IF"
jexec "$CLIENT_JAIL" ifconfig "$PARENT_IF" up
jexec "$CLIENT_JAIL" ifconfig "$PARENT_IF" inet6 -ifdisabled up
wait_jail_iface_active "$CLIENT_JAIL" "$PARENT_IF" 20 || true
if [ -n "$TEST_VLAN_ID" ]; then
jexec "$CLIENT_JAIL" ifconfig "$CLIENT_TEST_IF" create
jexec "$CLIENT_JAIL" ifconfig "$CLIENT_TEST_IF" vlan "$TEST_VLAN_ID" vlandev "$PARENT_IF" up
if [ "$VLAN_FLAP_COUNT" -gt 0 ]; then
flap_client_vlan
fi
fi
if [ "$promisc_mode" = "on" ]; then
jexec "$CLIENT_JAIL" ifconfig "$PARENT_IF" promisc
else
jexec "$CLIENT_JAIL" ifconfig "$PARENT_IF" -promisc >/dev/null 2>&1 || true
fi
jexec "$CLIENT_JAIL" ifconfig "$CLIENT_TEST_IF" inet6 -ifdisabled accept_rtadv up
clear_client_addrs
if [ "$ADDR_CHURN_COUNT" -gt 0 ]; then
churn_client_ipv6_mcast
fi
jexec "$CLIENT_JAIL" ifconfig "$PARENT_IF" > "$IFPLOG"
jexec "$CLIENT_JAIL" ifconfig "$CLIENT_TEST_IF" > "$IFVLOG"
jexec "$CLIENT_JAIL" netstat -g -f link >/tmp/${CLIENT_JAIL}.mcast.log 2>&1 || true
}
start_captures() {
rm -f "$RPKT" "$CPKT" "$RSLOG"
jexec "$ROUTER_JAIL" sh -c "tcpdump -p -l -U -ni '$ROUTER_RA_IF' -e -vv -c 1 'icmp6 and ip6[40]==134' > '$RPKT' 2>&1 & echo \$!" > "/tmp/${ROUTER_JAIL}.td.pid"
RTPDUMP=$(cat "/tmp/${ROUTER_JAIL}.td.pid")
jexec "$CLIENT_JAIL" sh -c "tcpdump -p -l -U -ni '$CLIENT_TEST_IF' -e -vv -c 1 'icmp6 and ip6[40]==134' > '$CPKT' 2>&1 & echo \$!" > "/tmp/${CLIENT_JAIL}.td.pid"
CTPDUMP=$(cat "/tmp/${CLIENT_JAIL}.td.pid")
}
stop_captures() {
[ -n "$RTPDUMP" ] && jexec "$ROUTER_JAIL" kill -2 "$RTPDUMP" >/dev/null 2>&1 || true
[ -n "$CTPDUMP" ] && jexec "$CLIENT_JAIL" kill -2 "$CTPDUMP" >/dev/null 2>&1 || true
sleep 1
[ -n "$RTPDUMP" ] && jexec "$ROUTER_JAIL" kill -9 "$RTPDUMP" >/dev/null 2>&1 || true
[ -n "$CTPDUMP" ] && jexec "$CLIENT_JAIL" kill -9 "$CTPDUMP" >/dev/null 2>&1 || true
RTPDUMP=""
CTPDUMP=""
}
slaac_present() {
if ! jexec "$CLIENT_JAIL" ifconfig "$CLIENT_TEST_IF" inet6 | grep -q 'autoconf'; then
return 1
fi
if ! jexec "$CLIENT_JAIL" ifconfig "$CLIENT_TEST_IF" inet6 | grep -q "$EXPECT_PREFIX"; then
return 1
fi
if [ "$REQUIRE_BOTH_PREFIXES" = "1" ]; then
if [ -z "$EXPECT_PREFIX2" ]; then
return 1
fi
jexec "$CLIENT_JAIL" ifconfig "$CLIENT_TEST_IF" inet6 | grep -q "$EXPECT_PREFIX2"
fi
}
run_ra_case() {
desc="$1"
promisc_mode="$2"
tag="$promisc_mode"
echo "== Test Case: $desc =="
cleanup 2>/dev/null || true
setup_router_jail || return 1
setup_client_jail "$promisc_mode" || return 1
cp "$IFPLOG" "/tmp/${CLIENT_JAIL}.${tag}.parent.ifconfig.log"
cp "$IFVLOG" "/tmp/${CLIENT_JAIL}.${tag}.test.ifconfig.log"
echo "DEBUG: Client parent ifconfig ($promisc_mode):"
cat "$IFPLOG" || true
echo "DEBUG: Client test ifconfig ($promisc_mode):"
cat "$IFVLOG" || true
start_captures
sleep 1
jexec "$CLIENT_JAIL" rtsol "$CLIENT_TEST_IF" >"$RSLOG" 2>&1 || true
if [ "$REQUIRE_BOTH_PREFIXES" = "1" ]; then
sleep 14
else
sleep 6
fi
stop_captures
jexec "$CLIENT_JAIL" ifconfig "$PARENT_IF" > "$IFPLOG_POST"
jexec "$CLIENT_JAIL" ifconfig "$CLIENT_TEST_IF" > "$IFVLOG_POST"
echo "DEBUG: Client parent ifconfig POST-capture ($promisc_mode):"
cat "$IFPLOG_POST" || true
echo "DEBUG: Client test ifconfig POST-capture ($promisc_mode):"
cat "$IFVLOG_POST" || true
echo " -> Verifying SLAAC on $CLIENT_TEST_IF ($CLIENT_JAIL)"
if slaac_present; then
echo "PASS: $desc"
return 0
fi
echo "FAIL: $desc"
echo "DEBUG: Router capture:"
cat "$RPKT" || true
echo "DEBUG: rtadvd log:"
cat "$RLOG" || true
echo "DEBUG: Client capture:"
cat "$CPKT" || true
echo "DEBUG: rtsol output:"
cat "$RSLOG" || true
echo "DEBUG: Client parent ifconfig:"
cat "$IFPLOG" || true
echo "DEBUG: Client test ifconfig:"
cat "$IFVLOG" || true
return 1
}
if [ "$(id -u)" -ne 0 ]; then
echo "Must run as root."
exit 1
fi
if ! ifconfig "$REF_IF" >/dev/null 2>&1; then
echo "REF_IF not found: $REF_IF"
exit 1
fi
if ! ifconfig "$PARENT_IF" >/dev/null 2>&1; then
echo "PARENT_IF not found: $PARENT_IF"
exit 1
fi
if jls -j "$ROUTER_JAIL" >/dev/null 2>&1 || jls -j "$CLIENT_JAIL" >/dev/null 2>&1; then
echo "Router/client jail already exists; set ROUTER_JAIL/CLIENT_JAIL."
exit 1
fi
echo "=== RA VLAN Repro Test (Both In Jails) ==="
echo "TEST_IF=$TEST_IF CLIENT_TEST_IF=$CLIENT_TEST_IF PARENT_IF=$PARENT_IF REF_IF=$REF_IF RA_VLAN_MODE=$RA_VLAN_MODE ROUTER_RA_IF=$ROUTER_RA_IF STRESS_ITERS=$STRESS_ITERS VLAN_FLAP_COUNT=$VLAN_FLAP_COUNT ADDR_CHURN_COUNT=$ADDR_CHURN_COUNT REQUIRE_BOTH_PREFIXES=$REQUIRE_BOTH_PREFIXES EXPECT_PREFIX=$EXPECT_PREFIX EXPECT_PREFIX2=$EXPECT_PREFIX2"
FINAL_RC=1
ITER=1
while [ "$ITER" -le "$STRESS_ITERS" ]; do
echo ""
echo "---- Iteration $ITER/$STRESS_ITERS ----"
BASE_OK=1
PROMISC_OK=1
if [ "$RUN_BASELINE" = "1" ]; then
if run_ra_case "Baseline (parent promisc OFF) [iter $ITER]" "off"; then
BASE_OK=0
fi
fi
if [ "$RUN_PROMISC" = "1" ]; then
echo ""
if run_ra_case "Parent promisc ON [iter $ITER]" "on"; then
PROMISC_OK=0
fi
fi
if [ "$BASE_OK" -ne 0 ] && [ "$PROMISC_OK" -eq 0 ]; then
FINAL_RC=2
break
fi
if [ "$BASE_OK" -eq 0 ]; then
FINAL_RC=0
fi
ITER=$((ITER + 1))
done
echo ""
echo "=== Summary ==="
if [ "$FINAL_RC" -eq 2 ]; then
echo "Result: BUG_PRESENT (fails baseline, passes with parent promisc)."
exit 2
fi
if [ "$FINAL_RC" -eq 0 ]; then
echo "Result: FIXED (baseline already works without parent promisc)."
exit 0
fi
echo "Result: INCONCLUSIVE (both modes failed or environment mismatch)."
exit 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment