Merge branch 'master' into next

This commit is contained in:
Johnathan Corgan
2026-07-27 17:57:36 +00:00
10 changed files with 378 additions and 97 deletions
+18 -7
View File
@@ -6,7 +6,12 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
NAT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
ROOT_DIR="$(cd "$NAT_DIR/../.." && pwd)"
DERIVE_KEYS="$ROOT_DIR/testing/lib/derive_keys.py"
OUTPUT_DIR="$NAT_DIR/generated-configs"
# Per-run, for the same reason static's and firewall's generators are: this
# directory is bind-mounted by compose and read back by the suite scripts
# AFTER the containers are up, so a shared path lets a second run's generator
# overwrite the npubs a first run is about to ping. Empty suffix renders
# today's plain path, so a bare invocation is unchanged.
OUTPUT_DIR="$NAT_DIR/generated-configs${FIPS_CI_NAME_SUFFIX:-}"
SCENARIO="${1:?usage: generate-configs.sh <cone|symmetric|lan> [mesh-name]}"
MESH_NAME="${2:-nat-lab-$(date +%s)-$$}"
@@ -28,12 +33,18 @@ npub_a="$(echo "$keys_a" | awk -F= '/^npub=/{print $2}')"
nsec_b="$(echo "$keys_b" | awk -F= '/^nsec=/{print $2}')"
npub_b="$(echo "$keys_b" | awk -F= '/^npub=/{print $2}')"
relay_addr="ws://172.31.254.30:7777"
stun_addr="stun:172.31.254.40:3478"
# The two lab bridges. ci-local.sh claims a free /24 for each per run and
# exports these; unset renders the addresses the lab has always used, so a
# bare invocation and the GitHub matrix are unaffected.
wan="${NAT_WAN_PREFIX:-172.31.254}"
lan="${NAT_LAN_PREFIX:-172.31.10}"
relay_addr="ws://${wan}.30:7777"
stun_addr="stun:${wan}.40:3478"
if [ "$SCENARIO" = "lan" ] || [ "$SCENARIO" = "nostr-publish-consume" ] \
|| [ "$SCENARIO" = "stun-faults" ]; then
relay_addr="ws://172.31.10.30:7777"
stun_addr="stun:172.31.10.40:3478"
relay_addr="ws://${lan}.30:7777"
stun_addr="stun:${lan}.40:3478"
fi
peer_block_a=$(cat <<EOF
@@ -58,10 +69,10 @@ EOF
if [ "$SCENARIO" = "symmetric" ]; then
peer_block_a="$peer_block_a"$'\n'" - transport: tcp
addr: \"172.31.254.11:8443\"
addr: \"${wan}.11:8443\"
priority: 20"
peer_block_b="$peer_block_b"$'\n'" - transport: tcp
addr: \"172.31.254.10:8443\"
addr: \"${wan}.10:8443\"
priority: 20"
fi
+48 -28
View File
@@ -9,6 +9,19 @@ BUILD_SCRIPT="$ROOT_DIR/testing/scripts/build.sh"
GENERATE_SCRIPT="$SCRIPT_DIR/generate-configs.sh"
TOPOLOGY_SCRIPT="$SCRIPT_DIR/setup-topology.sh"
WAIT_LIB="$ROOT_DIR/testing/lib/wait-converge.sh"
# Must track generate-configs.sh's OUTPUT_DIR and the compose bind-mounts: the
# npubs are read back here after the containers are up, so reading a different
# directory than the one the generator wrote pings an npub no node owns.
CONFIG_DIR="$NAT_DIR/generated-configs${FIPS_CI_NAME_SUFFIX:-}"
# The two lab bridges. ci-local.sh claims a free /24 for each per run and
# exports these; unset renders the addresses the lab has always used, so the
# GitHub matrix, mesh-lab/run-loop.sh and a bare run are unaffected. The
# router-side LANs (172.31.1.x / 172.31.2.x) are deliberately NOT parameterized:
# they live inside per-container network namespaces, never become docker
# networks, and so cannot collide across runs.
NAT_WAN="${NAT_WAN_PREFIX:-172.31.254}"
NAT_LAN="${NAT_LAN_PREFIX:-172.31.10}"
SCENARIO="${1:-all}"
COMPOSE=(docker compose -f "$NAT_DIR/docker-compose.yml")
@@ -87,9 +100,9 @@ PY
dump_fips_state() {
local container="$1"
local relay_host="${2:-172.31.254.30}"
local relay_host="${2:-${NAT_WAN}.30}"
local relay_port="${3:-7777}"
local stun_host="${4:-172.31.254.40}"
local stun_host="${4:-${NAT_WAN}.40}"
local stun_port="${5:-3478}"
dump_container_state "$container"
echo ""
@@ -114,7 +127,7 @@ dump_fips_state() {
dump_node_udp_probe() {
local node="$1"
local stun_host="${2:-172.31.254.40}"
local stun_host="${2:-${NAT_WAN}.40}"
local stun_port="${3:-3478}"
echo ""
@@ -122,7 +135,12 @@ dump_node_udp_probe() {
docker exec "$node" sh -lc 'ss -H -uanp 2>/dev/null || ss -H -uan 2>/dev/null || netstat -anu 2>/dev/null' 2>&1 || true
echo ""
echo "--- $node: UDP routes to STUN and peer WANs ---"
docker exec "$node" sh -lc 'for ip in 172.31.254.40 172.31.254.10 172.31.254.11; do ip route get "$ip"; done' 2>&1 || true
# Double-quoted so THIS shell expands NAT_WAN; the loop variable is escaped
# so the container's shell still expands that one. Left single-quoted, the
# literal string ${NAT_WAN}.40 would reach `ip route get` and the probe
# would stop probing without anything going red — these are diagnostic
# paths, so the loss would only surface during a failure investigation.
docker exec "$node" sh -lc "for ip in ${NAT_WAN}.40 ${NAT_WAN}.10 ${NAT_WAN}.11; do ip route get \"\$ip\"; done" 2>&1 || true
local capture_file
capture_file="$(mktemp)"
@@ -151,7 +169,7 @@ dump_node_udp_probe() {
dump_router_udp_probe() {
local router="$1"
local source_node="$2"
local stun_host="${3:-172.31.254.40}"
local stun_host="${3:-${NAT_WAN}.40}"
local stun_port="${4:-3478}"
echo ""
@@ -163,7 +181,9 @@ dump_router_udp_probe() {
docker exec "$router" sh -lc 'iptables -vnL FORWARD; echo; iptables -t nat -vnL POSTROUTING' 2>&1 || true
echo ""
echo "--- $router: UDP routes to STUN and peer WANs ---"
docker exec "$router" sh -lc 'for ip in 172.31.254.40 172.31.254.10 172.31.254.11; do ip route get "$ip"; done' 2>&1 || true
# See the note in dump_node_udp_probe: outer shell expands NAT_WAN, inner
# shell expands the loop variable.
docker exec "$router" sh -lc "for ip in ${NAT_WAN}.40 ${NAT_WAN}.10 ${NAT_WAN}.11; do ip route get \"\$ip\"; done" 2>&1 || true
local capture_file
capture_file="$(mktemp)"
@@ -195,7 +215,7 @@ dump_router_udp_probe() {
dump_stun_udp_probe() {
local source_node="$1"
local stun_host="${2:-172.31.254.40}"
local stun_host="${2:-${NAT_WAN}.40}"
local stun_port="${3:-3478}"
local helper_image
helper_image="$(helper_tcpdump_image)"
@@ -225,9 +245,9 @@ dump_stun_udp_probe() {
dump_cone_diagnostics() {
echo ""
echo "=== cone diagnostics ==="
dump_fips_state fips-nat-cone-a${FIPS_CI_NAME_SUFFIX:-} 172.31.254.30 7777 172.31.254.40 3478
dump_fips_state fips-nat-cone-a${FIPS_CI_NAME_SUFFIX:-} ${NAT_WAN}.30 7777 ${NAT_WAN}.40 3478
dump_node_udp_probe fips-nat-cone-a${FIPS_CI_NAME_SUFFIX:-}
dump_fips_state fips-nat-cone-b${FIPS_CI_NAME_SUFFIX:-} 172.31.254.30 7777 172.31.254.40 3478
dump_fips_state fips-nat-cone-b${FIPS_CI_NAME_SUFFIX:-} ${NAT_WAN}.30 7777 ${NAT_WAN}.40 3478
dump_node_udp_probe fips-nat-cone-b${FIPS_CI_NAME_SUFFIX:-}
dump_container_state fips-nat-router-a${FIPS_CI_NAME_SUFFIX:-}
dump_router_udp_probe fips-nat-router-a${FIPS_CI_NAME_SUFFIX:-} fips-nat-cone-a${FIPS_CI_NAME_SUFFIX:-}
@@ -242,8 +262,8 @@ dump_cone_diagnostics() {
dump_symmetric_diagnostics() {
echo ""
echo "=== symmetric diagnostics ==="
dump_fips_state fips-nat-symmetric-a${FIPS_CI_NAME_SUFFIX:-} 172.31.254.30 7777 172.31.254.40 3478
dump_fips_state fips-nat-symmetric-b${FIPS_CI_NAME_SUFFIX:-} 172.31.254.30 7777 172.31.254.40 3478
dump_fips_state fips-nat-symmetric-a${FIPS_CI_NAME_SUFFIX:-} ${NAT_WAN}.30 7777 ${NAT_WAN}.40 3478
dump_fips_state fips-nat-symmetric-b${FIPS_CI_NAME_SUFFIX:-} ${NAT_WAN}.30 7777 ${NAT_WAN}.40 3478
dump_container_state fips-nat-router-a${FIPS_CI_NAME_SUFFIX:-}
dump_container_state fips-nat-router-b${FIPS_CI_NAME_SUFFIX:-}
dump_container_state fips-nat-relay${FIPS_CI_NAME_SUFFIX:-}
@@ -253,8 +273,8 @@ dump_symmetric_diagnostics() {
dump_lan_diagnostics() {
echo ""
echo "=== lan diagnostics ==="
dump_fips_state fips-nat-lan-a${FIPS_CI_NAME_SUFFIX:-} 172.31.10.30 7777 172.31.10.40 3478
dump_fips_state fips-nat-lan-b${FIPS_CI_NAME_SUFFIX:-} 172.31.10.30 7777 172.31.10.40 3478
dump_fips_state fips-nat-lan-a${FIPS_CI_NAME_SUFFIX:-} ${NAT_LAN}.30 7777 ${NAT_LAN}.40 3478
dump_fips_state fips-nat-lan-b${FIPS_CI_NAME_SUFFIX:-} ${NAT_LAN}.30 7777 ${NAT_LAN}.40 3478
dump_container_state fips-nat-relay${FIPS_CI_NAME_SUFFIX:-}
dump_container_state fips-nat-stun${FIPS_CI_NAME_SUFFIX:-}
}
@@ -353,12 +373,12 @@ run_cone() {
dump_cone_diagnostics
return 1
}
assert_peer_path fips-nat-cone-a${FIPS_CI_NAME_SUFFIX:-} udp 172.31.254.
assert_peer_path fips-nat-cone-b${FIPS_CI_NAME_SUFFIX:-} udp 172.31.254.
assert_link_path fips-nat-cone-a${FIPS_CI_NAME_SUFFIX:-} 172.31.254.
assert_link_path fips-nat-cone-b${FIPS_CI_NAME_SUFFIX:-} 172.31.254.
assert_peer_path fips-nat-cone-a${FIPS_CI_NAME_SUFFIX:-} udp ${NAT_WAN}.
assert_peer_path fips-nat-cone-b${FIPS_CI_NAME_SUFFIX:-} udp ${NAT_WAN}.
assert_link_path fips-nat-cone-a${FIPS_CI_NAME_SUFFIX:-} ${NAT_WAN}.
assert_link_path fips-nat-cone-b${FIPS_CI_NAME_SUFFIX:-} ${NAT_WAN}.
# shellcheck disable=SC1090
source "$NAT_DIR/generated-configs/cone/npubs.env"
source "$CONFIG_DIR/cone/npubs.env"
ping_peer fips-nat-cone-a${FIPS_CI_NAME_SUFFIX:-} "$NPUB_B"
ping_peer fips-nat-cone-b${FIPS_CI_NAME_SUFFIX:-} "$NPUB_A"
cleanup
@@ -378,14 +398,14 @@ run_symmetric() {
dump_symmetric_diagnostics
return 1
}
assert_peer_path fips-nat-symmetric-a${FIPS_CI_NAME_SUFFIX:-} tcp 172.31.254.11:
assert_peer_path fips-nat-symmetric-b${FIPS_CI_NAME_SUFFIX:-} tcp 172.31.254.10:
assert_link_path fips-nat-symmetric-a${FIPS_CI_NAME_SUFFIX:-} 172.31.254.11:
assert_link_path fips-nat-symmetric-b${FIPS_CI_NAME_SUFFIX:-} 172.31.254.10:
assert_peer_path fips-nat-symmetric-a${FIPS_CI_NAME_SUFFIX:-} tcp ${NAT_WAN}.11:
assert_peer_path fips-nat-symmetric-b${FIPS_CI_NAME_SUFFIX:-} tcp ${NAT_WAN}.10:
assert_link_path fips-nat-symmetric-a${FIPS_CI_NAME_SUFFIX:-} ${NAT_WAN}.11:
assert_link_path fips-nat-symmetric-b${FIPS_CI_NAME_SUFFIX:-} ${NAT_WAN}.10:
require_bootstrap_activity fips-nat-symmetric-a${FIPS_CI_NAME_SUFFIX:-}
require_bootstrap_activity fips-nat-symmetric-b${FIPS_CI_NAME_SUFFIX:-}
# shellcheck disable=SC1090
source "$NAT_DIR/generated-configs/symmetric/npubs.env"
source "$CONFIG_DIR/symmetric/npubs.env"
ping_peer fips-nat-symmetric-a${FIPS_CI_NAME_SUFFIX:-} "$NPUB_B"
ping_peer fips-nat-symmetric-b${FIPS_CI_NAME_SUFFIX:-} "$NPUB_A"
cleanup
@@ -404,12 +424,12 @@ run_lan() {
dump_lan_diagnostics
return 1
}
assert_peer_path fips-nat-lan-a${FIPS_CI_NAME_SUFFIX:-} udp 172.31.10.
assert_peer_path fips-nat-lan-b${FIPS_CI_NAME_SUFFIX:-} udp 172.31.10.
assert_link_path fips-nat-lan-a${FIPS_CI_NAME_SUFFIX:-} 172.31.10.
assert_link_path fips-nat-lan-b${FIPS_CI_NAME_SUFFIX:-} 172.31.10.
assert_peer_path fips-nat-lan-a${FIPS_CI_NAME_SUFFIX:-} udp ${NAT_LAN}.
assert_peer_path fips-nat-lan-b${FIPS_CI_NAME_SUFFIX:-} udp ${NAT_LAN}.
assert_link_path fips-nat-lan-a${FIPS_CI_NAME_SUFFIX:-} ${NAT_LAN}.
assert_link_path fips-nat-lan-b${FIPS_CI_NAME_SUFFIX:-} ${NAT_LAN}.
# shellcheck disable=SC1090
source "$NAT_DIR/generated-configs/lan/npubs.env"
source "$CONFIG_DIR/lan/npubs.env"
ping_peer fips-nat-lan-a${FIPS_CI_NAME_SUFFIX:-} "$NPUB_B"
ping_peer fips-nat-lan-b${FIPS_CI_NAME_SUFFIX:-} "$NPUB_A"
# Skip the final teardown when the mesh-lab harness wraps this
+21 -2
View File
@@ -21,13 +21,32 @@ ROOT_DIR="$(cd "$NAT_DIR/../.." && pwd)"
BUILD_SCRIPT="$ROOT_DIR/testing/scripts/build.sh"
GENERATE_SCRIPT="$SCRIPT_DIR/generate-configs.sh"
WAIT_LIB="$ROOT_DIR/testing/lib/wait-converge.sh"
# Must track generate-configs.sh's OUTPUT_DIR and the compose bind-mounts.
CONFIG_DIR="$NAT_DIR/generated-configs${FIPS_CI_NAME_SUFFIX:-}"
PROFILE="nostr-publish-consume"
SCENARIO="$PROFILE"
COMPOSE=(docker compose -f "$NAT_DIR/docker-compose.yml")
# Optional extra compose-file overlay chain (colon-separated paths), matching
# nat-test.sh. ci-local.sh appends testing/nat/docker-compose.external-net.yml
# here so this suite attaches to the networks the run already claimed; without
# the hook compose would create its own from the base file's subnet and collide
# with the run's own claim. Paths are relative to ROOT_DIR unless absolute.
if [ -n "${FIPS_NAT_EXTRA_COMPOSE:-}" ]; then
IFS=':' read -ra _NAT_EXTRA <<< "${FIPS_NAT_EXTRA_COMPOSE}"
for _f in "${_NAT_EXTRA[@]}"; do
case "$_f" in
/*) COMPOSE+=(-f "$_f") ;;
*) COMPOSE+=(-f "$ROOT_DIR/$_f") ;;
esac
done
fi
NODE_A="fips-nat-nostr-pub-a${FIPS_CI_NAME_SUFFIX:-}"
NODE_B="fips-nat-nostr-pub-b${FIPS_CI_NAME_SUFFIX:-}"
RELAY_HOST="172.31.10.30"
# Claimed per run by ci-local.sh; unset renders the lab's historical address.
RELAY_HOST="${NAT_LAN_PREFIX:-172.31.10}.30"
RELAY_PORT=7777
RELAY_CONTAINER="fips-nat-relay${FIPS_CI_NAME_SUFFIX:-}"
@@ -342,7 +361,7 @@ run_test() {
fi
# shellcheck disable=SC1090
source "$NAT_DIR/generated-configs/$SCENARIO/npubs.env"
source "$CONFIG_DIR/$SCENARIO/npubs.env"
echo " NPUB_A=$NPUB_A"
echo " NPUB_B=$NPUB_B"
+30 -3
View File
@@ -4,6 +4,7 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
NAT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
ROOT_DIR="$(cd "$NAT_DIR/../.." && pwd)"
SCENARIO="${1:?usage: setup-topology.sh <cone|symmetric>}"
@@ -25,6 +26,29 @@ esac
router_a="fips-nat-router-a${FIPS_CI_NAME_SUFFIX:-}"
router_b="fips-nat-router-b${FIPS_CI_NAME_SUFFIX:-}"
# Host interface names live in one global namespace and are capped at
# IFNAMSIZ-1 = 15 characters — far short of the run suffix, which is 20-plus at
# the default run-id length. Two concurrent runs both creating `vna0` damage
# each other: setup_pair deletes, adds and moves the interface in three
# separate `docker run`s, so B's re-add inside A's window sends B's interface
# into A's namespace and fails B's own move.
#
# Scope the names with the same four-hex token the chaos simulation uses, and
# derive it by calling sim.naming rather than re-implementing sha1 here, so
# ci-cleanup.sh's reaper (which calls the same module) cannot end up matching a
# different width. Empty suffix yields an empty token and today's exact names.
#
# No fallback on a derivation failure: silently reverting to `vna0` would
# reinstate the collision this exists to remove, so let it fail loudly.
veth_token() {
local suffix="${FIPS_CI_NAME_SUFFIX:-}"
if [ -z "$suffix" ]; then
echo ""
return 0
fi
PYTHONPATH="$ROOT_DIR/testing/chaos" python3 -m sim.naming "$suffix"
}
helper_image() {
if [ -n "${IP_HELPER_IMAGE:-}" ]; then
echo "$IP_HELPER_IMAGE"
@@ -122,11 +146,14 @@ setup_pair() {
main() {
cd "$NAT_DIR"
local image
local image token
image="$(helper_image)"
token="$(veth_token)"
setup_pair "$image" "$node_a" "$router_a" vna0 vna1 172.31.1.10/24 172.31.1.254/24
setup_pair "$image" "$node_b" "$router_b" vnb0 vnb1 172.31.2.10/24 172.31.2.254/24
setup_pair "$image" "$node_a" "$router_a" \
"vna${token}0" "vna${token}1" 172.31.1.10/24 172.31.1.254/24
setup_pair "$image" "$node_b" "$router_b" \
"vnb${token}0" "vnb${token}1" 172.31.2.10/24 172.31.2.254/24
}
main "$@"
+18 -1
View File
@@ -33,11 +33,28 @@ GENERATE_SCRIPT="$SCRIPT_DIR/generate-configs.sh"
PROFILE="stun-faults"
SCENARIO="$PROFILE"
COMPOSE=(docker compose -f "$NAT_DIR/docker-compose.yml")
# Optional extra compose-file overlay chain (colon-separated paths), matching
# nat-test.sh. ci-local.sh appends testing/nat/docker-compose.external-net.yml
# here so this suite attaches to the networks the run already claimed; without
# the hook compose would create its own from the base file's subnet and collide
# with the run's own claim. Paths are relative to ROOT_DIR unless absolute.
if [ -n "${FIPS_NAT_EXTRA_COMPOSE:-}" ]; then
IFS=':' read -ra _NAT_EXTRA <<< "${FIPS_NAT_EXTRA_COMPOSE}"
for _f in "${_NAT_EXTRA[@]}"; do
case "$_f" in
/*) COMPOSE+=(-f "$_f") ;;
*) COMPOSE+=(-f "$ROOT_DIR/$_f") ;;
esac
done
fi
NODE="fips-nat-stun-fault-node${FIPS_CI_NAME_SUFFIX:-}"
PEER="fips-nat-stun-fault-peer${FIPS_CI_NAME_SUFFIX:-}"
SHIM="fips-nat-stun-fault-shim${FIPS_CI_NAME_SUFFIX:-}"
STUN_CONTAINER="fips-nat-stun${FIPS_CI_NAME_SUFFIX:-}"
STUN_HOST="172.31.10.40"
# Claimed per run by ci-local.sh; unset renders the lab's historical address.
STUN_HOST="${NAT_LAN_PREFIX:-172.31.10}.40"
STUN_PORT=3478
DEV="eth0"