Skip to content

test(e2e): NatProfile::PortRestrictedConeAutoNATOk variant - #52

Merged
pvg13 merged 1 commit into
mainfrom
feat/nat-autonat-permissive
May 7, 2026
Merged

test(e2e): NatProfile::PortRestrictedConeAutoNATOk variant#52
pvg13 merged 1 commit into
mainfrom
feat/nat-autonat-permissive

Conversation

@pvg13

@pvg13 pvg13 commented May 7, 2026

Copy link
Copy Markdown
Owner

Summary

Adds an AutoNAT-permissive NAT shape so libp2p's reachability-verification protocol can succeed inside the harness — closes the immediate "DCUtR doesn't fire because AutoNAT is blocked" finding from #50, surfaces a deeper finding that's now filed as #51.

Stacks on top of #50 (NAT-topology shapes); merge that first.

What's new

  • `NatProfile::PortRestrictedConeAutoNATOk` — same as `PortRestrictedCone` but whitelists the AutoNAT server's bridge IP for unsolicited inbound. The relay also acts as the AutoNAT server in our setup (via `autonat::v2::server`), so the harness automatically passes the relay's bridge IP through.
  • Harness resolves the relay's bridge IP via `ContainerAsync::get_bridge_ip_address()` after starting the relay, threads it through `apply_nat_to_container`.
  • `dcutr_validation_cellular_fair` updated to use the new variant.

Empirical findings

Under `cellular_fair + PortRestrictedConeAutoNATOk`:

  • ✅ Whitelist applied (`[harness] PortRestrictedConeAutoNATOk: whitelisting relay bridge IP 172.21.0.2`).
  • ✅ AutoNAT now reports the peer's address as reachable (was "NOT reachable" under plain `PortRestrictedCone`). Engine sees Public NAT.
  • ✅ Sync works end-to-end.
  • ❌ But `dcutr_upgrades_attempted = 0` still.

Root cause traced: the relay introduces peers via `PeerList` / `PeerJoined` near-simultaneously, both peers fire dial-outs immediately, each side's outbound creates a conntrack entry, the other's inbound matches as ESTABLISHED — peers form a direct connection naturally without DCUtR's coordination.

This is realistic NAT behaviour (it's why hole-punching works on most home networks), but it's not useful for exercising DCUtR specifically. Forcing DCUtR requires a symmetric-NAT shape that rotates source ports per-destination so simultaneous dial can't catch the same flow tuple. Filed as #51.

What lands here, what doesn't

✅ `PortRestrictedConeAutoNATOk` is useful infrastructure independent of the DCUtR question — any future bench that wants AutoNAT-verified peers behind realistic NAT can use it.

✅ The harness now correctly resolves and passes through container IPs at NAT-rule application time.

❌ DCUtR end-to-end validation still pending — needs #51 (symmetric NAT). The acceptance criterion 3 of #40 ("partition-recovery drops 30% on cellular_fair via DCUtR") therefore remains open until #51 lands.

Test plan

  • `cargo build -p wavesyncdb-e2e --tests`
  • `./tests-e2e/build-images.sh`
  • `cargo test -p wavesyncdb-e2e --test dcutr_validation -- --ignored --nocapture` runs cleanly; output documents the new finding.
  • AutoNAT-reachable status confirmed via container logs during validation runs.

Related

Adds an AutoNAT-permissive variant of the NAT shape so libp2p's
reachability-verification protocol can succeed inside the harness:

- New `NatProfile::PortRestrictedConeAutoNATOk` whitelists the
  AutoNAT server's bridge IP (the relay container, which also runs
  `autonat::v2::server`) for unsolicited inbound, while keeping the
  conntrack-based filter for arbitrary peer-to-peer unsolicited
  inbound.
- The harness resolves the relay's bridge IP via
  `ContainerAsync::get_bridge_ip_address()` after starting the
  relay, and threads it through `apply_nat_to_container`.
- DCUtR validation bench updated to use the new variant.

## Verified empirically

Under `cellular_fair + PortRestrictedConeAutoNATOk`:

- Whitelist applied correctly (harness logs the IP).
- AutoNAT now reports the peer address as **reachable** (it was
  "NOT reachable" with plain PortRestrictedCone). Engine sees its
  NAT status as Public, so it advertises direct addresses.
- Sync still works.

But `dcutr_upgrades_attempted = 0`. Root cause traced: the relay
introduces peers via `PeerList`/`PeerJoined` near-simultaneously,
and both peers fire dial-outs immediately. Each side's outbound
creates a conntrack entry; the other side's inbound packet then
matches as ESTABLISHED — peers form a direct connection without
DCUtR's coordination ever being needed.

This is the right behaviour for a port-restricted cone NAT in real
life (it's why hole-punching works on most home networks). It's
just not a useful shape for *exercising* DCUtR specifically. To
require DCUtR coordination, the bench needs a symmetric-NAT shape
where source ports are rotated per-destination so simultaneous-
dial doesn't catch the same flow tuple. Filed as #51.

The `PortRestrictedConeAutoNATOk` variant is still useful
infrastructure independent of the DCUtR question — it's the shape
needed for any future bench that wants AutoNAT-verified peers
behind realistic NAT.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@pvg13
pvg13 force-pushed the feat/nat-autonat-permissive branch from 1113b8f to f47c42b Compare May 7, 2026 21:35
@pvg13
pvg13 merged commit 6033d4e into main May 7, 2026
1 check passed
@pvg13
pvg13 deleted the feat/nat-autonat-permissive branch May 7, 2026 21:40
pvg13 added a commit that referenced this pull request May 12, 2026
Adds an AutoNAT-permissive variant of the NAT shape so libp2p's
reachability-verification protocol can succeed inside the harness:

- New `NatProfile::PortRestrictedConeAutoNATOk` whitelists the
  AutoNAT server's bridge IP (the relay container, which also runs
  `autonat::v2::server`) for unsolicited inbound, while keeping the
  conntrack-based filter for arbitrary peer-to-peer unsolicited
  inbound.
- The harness resolves the relay's bridge IP via
  `ContainerAsync::get_bridge_ip_address()` after starting the
  relay, and threads it through `apply_nat_to_container`.
- DCUtR validation bench updated to use the new variant.

## Verified empirically

Under `cellular_fair + PortRestrictedConeAutoNATOk`:

- Whitelist applied correctly (harness logs the IP).
- AutoNAT now reports the peer address as **reachable** (it was
  "NOT reachable" with plain PortRestrictedCone). Engine sees its
  NAT status as Public, so it advertises direct addresses.
- Sync still works.

But `dcutr_upgrades_attempted = 0`. Root cause traced: the relay
introduces peers via `PeerList`/`PeerJoined` near-simultaneously,
and both peers fire dial-outs immediately. Each side's outbound
creates a conntrack entry; the other side's inbound packet then
matches as ESTABLISHED — peers form a direct connection without
DCUtR's coordination ever being needed.

This is the right behaviour for a port-restricted cone NAT in real
life (it's why hole-punching works on most home networks). It's
just not a useful shape for *exercising* DCUtR specifically. To
require DCUtR coordination, the bench needs a symmetric-NAT shape
where source ports are rotated per-destination so simultaneous-
dial doesn't catch the same flow tuple. Filed as #51.

The `PortRestrictedConeAutoNATOk` variant is still useful
infrastructure independent of the DCUtR question — it's the shape
needed for any future bench that wants AutoNAT-verified peers
behind realistic NAT.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant