diff --git a/tests/integration/1_minute/testnat.nim b/tests/integration/1_minute/testnat.nim index 4a76e31c..0edfeedd 100644 --- a/tests/integration/1_minute/testnat.nim +++ b/tests/integration/1_minute/testnat.nim @@ -18,13 +18,11 @@ multinodesuite "AutoNAT detection": let natConfig = NodeConfigs( clients: StorageConfigs .init(nodes = 2) + .withRelay(0) .withNatNumPeersToAsk(1) .withNatMinConfidence(0.5) .withNatScheduleInterval(10.seconds) - .withNatMaxQueueSize(1) - # .withLogFile() - # .withLogLevel("DEBUG") - .some + .withNatMaxQueueSize(1).some ) test "node is reachable when using bootstrap node on same network", natConfig: let node2 = clients()[1] @@ -41,22 +39,45 @@ multinodesuite "AutoNAT detection": pollInterval = PollInterval, ) + let endpointIndependentConfig = NodeConfigs( + clients: StorageConfigs + .init(nodes = 2) + .withRelay(0) + .withNatSimulation(idx = 1, "endpoint-independent") + .withNatNumPeersToAsk(1) + .withNatMinConfidence(0.5) + .withNatScheduleInterval(10.seconds) + .withNatMaxQueueSize(1).some + ) + # EIF = Endpoint Independent Filtering + test "node with simulated EIF nat is detected as reachable", endpointIndependentConfig: + 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, + ) + let autonatConfig = NodeConfigs( clients: StorageConfigs .init(nodes = 2) - .withRelay(idx = 0) - .withNatSimulation(idx = 1) + .withRelay(0) + .withNatSimulation(idx = 1, "address-and-port-dependent") .withNatNumPeersToAsk(1) .withNatMinConfidence(0.5) .withNatScheduleInterval(10.seconds) - .withNatMaxQueueSize(1) - # .withLogLevel("DEBUG") - # .debug() - # .withLogFile() - .some + .withNatMaxQueueSize(1).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: + # APDF = Address and Port-Dependent Filtering + test "node with simulated APDF nat is detected as not reachable and starts relay", + autonatConfig: let node2 = clients()[1] check eventuallySafe( @@ -82,16 +103,17 @@ multinodesuite "AutoNAT detection": let transitionConfig = NodeConfigs( clients: StorageConfigs .init(nodes = 2) - .withRelay(idx = 0) - .withNatSimulation(idx = 1) + .withRelay(0) + .withNatSimulation(idx = 1, "address-and-port-dependent") .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: + # APDF = Address and Port-Dependent Filtering + # EIF = Endpoint Independent Filtering + test "node with simulated APDF nat recovers to reachable and stops relay when nat switches to EIF nat", + transitionConfig: let node2 = clients()[1] check eventuallySafe( @@ -131,16 +153,17 @@ multinodesuite "AutoNAT detection": let natToSimConfig = NodeConfigs( clients: StorageConfigs .init(nodes = 2) - .withRelay(idx = 0) + .withRelay(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: + + # APDF = Address and Port-Dependent Filtering + test "reachable node becomes not reachable and starts relay when nat switches to APDF nat", + natToSimConfig: let node2 = clients()[1] check eventuallySafe( @@ -168,3 +191,61 @@ multinodesuite "AutoNAT detection": timeout = RelayTimeout, pollInterval = PollInterval, ) + + let multiNatConfig = NodeConfigs( + clients: StorageConfigs + .init(nodes = 3) + .withRelay(0) + .withNatSimulation(idx = 1, "address-and-port-dependent") + .withNatSimulation(idx = 2, "address-and-port-dependent") + .withNatNumPeersToAsk(1) + .withNatMinConfidence(0.5) + .withNatScheduleInterval(5.seconds) + .withNatMaxQueueSize(1).some + ) + + # APDF = Address and Port-Dependent Filtering + test "two nodes with simulated APDF nat starts relay through the same relay node", + multiNatConfig: + let node2 = clients()[1] + let node3 = clients()[2] + + check eventuallySafe( + (await node2.client.natReachability()).get() == "NotReachable", + timeout = RelayTimeout, + pollInterval = PollInterval, + ) + + check eventuallySafe( + (await node3.client.natReachability()).get() == "NotReachable", + timeout = RelayTimeout, + pollInterval = PollInterval, + ) + + check eventuallySafe( + (await node2.client.natRelayRunning()).get(), + timeout = RelayTimeout, + pollInterval = PollInterval, + ) + + check eventuallySafe( + (await node3.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 eventuallySafe( + block: + let addrs = (await node3.client.info()).get["addrs"].getElems.mapIt(it.getStr) + addrs.anyIt("p2p-circuit" in it), + timeout = RelayTimeout, + pollInterval = PollInterval, + ) diff --git a/tests/integration/5_minutes/testnatdownload.nim b/tests/integration/5_minutes/testnatdownload.nim new file mode 100644 index 00000000..365217d2 --- /dev/null +++ b/tests/integration/5_minutes/testnatdownload.nim @@ -0,0 +1,65 @@ +import std/json +import std/sequtils +import pkg/chronos +import pkg/questionable/results + +import ../multinodes +import ../storageclient +import ../storageconfig + +const + RelayTimeout = 30_000 + PollInterval = 1_000 + +multinodesuite "NAT download": + let natDownloadConfig = NodeConfigs( + clients: StorageConfigs + .init(nodes = 3) + .withRelay(idx = 0) + .withNatSimulation(idx = 2, "address-and-port-dependent") + .withNatNumPeersToAsk(1) + .withNatMinConfidence(0.5) + .withNatScheduleInterval(5.seconds) + .withNatMaxQueueSize(1).some + ) + # APDF = Address and Port-Dependent Filtering + test "node 3 with simulated APDF downloads content from reachable seed node 2", + natDownloadConfig: + let seed = clients()[1] + let natNode = clients()[2] + + let content = "content for nat download test" + let cid = (await seed.client.upload(content)).get + + check eventuallySafe( + (await natNode.client.download(cid)).isOk, + timeout = RelayTimeout, + pollInterval = PollInterval, + ) + + check (await natNode.client.download(cid)).get == content + + # APDF = Address and Port-Dependent Filtering + test "reachable node 2 downloads content from node 3 with simulated APDF via relay", + natDownloadConfig: + let seed = clients()[1] + let natNode = clients()[2] + + check eventuallySafe( + block: + let addrs = (await natNode.client.info()).get["addrs"].getElems.mapIt(it.getStr) + addrs.anyIt("p2p-circuit" in it), + timeout = RelayTimeout, + pollInterval = PollInterval, + ) + + let content = "content seeded from nat node" + let cid = (await natNode.client.upload(content)).get + + check eventuallySafe( + (await seed.client.download(cid)).isOk, + timeout = RelayTimeout, + pollInterval = PollInterval, + ) + + check (await seed.client.download(cid)).get == content diff --git a/tests/integration/multinodes.nim b/tests/integration/multinodes.nim index 4d6d6f62..a0c70cc9 100644 --- a/tests/integration/multinodes.nim +++ b/tests/integration/multinodes.nim @@ -219,7 +219,8 @@ template multinodesuite*(suiteName: string, body: untyped) = for config in clients.configs: let node = await startClientNode(config) running.add RunningNode(role: Role.Client, node: node) - await StorageProcess(node).updateBootstrapNodes() + if config.isBootstrapNode: + await StorageProcess(node).updateBootstrapNodes() teardown: await teardownImpl() diff --git a/tests/integration/storageconfig.nim b/tests/integration/storageconfig.nim index ed839b1f..934f77d0 100644 --- a/tests/integration/storageconfig.nim +++ b/tests/integration/storageconfig.nim @@ -354,8 +354,21 @@ proc withRelay*(self: StorageConfigs): StorageConfigs {.raises: [StorageConfigEr config.addCliOption("--relay") return startConfig +# For testing, a node with extip (not behind nat) is a bootstrap node +proc isBootstrapNode*(config: StorageConfig): bool {.raises: [].} = + let opts = config.cliOptions.getOrDefault(StartUpCmd.noCmd) + + try: + if "--nat" in opts and "extip" in opts["--nat"].value: + return true + except KeyError: + warn "Failed to look at the extip config" + return false + + return false + proc withNatSimulation*( - self: StorageConfigs, idx: int, filtering = "address-and-port-dependent" + self: StorageConfigs, idx: int, filtering: string ): StorageConfigs {.raises: [StorageConfigError].} = self.checkBounds idx @@ -364,7 +377,7 @@ proc withNatSimulation*( return startConfig proc withNatSimulation*( - self: StorageConfigs, filtering = "address-and-port-dependent" + self: StorageConfigs, filtering: string ): StorageConfigs {.raises: [StorageConfigError].} = var startConfig = self for config in startConfig.configs.mitems: diff --git a/tests/integration/twonodes.nim b/tests/integration/twonodes.nim index 7fa55244..2398873a 100644 --- a/tests/integration/twonodes.nim +++ b/tests/integration/twonodes.nim @@ -12,7 +12,7 @@ export multinodes template twonodessuite*(name: string, body: untyped) = multinodesuite name: let twoNodesConfig {.inject, used.} = - NodeConfigs(clients: StorageConfigs.init(nodes = 2).some) + NodeConfigs(clients: StorageConfigs.init(nodes = 2).withExtIp(1).some) var node1 {.inject, used.}: StorageProcess var node2 {.inject, used.}: StorageProcess