From 00e5c0cfac5b594824b260724629edac8a516dfd Mon Sep 17 00:00:00 2001 From: Arnaud Date: Tue, 5 May 2026 14:58:26 +0400 Subject: [PATCH] use AutoNat dialback IP instead of router public IP --- storage/nat.nim | 281 +++++++--------------------- storage/storage.nim | 10 +- storage/utils/addrutils.nim | 9 +- tests/storage/helpers/nodeutils.nim | 9 +- tests/storage/testnat.nim | 132 +++++-------- 5 files changed, 133 insertions(+), 308 deletions(-) diff --git a/storage/nat.nim b/storage/nat.nim index ee9ce622..863b1e68 100644 --- a/storage/nat.nim +++ b/storage/nat.nim @@ -9,15 +9,14 @@ {.push raises: [].} import - std/[options, os, times, net, atomics, exitprocs], + std/[options, os, times, atomics, exitprocs], nat_traversal/[miniupnpc, natpmp], - json_serialization/std/net, results import pkg/chronos import pkg/chronicles import pkg/libp2p -import pkg/libp2p/protocols/connectivity/autonatv2/service +import pkg/libp2p/protocols/connectivity/autonat/types import pkg/libp2p/services/autorelayservice import ./utils @@ -50,41 +49,25 @@ var npmp {.threadvar.}: NatPmp strategy = NatStrategy.NatAuto natClosed: Atomic[bool] - extIp: Option[IpAddress] activeMappings: seq[PortMappings] natThreads: seq[Thread[PortMappingArgs]] = @[] logScope: topics = "nat" -type PrefSrcStatus = enum - NoRoutingInfo - PrefSrcIsPublic - PrefSrcIsPrivate - BindAddressIsPublic - BindAddressIsPrivate - type NatMapper* = ref object of RootObj -method mapNatAddresses*( - m: NatMapper, addrs: seq[MultiAddress] -): tuple[libp2p, discovery: seq[MultiAddress]] {.base, gcsafe, raises: [].} = - raiseAssert "mapNatAddresses not implemented" - -method getReachableAddresses*( - m: NatMapper, addrs: seq[MultiAddress] -): tuple[libp2p, discovery: seq[MultiAddress]] {.base, gcsafe, raises: [].} = - raiseAssert "getReachableAddresses not implemented" +method mapNatPorts*(m: NatMapper): Option[(Port, Port)] {.base, gcsafe, raises: [].} = + raiseAssert "mapNatPorts not implemented" type DefaultNatMapper* = ref object of NatMapper natConfig*: NatConfig + tcpPort*: Port discoveryPort*: Port -## Also does threadvar initialisation. +## Initialises the UPnP or NAT-PMP threadvar and sets the `strategy` threadvar. ## Must be called before redirectPorts() in each thread. -proc getExternalIP*(natStrategy: NatStrategy, quiet = false): Option[IpAddress] = - var externalIP: IpAddress - +proc initNatDevice(natStrategy: NatStrategy, quiet = false): bool = if natStrategy == NatStrategy.NatAuto or natStrategy == NatStrategy.NatUpnp: if upnp == nil: upnp = newMiniupnp() @@ -114,18 +97,8 @@ proc getExternalIP*(natStrategy: NatStrategy, quiet = false): Option[IpAddress] if not quiet: debug "UPnP", msg if canContinue: - let ires = upnp.externalIPAddress() - if ires.isErr: - debug "UPnP", msg = ires.error - else: - # if we got this far, UPnP is working and we don't need to try NAT-PMP - try: - externalIP = parseIpAddress(ires.value) - strategy = NatStrategy.NatUpnp - return some(externalIP) - except ValueError as e: - error "parseIpAddress() exception", err = e.msg - return + strategy = NatStrategy.NatUpnp + return true if natStrategy == NatStrategy.NatAuto or natStrategy == NatStrategy.NatPmp: if npmp == nil: @@ -134,61 +107,10 @@ proc getExternalIP*(natStrategy: NatStrategy, quiet = false): Option[IpAddress] if nres.isErr: debug "NAT-PMP", msg = nres.error else: - let nires = npmp.externalIPAddress() - if nires.isErr: - debug "NAT-PMP", msg = nires.error - else: - try: - externalIP = parseIpAddress($(nires.value)) - strategy = NatStrategy.NatPmp - return some(externalIP) - except ValueError as e: - error "parseIpAddress() exception", err = e.msg - return + strategy = NatStrategy.NatPmp + return true -# This queries the routing table to get the "preferred source" attribute and -# checks if it's a public IP. If so, then it's our public IP. -# -# Further more, we check if the bind address (user provided, or a "0.0.0.0" -# default) is a public IP. That's a long shot, because code paths involving a -# user-provided bind address are not supposed to get here. -proc getRoutePrefSrc(bindIp: IpAddress): (Option[IpAddress], PrefSrcStatus) = - let bindAddress = initTAddress(bindIp, Port(0)) - - if bindAddress.isAnyLocal(): - let ip = - if bindIp.family == IpAddressFamily.IPv6: - getRouteIpv6() - else: - getRouteIpv4() - - if ip.isErr(): - # No route was found, log error and continue without IP. - error "No routable IP address found, check your network connection", - error = ip.error - return (none(IpAddress), NoRoutingInfo) - elif ip.get().isGlobalUnicast(): - return (some(ip.get()), PrefSrcIsPublic) - else: - return (none(IpAddress), PrefSrcIsPrivate) - elif bindAddress.isGlobalUnicast(): - return (some(bindIp), BindAddressIsPublic) - else: - return (none(IpAddress), BindAddressIsPrivate) - -# Try to detect a public IP assigned to this host, before trying NAT traversal. -proc getPublicRoutePrefSrcOrExternalIP*( - natStrategy: NatStrategy, bindIp: IpAddress, quiet = true -): Option[IpAddress] = - let (prefSrcIp, prefSrcStatus) = getRoutePrefSrc(bindIp) - - case prefSrcStatus - of NoRoutingInfo, PrefSrcIsPublic, BindAddressIsPublic: - return prefSrcIp - of PrefSrcIsPrivate, BindAddressIsPrivate: - let extIp = getExternalIP(natStrategy, quiet) - if extIp.isSome: - return some(extIp.get) + return false proc doPortMapping( strategy: NatStrategy, tcpPort, udpPort: Port, description: string @@ -262,10 +184,8 @@ proc repeatPortMapping(args: PortMappingArgs) {.thread, raises: [ValueError].} = # We can't use copies of Miniupnp and NatPmp objects in this thread, because they share # C pointers with other instances that have already been garbage collected, so - # we use threadvars instead and initialise them again with getExternalIP(), - # even though we don't need the external IP's value. - let ipres = getExternalIP(strategy, quiet = true) - if ipres.isSome: + # we use threadvars instead and initialise them again here. + if initNatDevice(strategy, quiet = true): while natClosed.load() == false: let currTime = now() if currTime >= (lastUpdate + interval): @@ -292,8 +212,7 @@ proc stopNatThreads() {.noconv.} = # In Windows, a new thread is created for the signal handler, so we need to # initialise our threadvars again. - let ipres = getExternalIP(strategy, quiet = true) - if ipres.isSome: + if initNatDevice(strategy, quiet = true): if strategy == NatStrategy.NatUpnp: for entry in activeMappings: for t in [ @@ -358,55 +277,23 @@ proc redirectPorts*( proc setupNat*( natStrategy: NatStrategy, tcpPort, udpPort: Port, clientId: string -): tuple[ip: Option[IpAddress], tcpPort, udpPort: Option[Port]] = - ## Setup NAT port mapping and get external IP address. - ## If any of this fails, we don't return any IP address but do return the - ## original ports as best effort. +): Option[(Port, Port)] = + ## Setup NAT port mapping. + ## Returns the external (tcpPort, udpPort) if port mapping succeeded, none otherwise. ## TODO: Allow for tcp or udp port mapping to be optional. - if extIp.isNone: - extIp = getExternalIP(natStrategy) - if extIp.isSome: - let ip = extIp.get - let extPorts = ( - {.gcsafe.}: - redirectPorts( - strategy, tcpPort = tcpPort, udpPort = udpPort, description = clientId - ) - ) - if extPorts.isSome: - let (extTcpPort, extUdpPort) = extPorts.get() - (ip: some(ip), tcpPort: some(extTcpPort), udpPort: some(extUdpPort)) - else: - warn "UPnP/NAT-PMP available but port forwarding failed" - (ip: none(IpAddress), tcpPort: some(tcpPort), udpPort: some(udpPort)) - else: + if not initNatDevice(natStrategy): warn "UPnP/NAT-PMP not available" - (ip: none(IpAddress), tcpPort: some(tcpPort), udpPort: some(udpPort)) + return none((Port, Port)) -proc setupAddress*( - natConfig: NatConfig, bindIp: IpAddress, tcpPort, udpPort: Port, clientId: string -): tuple[ip: Option[IpAddress], tcpPort, udpPort: Option[Port]] {.gcsafe.} = - ## Set-up of the external address via any of the ways as configured in - ## `NatConfig`. In case all fails an error is logged and the bind ports are - ## selected also as external ports, as best effort and in hope that the - ## external IP can be figured out by other means at a later stage. - ## TODO: Allow for tcp or udp bind ports to be optional. - - if natConfig.hasExtIp: - # any required port redirection must be done by hand - return (some(natConfig.extIp), some(tcpPort), some(udpPort)) - - case natConfig.nat - of NatStrategy.NatAuto: - let (prefSrcIp, prefSrcStatus) = getRoutePrefSrc(bindIp) - - case prefSrcStatus - of NoRoutingInfo, PrefSrcIsPublic, BindAddressIsPublic: - return (prefSrcIp, some(tcpPort), some(udpPort)) - of PrefSrcIsPrivate, BindAddressIsPrivate: - return setupNat(natConfig.nat, tcpPort, udpPort, clientId) - of NatStrategy.NatUpnp, NatStrategy.NatPmp: - return setupNat(natConfig.nat, tcpPort, udpPort, clientId) + let extPorts = ( + {.gcsafe.}: + redirectPorts( + strategy, tcpPort = tcpPort, udpPort = udpPort, description = clientId + ) + ) + if extPorts.isNone: + warn "UPnP/NAT-PMP available but port forwarding failed" + extPorts proc findReachableNodes*(bootstrapNodes: seq[SignedPeerRecord]): seq[SignedPeerRecord] = ## Returns the list of nodes known to be directly reachable. @@ -414,56 +301,13 @@ proc findReachableNodes*(bootstrapNodes: seq[SignedPeerRecord]): seq[SignedPeerR ## confirmed reachable by AutoNAT could be included. bootstrapNodes -proc nattedAddress*( - natConfig: NatConfig, addrs: seq[MultiAddress], udpPort: Port -): tuple[libp2p, discovery: seq[MultiAddress]] = - ## Takes a NAT configuration, sequence of multiaddresses and UDP port and returns: - ## - Modified multiaddresses with NAT-mapped addresses for libp2p - ## - Discovery addresses with NAT-mapped UDP ports +proc nattedPorts*(natConfig: NatConfig, tcpPort, udpPort: Port): Option[(Port, Port)] = + if natConfig.hasExtIp: + return none((Port, Port)) # manual setup, no port mapping needed + setupNat(natConfig.nat, tcpPort, udpPort, "storage") - var discoveryAddrs = newSeq[MultiAddress](0) - let newAddrs = addrs.mapIt: - block: - # Extract IP address and port from the multiaddress - let (ipPart, port) = getAddressAndPort(it) - if ipPart.isSome and port.isSome: - # Try to setup NAT mapping for the address - let (newIP, tcp, udp) = - setupAddress(natConfig, ipPart.get, port.get, udpPort, "storage") - if newIP.isSome: - # NAT mapping successful - add discovery address with mapped UDP port - discoveryAddrs.add(getMultiAddrWithIPAndUDPPort(newIP.get, udp.get)) - # Remap original address with NAT IP and TCP port - it.remapAddr(ip = newIP, port = tcp) - else: - # NAT mapping failed - use original address - echo "Failed to get external IP, using original address", it - discoveryAddrs.add(getMultiAddrWithIPAndUDPPort(ipPart.get, udpPort)) - it - else: - # Invalid multiaddress format - return as is - it - (newAddrs, discoveryAddrs) - -method mapNatAddresses*( - m: DefaultNatMapper, addrs: seq[MultiAddress] -): tuple[libp2p, discovery: seq[MultiAddress]] {.gcsafe, raises: [].} = - nattedAddress(m.natConfig, addrs, m.discoveryPort) - -method getReachableAddresses*( - m: DefaultNatMapper, addrs: seq[MultiAddress] -): tuple[libp2p, discovery: seq[MultiAddress]] {.gcsafe, raises: [].} = - let ip = - if m.natConfig.hasExtIp: - some(m.natConfig.extIp) - else: - let (routeIp, _) = getRoutePrefSrc(static parseIpAddress("0.0.0.0")) - routeIp - if ip.isNone: - return (@[], @[]) - let announceAddrs = - addrs.mapIt(it.remapAddr(ip = ip, port = none(Port))).deduplicate() - (announceAddrs, @[getMultiAddrWithIPAndUDPPort(ip.get, m.discoveryPort)]) +method mapNatPorts*(m: DefaultNatMapper): Option[(Port, Port)] {.gcsafe, raises: [].} = + nattedPorts(m.natConfig, m.tcpPort, m.discoveryPort) proc hasPublicIp*(addrs: seq[MultiAddress]): bool = for addr in addrs: @@ -473,6 +317,8 @@ proc hasPublicIp*(addrs: seq[MultiAddress]): bool = proc handleNatStatus*( networkReachability: NetworkReachability, + dialBackAddr: Opt[MultiAddress], + discoveryPort: Port, mapper: NatMapper, discovery: Discovery, switch: Switch, @@ -483,33 +329,42 @@ proc handleNatStatus*( # Nothing to do here, not enough confidence score result discard of Reachable: - # For UPnP, it the mapping was a success, - # the autorelay service has been stopped - # and the address was already announced + if dialBackAddr.isNone: + warn "Got empty dialback address in AutoNat when node is Reachable" + return + if autoRelayService.isRunning: if not await autoRelayService.stop(switch): debug "AutoRelayService stop method returned false" - let (announceAddrs, discoveryAddrs) = - mapper.getReachableAddresses(switch.peerInfo.addrs) - discovery.updateAnnounceRecord(announceAddrs) - discovery.updateDhtRecord(announceAddrs & discoveryAddrs) + let discAddr = + dialBackAddr.get.remapAddr(protocol = some("udp"), port = some(discoveryPort)) + discovery.updateAnnounceRecord(@[dialBackAddr.get]) + discovery.updateDhtRecord(@[dialBackAddr.get, discAddr]) # TODO: switch DHT to server mode of NotReachable: - let (announceAddrs, discoveryAddrs) = mapper.mapNatAddresses(switch.peerInfo.addrs) + var hasPortMapping = false - # With a UPnP / NatPmP successful mapping, - # we suppose that having a public IP make it Reachable. - # If not, the state will be updated in the next Autonat iteration. - # TODO: Do we need to manually call dialMe to make sure we are Reachable ? - if hasPublicIp(announceAddrs): - discovery.updateAnnounceRecord(announceAddrs) - discovery.updateDhtRecord(announceAddrs & discoveryAddrs) + if dialBackAddr.isSome: + let maybePorts = mapper.mapNatPorts() - if autoRelayService.isRunning: - if not await autoRelayService.stop(switch): - debug "AutoRelayService stop method returned false" - else: - if not autoRelayService.isRunning: - if not await autoRelayService.setup(switch): - debug "AutoRelayService setup method returned false" + if maybePorts.isSome: + let (tcpPort, udpPort) = maybePorts.get() + let announceAddr = dialBackAddr.get.remapAddr(port = some(tcpPort)) + let discAddr = + dialBackAddr.get.remapAddr(protocol = some("udp"), port = some(udpPort)) + + # TODO: Try a dial me to make sure we are reachable + + if autoRelayService.isRunning: + if not await autoRelayService.stop(switch): + debug "AutoRelayService stop method returned false" + + discovery.updateAnnounceRecord(@[announceAddr]) + discovery.updateDhtRecord(@[announceAddr, discAddr]) + + hasPortMapping = true + + if not hasPortMapping and not autoRelayService.isRunning: + if not await autoRelayService.setup(switch): + debug "AutoRelayService setup method returned false" diff --git a/storage/storage.nim b/storage/storage.nim index 31bb9d8a..5e7ce9f7 100644 --- a/storage/storage.nim +++ b/storage/storage.nim @@ -360,8 +360,11 @@ proc new*( switch.mount(network) switch.mount(manifestProto) - let natMapper = - DefaultNatMapper(natConfig: config.nat, discoveryPort: config.discoveryPort) + let natMapper = DefaultNatMapper( + natConfig: config.nat, + tcpPort: config.listenPort, + discoveryPort: config.discoveryPort, + ) autonatService.setStatusAndConfidenceHandler( proc( networkReachability: NetworkReachability, @@ -370,7 +373,8 @@ proc new*( ) {.async: (raises: [CancelledError]).} = debug "AutoNAT status", reachability = networkReachability, confidence await handleNatStatus( - networkReachability, natMapper, discovery, switch, autoRelayService + networkReachability, addrs, config.discoveryPort, natMapper, discovery, switch, + autoRelayService, ) ) diff --git a/storage/utils/addrutils.nim b/storage/utils/addrutils.nim index 31570e06..09891cfa 100644 --- a/storage/utils/addrutils.nim +++ b/storage/utils/addrutils.nim @@ -20,8 +20,9 @@ func remapAddr*( address: MultiAddress, ip: Option[IpAddress] = IpAddress.none, port: Option[Port] = Port.none, + protocol: Option[string] = string.none, ): MultiAddress = - ## Remap addresses to new IP and/or Port + ## Remap addresses to new IP, port, and/or transport protocol (e.g. "tcp" → "udp") ## var parts = ($address).split("/") @@ -32,6 +33,12 @@ func remapAddr*( else: parts[2] + parts[3] = + if protocol.isSome: + protocol.get + else: + parts[3] + parts[4] = if port.isSome: $port.get diff --git a/tests/storage/helpers/nodeutils.nim b/tests/storage/helpers/nodeutils.nim index a891db23..ae8dab9c 100644 --- a/tests/storage/helpers/nodeutils.nim +++ b/tests/storage/helpers/nodeutils.nim @@ -218,13 +218,8 @@ proc generateNodes*( if config.enableBootstrap: waitFor switch.peerInfo.update() - let (announceAddrs, discoveryAddrs) = nattedAddress( - NatConfig(hasExtIp: false, nat: NatAuto), - switch.peerInfo.addrs, - bindPort.Port, - ) - blockDiscovery.updateAnnounceRecord(announceAddrs) - blockDiscovery.updateDhtRecord(discoveryAddrs) + blockDiscovery.updateAnnounceRecord(switch.peerInfo.addrs) + blockDiscovery.updateDhtRecord(switch.peerInfo.addrs) if blockDiscovery.dhtRecord.isSome: bootstrapNodes.add !blockDiscovery.dhtRecord diff --git a/tests/storage/testnat.nim b/tests/storage/testnat.nim index ee9ed27e..70b80572 100644 --- a/tests/storage/testnat.nim +++ b/tests/storage/testnat.nim @@ -15,66 +15,34 @@ import ../../storage/discovery import ../../storage/rng import ../../storage/utils import ../../storage/utils/natutils +import ../../storage/utils/addrutils type MockNatMapper = ref object of NatMapper - mapped: tuple[libp2p, discovery: seq[MultiAddress]] + mappedPorts: Option[(Port, Port)] -method mapNatAddresses*( - m: MockNatMapper, addrs: seq[MultiAddress] -): tuple[libp2p, discovery: seq[MultiAddress]] {.raises: [].} = - m.mapped +method mapNatPorts*(m: MockNatMapper): Option[(Port, Port)] {.raises: [].} = + m.mappedPorts -method getReachableAddresses*( - m: MockNatMapper, addrs: seq[MultiAddress] -): tuple[libp2p, discovery: seq[MultiAddress]] {.raises: [].} = - m.mapped +suite "remapAddr": + test "replaces protocol tcp with udp": + let ma = MultiAddress.init("/ip4/1.2.3.4/tcp/5000").expect("valid") + let remapped = ma.remapAddr(protocol = some("udp"), port = some(Port(9000))) + check remapped == MultiAddress.init("/ip4/1.2.3.4/udp/9000").expect("valid") -suite "NAT Address Tests": - test "nattedAddress with local addresses": - # Setup test data - let - udpPort = Port(1234) - natConfig = NatConfig(hasExtIp: true, extIp: parseIpAddress("8.8.8.8")) + test "replaces only port, keeping protocol": + let ma = MultiAddress.init("/ip4/1.2.3.4/tcp/5000").expect("valid") + let remapped = ma.remapAddr(port = some(Port(9000))) + check remapped == MultiAddress.init("/ip4/1.2.3.4/tcp/9000").expect("valid") - # Create test addresses - localAddr = MultiAddress.init("/ip4/127.0.0.1/tcp/5000").expect("valid multiaddr") - anyAddr = MultiAddress.init("/ip4/0.0.0.0/tcp/5000").expect("valid multiaddr") - publicAddr = - MultiAddress.init("/ip4/192.168.1.1/tcp/5000").expect("valid multiaddr") + test "replaces only ip, keeping protocol and port": + let ma = MultiAddress.init("/ip4/1.2.3.4/tcp/5000").expect("valid") + let remapped = ma.remapAddr(ip = some(parseIpAddress("8.8.8.8"))) + check remapped == MultiAddress.init("/ip4/8.8.8.8/tcp/5000").expect("valid") - # Expected results - let - expectedDiscoveryAddrs = @[ - MultiAddress.init("/ip4/8.8.8.8/udp/1234").expect("valid multiaddr"), - MultiAddress.init("/ip4/8.8.8.8/udp/1234").expect("valid multiaddr"), - MultiAddress.init("/ip4/8.8.8.8/udp/1234").expect("valid multiaddr"), - ] - expectedlibp2pAddrs = @[ - MultiAddress.init("/ip4/8.8.8.8/tcp/5000").expect("valid multiaddr"), - MultiAddress.init("/ip4/8.8.8.8/tcp/5000").expect("valid multiaddr"), - MultiAddress.init("/ip4/8.8.8.8/tcp/5000").expect("valid multiaddr"), - ] - #ipv6Addr = MultiAddress.init("/ip6/::1/tcp/5000").expect("valid multiaddr") - addrs = @[localAddr, anyAddr, publicAddr] - - # Test address remapping - let (libp2pAddrs, discoveryAddrs) = nattedAddress(natConfig, addrs, udpPort) - - # Verify results - check(discoveryAddrs == expectedDiscoveryAddrs) - check(libp2pAddrs == expectedlibp2pAddrs) - -suite "getReachableAddresses": - test "returns remapped addresses when extIp is configured": - let - natConfig = NatConfig(hasExtIp: true, extIp: parseIpAddress("1.2.3.4")) - mapper = DefaultNatMapper(natConfig: natConfig, discoveryPort: Port(8090)) - listenAddr = MultiAddress.init("/ip4/0.0.0.0/tcp/5000").expect("valid") - - let (libp2pAddrs, discAddrs) = mapper.getReachableAddresses(@[listenAddr]) - - check libp2pAddrs == @[MultiAddress.init("/ip4/1.2.3.4/tcp/5000").expect("valid")] - check discAddrs == @[MultiAddress.init("/ip4/1.2.3.4/udp/8090").expect("valid")] +suite "nattedPorts": + test "returns none when extIp is configured (manual setup)": + let natConfig = NatConfig(hasExtIp: true, extIp: parseIpAddress("8.8.8.8")) + check nattedPorts(natConfig, Port(5000), Port(1234)).isNone suite "hasPublicIp": test "hasPublicIp returns true when the address is public": @@ -107,51 +75,47 @@ asyncchecksuite "handleNatStatus": if autoRelay.isRunning: discard await autoRelay.stop(sw) - test "handleNatStatus announces address when the node is not Reachable and the UPnP succeed with public ip": - let announceAddr = MultiAddress.init("/ip4/1.2.3.4/tcp/8080").expect("valid") - let discAddr = MultiAddress.init("/ip4/1.2.3.4/udp/8090").expect("valid") - let mapper = MockNatMapper(mapped: (@[announceAddr], @[discAddr])) + let discoveryPort = Port(8090) - await handleNatStatus(NotReachable, mapper, disc, sw, autoRelay) + test "handleNatStatus announces mapped address when NotReachable and UPnP succeeds": + let dialBack = MultiAddress.init("/ip4/1.2.3.4/tcp/8080").expect("valid") + let mapper = MockNatMapper(mappedPorts: some((Port(9000), Port(9001)))) - check disc.announceAddrs == @[announceAddr] + await handleNatStatus( + NotReachable, Opt.some(dialBack), discoveryPort, mapper, disc, sw, autoRelay + ) + + check disc.announceAddrs == + @[MultiAddress.init("/ip4/1.2.3.4/tcp/9000").expect("valid")] check not autoRelay.isRunning - # test "handleNatStatus does not announce address when the node is not Reachable and the UPnP succeed with private ip": - # let privateAddr = MultiAddress.init("/ip4/192.168.1.1/tcp/8080").expect("valid") - # let mapper = MockNatMapper(mapped: (@[privateAddr], @[])) + test "handleNatStatus starts autoRelay when NotReachable and UPnP failed": + let mapper = MockNatMapper(mappedPorts: none((Port, Port))) - # await handleNatStatus( - # NotReachable, mapper, disc, sw, autoRelay - # ) - - # check disc.announceAddrs == @[] - # check not autoRelay.isRunning - - test "handleNatStatus starts autoRelay when node is not Reachable and UPnP failed": - let mapper = MockNatMapper(mapped: (@[], @[])) - - await handleNatStatus(NotReachable, mapper, disc, sw, autoRelay) + await handleNatStatus( + NotReachable, Opt.none(MultiAddress), discoveryPort, mapper, disc, sw, autoRelay + ) check autoRelay.isRunning - # The addresses will be announced in the onReservation callback - # after a node accepted a Relay reservation. - test "handleNatStatus does not announce address when node is Reachable and relay is not running": - let mapper = MockNatMapper(mapped: (@[], @[])) + test "handleNatStatus does not announce address when Reachable and no dialBackAddr": + let mapper = MockNatMapper(mappedPorts: none((Port, Port))) - await handleNatStatus(Reachable, mapper, disc, sw, autoRelay) + await handleNatStatus( + Reachable, Opt.none(MultiAddress), discoveryPort, mapper, disc, sw, autoRelay + ) check disc.announceAddrs == newSeq[MultiAddress]() check not autoRelay.isRunning - test "handleNatStatus stops relay and announces address when node is Reachable and relay is running": - let announceAddr = MultiAddress.init("/ip4/1.2.3.4/tcp/8080").expect("valid") - let discAddr = MultiAddress.init("/ip4/1.2.3.4/udp/8090").expect("valid") - let mapper = MockNatMapper(mapped: (@[announceAddr], @[discAddr])) + test "handleNatStatus stops relay and announces dialBackAddr when Reachable": + let dialBack = MultiAddress.init("/ip4/1.2.3.4/tcp/8080").expect("valid") + let mapper = MockNatMapper(mappedPorts: none((Port, Port))) discard await autorelayservice.setup(autoRelay, sw) - await handleNatStatus(Reachable, mapper, disc, sw, autoRelay) + await handleNatStatus( + Reachable, Opt.some(dialBack), discoveryPort, mapper, disc, sw, autoRelay + ) check not autoRelay.isRunning - check disc.announceAddrs == @[announceAddr] + check disc.announceAddrs == @[dialBack]