Provide tests for nat

This commit is contained in:
Arnaud 2026-05-12 10:06:23 +04:00
parent e8335c14eb
commit 0ce3139a82
No known key found for this signature in database
GPG Key ID: A6C7C781817146FA
3 changed files with 196 additions and 6 deletions

View File

@ -1,5 +1,6 @@
import std/json
import std/options
import std/sequtils
import pkg/chronos
import pkg/questionable/results
@ -7,7 +8,13 @@ import ../multinodes
import ../storageclient
import ../storageconfig
multinodesuite "AutoNAT integration":
const
DetectionTimeout = 15_000
RelayTimeout = 30_000
PollInterval = 1_000
# Reminder: multinodesuite setup the first node as bootstrap node
multinodesuite "AutoNAT detection":
let natConfig = NodeConfigs(
clients: StorageConfigs
.init(nodes = 2)
@ -19,14 +26,145 @@ multinodesuite "AutoNAT integration":
# .withLogLevel("DEBUG")
.some
)
# Reminder: multinodesuite setup the first node as bootstrap node
test "node is reachable when using bootstrap node on same network", natConfig:
let node1 = clients()[0]
let node2 = clients()[1]
check eventuallySafe(
(await node2.client.natReachability()).get() == "Reachable",
timeout = 30_000,
pollInterval = 500,
timeout = RelayTimeout,
pollInterval = PollInterval,
)
check eventuallySafe(
not (await node2.client.natRelayRunning()).get(),
timeout = RelayTimeout,
pollInterval = PollInterval,
)
let autonatConfig = NodeConfigs(
clients: StorageConfigs
.init(nodes = 2)
.withRelay(idx = 0)
.withNatSimulation(idx = 1)
.withNatNumPeersToAsk(1)
.withNatMinConfidence(0.5)
.withNatScheduleInterval(10.seconds)
.withNatMaxQueueSize(1)
# .withLogLevel("DEBUG")
# .debug()
# .withLogFile()
.some
)
# node2 is behind simulated NAT: AutoNAT peers try to dial back but are blocked.
test "nat node is detected as not reachable and starts relay", autonatConfig:
let node2 = clients()[1]
check eventuallySafe(
(await node2.client.natReachability()).get() == "NotReachable",
timeout = DetectionTimeout,
pollInterval = PollInterval,
)
check eventuallySafe(
(await node2.client.natRelayRunning()).get(),
timeout = RelayTimeout,
pollInterval = PollInterval,
)
check eventuallySafe(
block:
let addrs = (await node2.client.info()).get["addrs"].getElems.mapIt(it.getStr)
addrs.anyIt("p2p-circuit" in it),
timeout = RelayTimeout,
pollInterval = PollInterval,
)
let transitionConfig = NodeConfigs(
clients: StorageConfigs
.init(nodes = 2)
.withRelay(idx = 0)
.withNatSimulation(idx = 1)
.withNatNumPeersToAsk(1)
.withNatMinConfidence(0.5)
.withNatScheduleInterval(5.seconds)
.withNatMaxQueueSize(1).some
)
# node2 starts behind simulated NAT (NotReachable + relay), then NAT is lifted
# and AutoNAT detects Reachable on the next scheduled check.
test "nat node recovers to reachable when nat is lifted", transitionConfig:
let node2 = clients()[1]
check eventuallySafe(
(await node2.client.natReachability()).get() == "NotReachable",
timeout = DetectionTimeout,
pollInterval = PollInterval,
)
check eventuallySafe(
(await node2.client.natRelayRunning()).get(),
timeout = RelayTimeout,
pollInterval = PollInterval,
)
check eventuallySafe(
block:
let addrs = (await node2.client.info()).get["addrs"].getElems.mapIt(it.getStr)
addrs.anyIt("p2p-circuit" in it),
timeout = RelayTimeout,
pollInterval = PollInterval,
)
check (await node2.client.setNatFiltering("endpoint-independent")).isOk
check eventuallySafe(
(await node2.client.natReachability()).get() == "Reachable",
timeout = RelayTimeout,
pollInterval = PollInterval,
)
check eventuallySafe(
not (await node2.client.natRelayRunning()).get(),
timeout = RelayTimeout,
pollInterval = PollInterval,
)
let natToSimConfig = NodeConfigs(
clients: StorageConfigs
.init(nodes = 2)
.withRelay(idx = 0)
.withNatSimulation(idx = 1, "endpoint-independent")
.withNatNumPeersToAsk(1)
.withNatMinConfidence(0.5)
.withNatScheduleInterval(5.seconds)
.withNatMaxQueueSize(1).some
)
# node2 starts reachable (endpoint-independent NAT sim = pass-through),
# then NAT is tightened to block dial-backs and AutoNAT detects NotReachable.
test "reachable node becomes not reachable when nat is applied", natToSimConfig:
let node2 = clients()[1]
check eventuallySafe(
(await node2.client.natReachability()).get() == "Reachable",
timeout = RelayTimeout,
pollInterval = PollInterval,
)
check eventuallySafe(
not (await node2.client.natRelayRunning()).get(),
timeout = RelayTimeout,
pollInterval = PollInterval,
)
check (await node2.client.setNatFiltering("address-and-port-dependent")).isOk
check eventuallySafe(
(await node2.client.natReachability()).get() == "NotReachable",
timeout = RelayTimeout,
pollInterval = PollInterval,
)
check eventuallySafe(
(await node2.client.natRelayRunning()).get(),
timeout = RelayTimeout,
pollInterval = PollInterval,
)

View File

@ -281,3 +281,23 @@ proc natReachability*(
return info.get()["nat"]["reachability"].getStr().success
except KeyError as e:
return failure e.msg
proc natRelayRunning*(
client: StorageClient
): Future[?!bool] {.async: (raises: [CancelledError, HttpError]).} =
let info = await client.info()
if info.isErr:
return failure "Failed to get node info"
try:
return info.get()["nat"]["relayRunning"].getBool().success
except KeyError as e:
return failure e.msg
proc setNatFiltering*(
client: StorageClient, filtering: string
): Future[?!void] {.async: (raises: [CancelledError, HttpError]).} =
let response =
await client.post(client.baseurl & "/debug/nat/filtering?filtering=" & filtering)
if response.status != 200:
return failure "Failed to set NAT filtering: " & $response.status
return success()

View File

@ -338,3 +338,35 @@ proc withExtIp*(
for config in startConfig.configs.mitems:
config.addCliOption("--nat", "extip:" & ip)
return startConfig
proc withRelay*(
self: StorageConfigs, idx: int
): StorageConfigs {.raises: [StorageConfigError].} =
self.checkBounds idx
var startConfig = self
startConfig.configs[idx].addCliOption("--relay")
return startConfig
proc withRelay*(self: StorageConfigs): StorageConfigs {.raises: [StorageConfigError].} =
var startConfig = self
for config in startConfig.configs.mitems:
config.addCliOption("--relay")
return startConfig
proc withNatSimulation*(
self: StorageConfigs, idx: int, filtering = "address-and-port-dependent"
): StorageConfigs {.raises: [StorageConfigError].} =
self.checkBounds idx
var startConfig = self
startConfig.configs[idx].addCliOption("--nat-simulation", filtering)
return startConfig
proc withNatSimulation*(
self: StorageConfigs, filtering = "address-and-port-dependent"
): StorageConfigs {.raises: [StorageConfigError].} =
var startConfig = self
for config in startConfig.configs.mitems:
config.addCliOption("--nat-simulation", filtering)
return startConfig