diff --git a/storage/nat.nim b/storage/nat.nim index 0b83729e..ad06fba3 100644 --- a/storage/nat.nim +++ b/storage/nat.nim @@ -17,10 +17,12 @@ import import pkg/chronos import pkg/chronicles import pkg/libp2p +import pkg/libp2p/protocols/connectivity/autonatv2/service import ./utils import ./utils/natutils import ./utils/addrutils +import ./discovery const UPNP_TIMEOUT = 200 # ms @@ -61,6 +63,16 @@ type PrefSrcStatus = enum BindAddressIsPublic BindAddressIsPrivate +type NatMapper* = ref object of RootObj + +method mapNatAddresses*( + m: NatMapper, addrs: seq[MultiAddress], discoveryPort: Port +): tuple[libp2p, discovery: seq[MultiAddress]] {.base, gcsafe, raises: [].} = + raiseAssert "mapNatAddresses not implemented" + +type DefaultNatMapper* = ref object of NatMapper + natConfig*: NatConfig + ## Also does threadvar initialisation. ## Must be called before redirectPorts() in each thread. proc getExternalIP*(natStrategy: NatStrategy, quiet = false): Option[IpAddress] = @@ -437,3 +449,31 @@ proc nattedAddress*( # Invalid multiaddress format - return as is it (newAddrs, discoveryAddrs) + +method mapNatAddresses*( + m: DefaultNatMapper, addrs: seq[MultiAddress], discoveryPort: Port +): tuple[libp2p, discovery: seq[MultiAddress]] {.gcsafe, raises: [].} = + nattedAddress(m.natConfig, addrs, discoveryPort) + +proc handleNatStatus*( + networkReachability: NetworkReachability, + confidence: Opt[float], + mapper: NatMapper, + listenAddrs: seq[MultiAddress], + discoveryPort: Port, + discovery: Discovery, +) {.async: (raises: [CancelledError]).} = + debug "AutoNAT status", reachability = networkReachability, confidence + + case networkReachability + of Reachable: + # TODO: switch DHT to server mode, stop relay if running + discard + of NotReachable: + let (announceAddrs, discoveryAddrs) = + mapper.mapNatAddresses(listenAddrs, discoveryPort) + discovery.updateAnnounceRecord(announceAddrs) + discovery.updateDhtRecord(announceAddrs & discoveryAddrs) + of Unknown: + # Nothing to do here, not enough confidence score result + discard diff --git a/storage/storage.nim b/storage/storage.nim index 7c51fde0..c760e7dd 100644 --- a/storage/storage.nim +++ b/storage/storage.nim @@ -344,15 +344,15 @@ proc new*( switch.mount(network) switch.mount(manifestProto) + let natMapper = DefaultNatMapper(natConfig: config.nat) autonatService.setStatusAndConfidenceHandler( proc( networkReachability: NetworkReachability, confidence: Opt[float] ) {.async: (raises: [CancelledError]).} = - if networkReachability == NotReachable: - let (announceAddrs, discoveryAddrs) = - nattedAddress(config.nat, switch.peerInfo.addrs, config.discoveryPort) - discovery.updateAnnounceRecord(announceAddrs) - discovery.updateDhtRecord(announceAddrs & discoveryAddrs) + await handleNatStatus( + networkReachability, confidence, natMapper, switch.peerInfo.addrs, + config.discoveryPort, discovery, + ) ) StorageServer( diff --git a/tests/storage/testnat.nim b/tests/storage/testnat.nim index 71155540..0acb0725 100644 --- a/tests/storage/testnat.nim +++ b/tests/storage/testnat.nim @@ -1,12 +1,24 @@ import std/[unittest, net] import pkg/chronos +import pkg/libp2p import pkg/libp2p/[multiaddress, multihash, multicodec] +import pkg/libp2p/protocols/connectivity/autonatv2/service import pkg/results import ../../storage/nat +import ../../storage/discovery +import ../../storage/rng import ../../storage/utils import ../../storage/utils/natutils +type MockNatMapper = ref object of NatMapper + mapped: tuple[libp2p, discovery: seq[MultiAddress]] + +method mapNatAddresses*( + m: MockNatMapper, addrs: seq[MultiAddress], discoveryPort: Port +): tuple[libp2p, discovery: seq[MultiAddress]] {.raises: [].} = + m.mapped + suite "NAT Address Tests": test "nattedAddress with local addresses": # Setup test data @@ -65,3 +77,18 @@ suite "setupAddress": check ip == none(IpAddress) check tcpPort == some(Port(5000)) check udpPort == some(Port(5001)) + +suite "handleNatStatus": + let key = PrivateKey.random(Rng.instance[]).get() + + test "NotReachable updates announce addresses": + let disc = Discovery.new(key, announceAddrs = @[]) + 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])) + + waitFor handleNatStatus( + NotReachable, Opt.none(float), mapper, @[], Port(8090), disc + ) + + check disc.announceAddrs == @[announceAddr]