mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-05 14:14:39 +00:00
NodeProfile enum (Full/NonRouting/Leaf) with FMP feature bitfield: bits 0-2 profile, bits 3-6 MMP wants/provides, bit 7 bloom filter size negotiable. Bloom size TLV always sent with min=max=1KB. Config mapping: leaf_only -> Leaf, disable_routing -> NonRouting. Profile and agreed bloom size stored on PeerConnection and ActivePeer. Handshake msg2/msg3 carry FMP negotiation payload with profile validation and bloom size agreement. MMP report sending gated by profile wants/provides. Parent selection and routing constraints skip non-full peers. One-way bloom filters for non-routing peers (F inserts N as dependent). Leaf mode: single-peer enforcement, suppress tree announces and discovery forwarding. Mixed-profile integration test: A(Full) + B(Full) + C(NonRouting) + D(Leaf) with 9 connectivity assertions.
170 lines
5.1 KiB
Bash
Executable File
170 lines
5.1 KiB
Bash
Executable File
#!/bin/bash
|
|
# Mixed-profile integration test: Full, NonRouting, and Leaf nodes.
|
|
#
|
|
# Topology:
|
|
# A (Full) ─── B (Full)
|
|
# │ \ │
|
|
# │ \ │
|
|
# D (Leaf) C (NonRouting)
|
|
#
|
|
# Usage:
|
|
# ./mixed-profile-test.sh inject-config Inject profile config overrides
|
|
# ./mixed-profile-test.sh Run the full test
|
|
#
|
|
# inject-config is run separately by CI after generate-configs.sh and
|
|
# before building Docker images.
|
|
|
|
set -e
|
|
trap 'echo ""; echo "Test interrupted"; exit 130' INT
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
TOPOLOGY="mixed-profile"
|
|
NODES="a b c d"
|
|
|
|
# ── inject-config subcommand ──────────────────────────────────────────
|
|
# Inject profile overrides into generated node configs.
|
|
if [ "${1:-}" = "inject-config" ]; then
|
|
echo "Injecting mixed-profile config overrides..."
|
|
|
|
# Node C: non-routing
|
|
cfg="$SCRIPT_DIR/../generated-configs/$TOPOLOGY/node-c.yaml"
|
|
if [ ! -f "$cfg" ]; then
|
|
echo " Error: $cfg not found" >&2
|
|
exit 1
|
|
fi
|
|
python3 -c "
|
|
import yaml
|
|
with open('$cfg') as f:
|
|
cfg = yaml.safe_load(f)
|
|
cfg.setdefault('node', {})['disable_routing'] = True
|
|
with open('$cfg', 'w') as f:
|
|
yaml.dump(cfg, f, default_flow_style=False, sort_keys=False)
|
|
"
|
|
echo " ✓ node-c (disable_routing: true)"
|
|
|
|
# Node D: leaf
|
|
cfg="$SCRIPT_DIR/../generated-configs/$TOPOLOGY/node-d.yaml"
|
|
if [ ! -f "$cfg" ]; then
|
|
echo " Error: $cfg not found" >&2
|
|
exit 1
|
|
fi
|
|
python3 -c "
|
|
import yaml
|
|
with open('$cfg') as f:
|
|
cfg = yaml.safe_load(f)
|
|
cfg.setdefault('node', {})['leaf_only'] = True
|
|
with open('$cfg', 'w') as f:
|
|
yaml.dump(cfg, f, default_flow_style=False, sort_keys=False)
|
|
"
|
|
echo " ✓ node-d (leaf_only: true)"
|
|
|
|
echo "✓ Config injection complete"
|
|
exit 0
|
|
fi
|
|
|
|
# ── Full test ─────────────────────────────────────────────────────────
|
|
source "$SCRIPT_DIR/../../lib/wait-converge.sh"
|
|
ENV_FILE="$SCRIPT_DIR/../generated-configs/npubs.env"
|
|
if [ ! -f "$ENV_FILE" ]; then
|
|
echo "Error: $ENV_FILE not found. Run generate-configs.sh first." >&2
|
|
exit 1
|
|
fi
|
|
source "$ENV_FILE"
|
|
|
|
PASSED=0
|
|
FAILED=0
|
|
|
|
check() {
|
|
local desc="$1"
|
|
shift
|
|
echo -n " $desc ... "
|
|
if "$@" >/dev/null 2>&1; then
|
|
echo "PASS"
|
|
PASSED=$((PASSED + 1))
|
|
else
|
|
echo "FAIL"
|
|
FAILED=$((FAILED + 1))
|
|
fi
|
|
}
|
|
|
|
ping_fips() {
|
|
local from="$1"
|
|
local to_npub="$2"
|
|
docker exec "fips-$from" ping6 -c 1 -W 5 "${to_npub}.fips"
|
|
}
|
|
|
|
echo "=== Mixed-Profile Integration Test ==="
|
|
echo ""
|
|
|
|
# Phase 1: Wait for link convergence
|
|
echo "Phase 1: Link convergence"
|
|
# A: peers with B, C, D → 3 links
|
|
# B: peers with A, C → 2 links
|
|
# C (NonRouting): peers with A, B → 2 links
|
|
# D (Leaf): peers with A → 1 link
|
|
wait_for_peers fips-node-a 3 30 || true
|
|
wait_for_peers fips-node-b 2 30 || true
|
|
wait_for_peers fips-node-c 2 30 || true
|
|
wait_for_peers fips-node-d 1 30 || true
|
|
|
|
# Phase 2: Verify link counts (already validated by wait_for_peers above)
|
|
echo ""
|
|
echo "Phase 2: Link counts verified via convergence wait"
|
|
|
|
# Phase 3: Wait for discovery/session convergence
|
|
echo ""
|
|
echo "Phase 3: Waiting for session convergence (up to 45s)..."
|
|
# Try all pairs repeatedly until they all work
|
|
CONV_START=$SECONDS
|
|
CONV_TIMEOUT=45
|
|
ALL_OK=false
|
|
while (( SECONDS - CONV_START < CONV_TIMEOUT )); do
|
|
ALL_GOOD=true
|
|
for pair in "node-a:$NPUB_B" "node-a:$NPUB_C" "node-a:$NPUB_D" \
|
|
"node-b:$NPUB_A" "node-b:$NPUB_C" \
|
|
"node-c:$NPUB_A" "node-c:$NPUB_B" \
|
|
"node-d:$NPUB_A" "node-d:$NPUB_B"; do
|
|
from="${pair%%:*}"
|
|
to="${pair##*:}"
|
|
if ! docker exec "fips-$from" ping6 -c 1 -W 1 "${to}.fips" >/dev/null 2>&1; then
|
|
ALL_GOOD=false
|
|
break
|
|
fi
|
|
done
|
|
if $ALL_GOOD; then
|
|
echo " All pairs reachable after $((SECONDS - CONV_START))s"
|
|
ALL_OK=true
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
if ! $ALL_OK; then
|
|
echo " WARNING: Not all pairs converged within ${CONV_TIMEOUT}s (continuing with tests)"
|
|
fi
|
|
|
|
# Phase 4: Connectivity tests
|
|
echo ""
|
|
echo "Phase 4: F↔F connectivity"
|
|
check "A → B (Full → Full, direct)" ping_fips node-a "$NPUB_B"
|
|
check "B → A (Full → Full, direct)" ping_fips node-b "$NPUB_A"
|
|
|
|
echo ""
|
|
echo "Phase 5: F↔N connectivity"
|
|
check "A → C (Full → NonRouting, direct)" ping_fips node-a "$NPUB_C"
|
|
check "C → A (NonRouting → Full, direct)" ping_fips node-c "$NPUB_A"
|
|
check "B → C (Full → NonRouting, direct)" ping_fips node-b "$NPUB_C"
|
|
check "C → B (NonRouting → Full, direct)" ping_fips node-c "$NPUB_B"
|
|
|
|
echo ""
|
|
echo "Phase 6: F↔L connectivity"
|
|
check "A → D (Full → Leaf, direct)" ping_fips node-a "$NPUB_D"
|
|
check "D → A (Leaf → Full, direct)" ping_fips node-d "$NPUB_A"
|
|
|
|
echo ""
|
|
echo "Phase 7: Multi-hop through Full nodes"
|
|
check "D → B (Leaf → Full, via A)" ping_fips node-d "$NPUB_B"
|
|
|
|
echo ""
|
|
echo "=== Results: $PASSED passed, $FAILED failed ==="
|
|
[ "$FAILED" -eq 0 ] && exit 0 || exit 1
|