Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: proper use of setupNat #1740

Merged
merged 2 commits into from
May 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions apps/chat2/chat2.nim
Original file line number Diff line number Diff line change
Expand Up @@ -404,11 +404,15 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
if conf.logLevel != LogLevel.NONE:
setLogLevel(conf.logLevel)

let
(extIp, extTcpPort, extUdpPort) = setupNat(conf.nat, clientId,
let natRes = setupNat(conf.nat, clientId,
Port(uint16(conf.tcpPort) + conf.portsShift),
Port(uint16(conf.udpPort) + conf.portsShift))

if natRes.isErr():
raise newException(ValueError, "setupNat error " & natRes.error)

let (extIp, extTcpPort, extUdpPort) = natRes.get()

let node = block:
var builder = WakuNodeBuilder.init()
builder.withNodeKey(nodeKey)
Expand Down
10 changes: 7 additions & 3 deletions apps/chat2bridge/chat2bridge.nim
Original file line number Diff line number Diff line change
Expand Up @@ -248,11 +248,15 @@ when isMainModule:
if conf.logLevel != LogLevel.NONE:
setLogLevel(conf.logLevel)

let natRes = setupNat(conf.nat, clientId,
Port(uint16(conf.libp2pTcpPort) + conf.portsShift),
Port(uint16(conf.udpPort) + conf.portsShift))
if natRes.isErr():
error "Error in setupNat", error = natRes.error

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are calling quit() in the wakubridge, but not here - should the reaction be consistent accross apps?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree indeed. However, this is consistent within the chat2bridge.nim itself. I think it is better if we raise a separate PR for that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vpavlin - Next week I will create a separate PR for adding consistency in the parameters checks for all the apps.

# Load address configuration
let
(nodev2ExtIp, nodev2ExtPort, _) = setupNat(conf.nat, clientId,
Port(uint16(conf.libp2pTcpPort) + conf.portsShift),
Port(uint16(conf.udpPort) + conf.portsShift))
(nodev2ExtIp, nodev2ExtPort, _) = natRes.get()
## The following heuristic assumes that, in absence of manual
## config, the external port is the same as the bind port.
extPort = if nodev2ExtIp.isSome() and nodev2ExtPort.isNone():
Expand Down
22 changes: 16 additions & 6 deletions apps/wakubridge/wakubridge.nim
Original file line number Diff line number Diff line change
Expand Up @@ -349,11 +349,23 @@ when isMainModule:
## actually a supported transport.
let udpPort = conf.devp2pTcpPort

let natRes = setupNat(conf.nat, ClientIdV1,
Port(conf.devp2pTcpPort + conf.portsShift),
Port(udpPort + conf.portsShift))
if natRes.isErr():
error "failed setupNat", error = natRes.error
quit(QuitFailure)

let natRes2 = setupNat(conf.nat, clientId,
Port(uint16(conf.libp2pTcpPort) + conf.portsShift),
Port(uint16(udpPort) + conf.portsShift))
if natRes2.isErr():
error "failed setupNat", error = natRes2.error
quit(QuitFailure)

# Load address configuration
let
(nodev1ExtIp, _, _) = setupNat(conf.nat, ClientIdV1,
Port(conf.devp2pTcpPort + conf.portsShift),
Port(udpPort + conf.portsShift))
(nodev1ExtIp, _, _) = natRes.get()
# TODO: EthereumNode should have a better split of binding address and
# external address. Also, can't have different ports as it stands now.
nodev1Address = if nodev1ExtIp.isNone():
Expand All @@ -364,9 +376,7 @@ when isMainModule:
Address(ip: nodev1ExtIp.get(),
tcpPort: Port(conf.devp2pTcpPort + conf.portsShift),
udpPort: Port(udpPort + conf.portsShift))
(nodev2ExtIp, nodev2ExtPort, _) = setupNat(conf.nat, clientId,
Port(uint16(conf.libp2pTcpPort) + conf.portsShift),
Port(uint16(udpPort) + conf.portsShift))
(nodev2ExtIp, nodev2ExtPort, _) = natRes2.get()

# Topic interest and bloom
var topicInterest: Option[seq[waku_protocol.Topic]]
Expand Down
48 changes: 1 addition & 47 deletions apps/wakunode2/app.nim
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ import
libp2p/protocols/pubsub/gossipsub,
libp2p/peerid,
eth/keys,
eth/net/nat,
json_rpc/rpcserver,
presto,
metrics,
metrics/chronos_httpserver
import
../../waku/common/utils/nat,
../../waku/common/sqlite,
../../waku/v2/waku_core,
../../waku/v2/waku_node,
Expand Down Expand Up @@ -317,51 +317,6 @@ proc setupDyamicBootstrapNodes*(app: var App): AppResult[void] =

## Init waku node instance

proc setupNat(natConf, clientId: string, tcpPort, udpPort: Port):
AppResult[tuple[ip: Option[ValidIpAddress], tcpPort: Option[Port], udpPort: Option[Port]]] {.gcsafe.} =

let strategy = case natConf.toLowerAscii():
of "any": NatAny
of "none": NatNone
of "upnp": NatUpnp
of "pmp": NatPmp
else: NatNone

var endpoint: tuple[ip: Option[ValidIpAddress], tcpPort: Option[Port], udpPort: Option[Port]]

if strategy != NatNone:
let extIp = getExternalIP(strategy)
if extIP.isSome():
endpoint.ip = some(ValidIpAddress.init(extIp.get()))
# RedirectPorts in considered a gcsafety violation
# because it obtains the address of a non-gcsafe proc?
var extPorts: Option[(Port, Port)]
try:
extPorts = ({.gcsafe.}: redirectPorts(tcpPort = tcpPort,
udpPort = udpPort,
description = clientId))
except CatchableError:
# TODO: nat.nim Error: can raise an unlisted exception: Exception. Isolate here for now.
error "unable to determine external ports"
extPorts = none((Port, Port))

if extPorts.isSome():
let (extTcpPort, extUdpPort) = extPorts.get()
endpoint.tcpPort = some(extTcpPort)
endpoint.udpPort = some(extUdpPort)

else: # NatNone
if not natConf.startsWith("extip:"):
return err("not a valid NAT mechanism: " & $natConf)

try:
# any required port redirection is assumed to be done by hand
endpoint.ip = some(ValidIpAddress.init(natConf[6..^1]))
except ValueError:
return err("not a valid IP address: " & $natConf[6..^1])

return ok(endpoint)

proc initNode(conf: WakuNodeConf,
rng: ref HmacDrbgContext,
peerStore: Option[WakuPeerStorage],
Expand Down Expand Up @@ -401,7 +356,6 @@ proc initNode(conf: WakuNodeConf,

let (extIp, extTcpPort, _) = natRes.get()


let
dns4DomainName = if conf.dns4DomainName != "": some(conf.dns4DomainName)
else: none(string)
Expand Down
12 changes: 9 additions & 3 deletions examples/v1/example.nim
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,16 @@ import
const clientId = "Waku example v1"

proc run(config: WakuNodeConf, rng: ref HmacDrbgContext) =

let natRes = setupNat(config.nat, clientId,
Port(config.tcpPort + config.portsShift),
Port(config.udpPort + config.portsShift))
if natRes.isErr():
fatal "setupNat failed", error = natRes.error
quit(1)

# Set up the address according to NAT information.
let (ipExt, tcpPortExt, udpPortExt) = setupNat(config.nat, clientId,
Port(config.tcpPort + config.portsShift),
Port(config.udpPort + config.portsShift))
let (ipExt, tcpPortExt, udpPortExt) = natRes.get()
# TODO: EthereumNode should have a better split of binding address and
# external address. Also, can't have different ports as it stands now.
let address = if ipExt.isNone():
Expand Down
49 changes: 1 addition & 48 deletions library/libwaku.nim
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import
chronicles,
chronos,
libp2p/crypto/secp,
stew/shims/net,
eth/net/nat as todo_delete_this_module
stew/shims/net
import
../vendor/nim-libp2p/libp2p/crypto/crypto,
../../waku/common/utils/nat,
Expand Down Expand Up @@ -81,52 +80,6 @@ proc errResp(message: string): string =
# }
return $(%* { "error": message })

proc setupNat(natConf, clientId: string, tcpPort, udpPort: Port):
Result[tuple[ip: Option[ValidIpAddress], tcpPort: Option[Port], udpPort: Option[Port]], string] {.gcsafe.} =
# TODO reorganize all setupNat calls and use only one commom proc

let strategy = case natConf.toLowerAscii():
of "any": NatAny
of "none": NatNone
of "upnp": NatUpnp
of "pmp": NatPmp
else: NatNone

var endpoint: tuple[ip: Option[ValidIpAddress], tcpPort: Option[Port], udpPort: Option[Port]]

if strategy != NatNone:
let extIp = getExternalIP(strategy)
if extIP.isSome():
endpoint.ip = some(ValidIpAddress.init(extIp.get()))
# RedirectPorts in considered a gcsafety violation
# because it obtains the address of a non-gcsafe proc?
var extPorts: Option[(Port, Port)]
try:
extPorts = ({.gcsafe.}: redirectPorts(tcpPort = tcpPort,
udpPort = udpPort,
description = clientId))
except CatchableError:
# TODO: nat.nim Error: can raise an unlisted exception: Exception. Isolate here for now.
error "unable to determine external ports"
extPorts = none((Port, Port))

if extPorts.isSome():
let (extTcpPort, extUdpPort) = extPorts.get()
endpoint.tcpPort = some(extTcpPort)
endpoint.udpPort = some(extUdpPort)

else: # NatNone
if not natConf.startsWith("extip:"):
return err("not a valid NAT mechanism: " & $natConf)

try:
# any required port redirection is assumed to be done by hand
endpoint.ip = some(ValidIpAddress.init(natConf[6..^1]))
except ValueError:
return err("not a valid IP address: " & $natConf[6..^1])

return ok(endpoint)

proc parseConfig(config: ConfigNode,
privateKey: var PrivateKey,
netConfig: var NetConfig,
Expand Down
8 changes: 4 additions & 4 deletions tests/v1/test_waku_connect.nim
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ procSuite "Waku connections":
p2 = await n2.rlpxConnect(newNode(n3.toENode()))
p3 = await n4.rlpxConnect(newNode(n3.toENode()))
check:
p1.isNil
p2.isNil == false
p3.isNil == false
p1.isErr() == true
p2.isErr() == false
p3.isErr() == false

asyncTest "Filters with encryption and signing":
var node1 = setupTestNode(rng, Waku)
Expand Down Expand Up @@ -386,7 +386,7 @@ procSuite "Waku connections":

ln2.startListening()
let peer = await ln1.rlpxConnect(newNode(ln2.toENode()))
check peer.isNil == true
check peer.isErr() == true

asyncTest "Waku set-topic-interest":
var
Expand Down
2 changes: 1 addition & 1 deletion tests/whisper/test_shh_connect.nim
Original file line number Diff line number Diff line change
Expand Up @@ -326,4 +326,4 @@ procSuite "Whisper connections":

ln2.startListening()
let peer = await ln1.rlpxConnect(newNode(ln2.toENode()))
check peer.isNil == true
check peer.isErr() == true
2 changes: 1 addition & 1 deletion vendor/nim-eth
Submodule nim-eth updated 67 files
+0 −82 .appveyor.yml
+5 −5 .github/workflows/ci.yml
+0 −49 .travis.yml
+3 −9 eth.nimble
+2 −5 eth/common/eth_hash.nim
+5 −8 eth/common/eth_types.nim
+7 −1 eth/common/eth_types_json_serialization.nim
+9 −4 eth/common/eth_types_rlp.nim
+3 −7 eth/db/kvstore.nim
+2 −6 eth/db/kvstore_sqlite3.nim
+3 −2 eth/keyfile/keyfile.nim
+1 −1 eth/keyfile/uuid.nim
+59 −15 eth/keys.nim
+7 −9 eth/net/nat.nim
+2 −2 eth/net/utils.nim
+5 −5 eth/p2p.nim
+7 −7 eth/p2p/auth.nim
+14 −14 eth/p2p/discovery.nim
+3 −3 eth/p2p/discoveryv5/encoding.nim
+5 −5 eth/p2p/discoveryv5/enr.nim
+12 −1 eth/p2p/discoveryv5/hkdf.nim
+2 −2 eth/p2p/discoveryv5/ip_vote.nim
+10 −2 eth/p2p/discoveryv5/lru.nim
+2 −2 eth/p2p/discoveryv5/messages.nim
+5 −5 eth/p2p/discoveryv5/messages_encoding.nim
+2 −2 eth/p2p/discoveryv5/node.nim
+1 −1 eth/p2p/discoveryv5/nodes_verification.nim
+4 −4 eth/p2p/discoveryv5/protocol.nim
+10 −0 eth/p2p/discoveryv5/random2.nim
+8 −5 eth/p2p/discoveryv5/routing_table.nim
+2 −2 eth/p2p/discoveryv5/sessions.nim
+7 −7 eth/p2p/ecies.nim
+6 −2 eth/p2p/enode.nim
+25 −17 eth/p2p/kademlia.nim
+2 −2 eth/p2p/p2p_backends_helpers.nim
+1 −1 eth/p2p/p2p_protocol_dsl.nim
+65 −21 eth/p2p/peer_pool.nim
+18 −11 eth/p2p/private/p2p_types.nim
+191 −90 eth/p2p/rlpx.nim
+2 −2 eth/p2p/rlpxcrypt.nim
+1 −1 eth/trie/binaries.nim
+23 −6 eth/trie/db.nim
+260 −104 eth/trie/hexary.nim
+2 −2 eth/trie/hexary_proof_verification.nim
+2 −2 eth/utp/clock_drift_calculator.nim
+2 −2 eth/utp/delay_histogram.nim
+17 −12 eth/utp/growable_buffer.nim
+2 −2 eth/utp/ledbat_congestion_control.nim
+54 −53 eth/utp/packets.nim
+8 −5 eth/utp/utp.nim
+2 −2 eth/utp/utp_discv5_protocol.nim
+7 −5 eth/utp/utp_protocol.nim
+25 −20 eth/utp/utp_router.nim
+121 −100 eth/utp/utp_socket.nim
+2 −2 eth/utp/utp_utils.nim
+5 −1 tests/fuzzing/rlpx/thunk.nim
+5 −5 tests/keys/test_keys.nim
+4 −4 tests/p2p/test_discoveryv5.nim
+1 −1 tests/p2p/test_discoveryv5_encoding.nim
+6 −4 tests/p2p/test_protocol_handlers.nim
+4 −1 tests/p2p/test_rlpx_thunk.nim
+28 −3 tests/rlp/test_api_usage.nim
+1 −1 tests/trie/test_branches_utils.nim
+2 −2 tests/trie/test_hexary_proof.nim
+5 −5 tests/utp/test_buffer.nim
+56 −64 tests/utp/test_packets.nim
+1 −1 tests/utp/test_utils.nim
91 changes: 44 additions & 47 deletions waku/common/utils/nat.nim
Original file line number Diff line number Diff line change
@@ -1,70 +1,67 @@

when (NimMajor, NimMinor) < (1, 4):
{.push raises: [Defect].}
else:
{.push raises: [].}

import
std/[strutils, options],
chronicles, stew/shims/net as stewNet,
eth/net/nat
std/[options, strutils]
import
chronicles,
eth/net/nat,
stew/results,
stew/shims/net,
nativesockets

logScope:
topics = "nat"

proc setupNat*(natConf, clientId: string, tcpPort, udpPort: Port):
tuple[ip: Option[ValidIpAddress],
tcpPort: Option[Port],
udpPort: Option[Port]] {.gcsafe, deprecated:
"Unsafe: this proc quits the app if something is not ok".} =
proc setupNat*(natConf, clientId: string,
tcpPort, udpPort: Port):
Result[tuple[ip: Option[ValidIpAddress],
tcpPort: Option[Port],
udpPort: Option[Port]], string]
{.gcsafe.} =

var
endpoint: tuple[ip: Option[ValidIpAddress],
tcpPort: Option[Port],
udpPort: Option[Port]]
nat: NatStrategy
let strategy = case natConf.toLowerAscii():
of "any": NatAny
of "none": NatNone
of "upnp": NatUpnp
of "pmp": NatPmp
else: NatNone

case natConf.toLowerAscii:
of "any":
nat = NatAny
of "none":
nat = NatNone
of "upnp":
nat = NatUpnp
of "pmp":
nat = NatPmp
else:
if natConf.startsWith("extip:"):
try:
# any required port redirection is assumed to be done by hand
endpoint.ip = some(ValidIpAddress.init(natConf[6..^1]))
nat = NatNone
except ValueError:
error "not a valid IP address", address = natConf[6..^1]
quit QuitFailure
else:
error "not a valid NAT mechanism", value = natConf
quit QuitFailure
var endpoint: tuple[ip: Option[ValidIpAddress], tcpPort: Option[Port], udpPort: Option[Port]]

if nat != NatNone:
let extIp = getExternalIP(nat)
if extIP.isSome:
endpoint.ip = some(ValidIpAddress.init extIp.get)
# TODO redirectPorts in considered a gcsafety violation
if strategy != NatNone:
let extIp = getExternalIP(strategy)
if extIP.isSome():
endpoint.ip = some(ValidIpAddress.init(extIp.get()))
# RedirectPorts in considered a gcsafety violation
# because it obtains the address of a non-gcsafe proc?
var extPorts: Option[(Port, Port)]
try:
extPorts = ({.gcsafe.}:
redirectPorts(tcpPort = tcpPort,
udpPort = udpPort,
description = clientId))
except Exception:
# @TODO: nat.nim Error: can raise an unlisted exception: Exception. Isolate here for now.
extPorts = ({.gcsafe.}: redirectPorts(tcpPort = tcpPort,
udpPort = udpPort,
description = clientId))
except CatchableError:
# TODO: nat.nim Error: can raise an unlisted exception: Exception. Isolate here for now.
error "unable to determine external ports"
extPorts = none((Port, Port))

if extPorts.isSome:
if extPorts.isSome():
let (extTcpPort, extUdpPort) = extPorts.get()
endpoint.tcpPort = some(extTcpPort)
endpoint.udpPort = some(extUdpPort)

return endpoint
else: # NatNone
if not natConf.startsWith("extip:"):
return err("not a valid NAT mechanism: " & $natConf)

try:
# any required port redirection is assumed to be done by hand
endpoint.ip = some(ValidIpAddress.init(natConf[6..^1]))
except ValueError:
return err("not a valid IP address: " & $natConf[6..^1])

return ok(endpoint)

Loading