From a4861df57360779c774e09e8ebd63488afdbe30c Mon Sep 17 00:00:00 2001 From: kdeme <7857583+kdeme@users.noreply.github.com> Date: Thu, 11 Dec 2025 01:28:27 +0100 Subject: [PATCH] Portal: Add IPv6 support --- portal/client/nimbus_portal_client.nim | 71 ++++++++++++++++++--- portal/client/nimbus_portal_client_conf.nim | 18 ++++-- portal/network/wire/portal_protocol.nim | 4 +- portal/scripts/launch_local_testnet.sh | 42 +++++++++++- vendor/nim-eth | 2 +- 5 files changed, 115 insertions(+), 22 deletions(-) diff --git a/portal/client/nimbus_portal_client.nim b/portal/client/nimbus_portal_client.nim index 3e730ce01a..ac2acb632e 100644 --- a/portal/client/nimbus_portal_client.nim +++ b/portal/client/nimbus_portal_client.nim @@ -102,16 +102,64 @@ proc run(portalClient: PortalClient, config: PortalConf) {.raises: [CatchableErr ) ## Network configuration + ## + ## - bind IP is dual stack: + ## - both enrIpv6Address and nat:extip set: use those + ## - only nat:extip set: use nat:extip for v4 and getBestRoute for v6 + ## - only enrIpv6Address set: use enrIpv6Address for v6 and getBestRoute + nat for v4 + ## - none set: use getBestRoute + nat for v4 and getBestRoute for v6 + ## - bind IP is only v4: + ## - nat:extip set: use nat:extip for v4 + ## - none set: use getBestRoute + nat for v4 + ## - bind IP is only v6: + ## - enrIpv6Address set: use enrIpv6Address for v6 + ## - none set: use getBestRoute for v6 + let - bindIp = config.listenAddress udpPort = Port(config.udpPort) - (extIp, extPorts) = setupAddress( - config.nat, - config.listenAddress, - @[(port: udpPort, protocol: PortProtocol.UDP)], - "nimbus_portal_client", - ) - extUdpPort = extPorts[0].toPort() + listenAddress = + if config.listenAddress.isSome(): + config.listenAddress.get() + else: + getAutoAddress(Port(0)).toIpAddress() + + (extIp4, extIp6, extUdpPort) = + if listenAddress == AnyAddress6.toIpAddress(): + let + extIp6 = + if config.enrIpv6Address.isSome(): + Opt.some(config.enrIpv6Address.get()) + else: + getRoutePrefSrcv6(listenAddress) + + (extIp4, extPorts) = setupAddress( + config.nat, + listenAddress, + @[(port: udpPort, protocol: PortProtocol.UDP)], + "nimbus_portal_client", + ) + extUdpPort = extPorts[0].toPort() + + (extIp4, extIp6, extUdpPort) + elif listenAddress.family == IpAddressFamily.IPv6: + let extIp6 = + if config.enrIpv6Address.isSome(): + Opt.some(config.enrIpv6Address.get()) + else: + getRoutePrefSrcv6(listenAddress) + (Opt.none(IpAddress), extIp6, Opt.some(udpPort)) + else: # listenAddress.family == IpAddressFamily.IPv4 + let + (extIp4, extPorts) = setupAddress( + config.nat, + listenAddress, + @[(port: udpPort, protocol: PortProtocol.UDP)], + "nimbus_portal_client", + ) + extUdpPort = extPorts[0].toPort() + + (extIp4, Opt.none(IpAddress), extUdpPort) + (netkey, newNetKey) = if config.networkKey.isSome(): (config.networkKey.get(), true) @@ -146,7 +194,10 @@ proc run(portalClient: PortalClient, config: PortalConf) {.raises: [CatchableErr ) d = newProtocol( netkey, - extIp, + extIp4, + Opt.none(Port), + extUdpPort, + extIp6, Opt.none(Port), extUdpPort, # Note: usage of the client field "c" is replaced with ping extensions client_info. @@ -157,7 +208,7 @@ proc run(portalClient: PortalClient, config: PortalConf) {.raises: [CatchableErr ], bootstrapRecords = bootstrapRecords, previousRecord = previousEnr, - bindIp = bindIp, + bindIp = Opt.some(listenAddress), bindPort = udpPort, enrAutoUpdate = config.enrAutoUpdate, config = discoveryConfig, diff --git a/portal/client/nimbus_portal_client_conf.nim b/portal/client/nimbus_portal_client_conf.nim index 1c49990417..1658499aac 100644 --- a/portal/client/nimbus_portal_client_conf.nim +++ b/portal/client/nimbus_portal_client_conf.nim @@ -25,9 +25,7 @@ import ../network/wire/portal_protocol_config const - defaultListenAddress* = (static parseIpAddress("0.0.0.0")) defaultAdminListenAddress* = (static parseIpAddress("127.0.0.1")) - defaultListenAddressDesc = $defaultListenAddress defaultAdminListenAddressDesc = $defaultAdminListenAddress defaultStorageCapacity* = 2000'u32 # 2 GB default @@ -77,11 +75,9 @@ type uint16 listenAddress* {. - defaultValue: defaultListenAddress, - defaultValueDesc: $defaultListenAddressDesc, - desc: "Listening address for the Discovery v5 traffic", - name: "listen-address" - .}: IpAddress + desc: "Listening address for the Discovery v5 traffic" + defaultValueDesc: "*" + name: "listen-address" .}: Option[IpAddress] network* {. desc: @@ -124,6 +120,14 @@ type name: "nat" .}: NatConfig + # Note: add enrIpv4Address option that would deprecate nat:extip + # Or have one enr-address that accepts multiple addresses? (ipv4/ipv6) + enrIpv6Address* {. + hidden, + desc: "Specifies the IPv6 address to be advertised in the node’s ENR", + name: "debug-enr-ipv6-address" + .}: Option[IpAddress] + enrAutoUpdate* {. defaultValue: false, desc: diff --git a/portal/network/wire/portal_protocol.nim b/portal/network/wire/portal_protocol.nim index 3657669547..ba3681617b 100644 --- a/portal/network/wire/portal_protocol.nim +++ b/portal/network/wire/portal_protocol.nim @@ -909,7 +909,7 @@ proc findNodes*( let records = ?recordsFromBytes(response.enrs) ok( - verifyNodesRecords(records, dst, enrsResultLimit, distances).filterIt( + verifyNodesRecords(records, dst, p.localNode, enrsResultLimit, distances).filterIt( not p.isBanned(it.id) ) ) @@ -987,7 +987,7 @@ proc findContent*( ) of enrsType: let records = ?recordsFromBytes(response.enrs) - let verifiedNodes = verifyNodesRecords(records, dst, enrsResultLimit) + let verifiedNodes = verifyNodesRecords(records, dst, p.localNode, enrsResultLimit) ok( FoundContent( diff --git a/portal/scripts/launch_local_testnet.sh b/portal/scripts/launch_local_testnet.sh index 2fb942ce9d..fbfa6b16eb 100755 --- a/portal/scripts/launch_local_testnet.sh +++ b/portal/scripts/launch_local_testnet.sh @@ -332,12 +332,50 @@ for NUM_NODE in $(seq 0 $(( NUM_NODES - 1 ))); do RPC="ws" fi + # Some testing here for now... + group=$(( NUM_NODE % 3 )) + + case "${group}" in + 0) + # Group 2: Dual stack + # Need to specifically set both extip because they are loopback addresses + EXTIP="127.0.0.1" + EXTIPV6="::1" + LISTEN_ADDRESS="::" # This is the default setting, so could also not set it + NETWORK_ARGS="\ + --listen-address="${LISTEN_ADDRESS}" \ + --nat:extip:"${EXTIP}" \ + --debug-enr-ipv6-address="${EXTIPV6}" \ + " + ;; + 1) + # Group 0: IPv4 only + EXTIP="127.0.0.1" + LISTEN_ADDRESS="127.0.0.1" # Could set 0.0.0.0 + NETWORK_ARGS="\ + --listen-address="${LISTEN_ADDRESS}" \ + --nat:extip:"${EXTIP}" \ + " + ;; + 2) + # Group 1: IPv6 only + # TODO: The nat:none should not be needed, based on IPv6 only bind address we should not set enr ipv4 + EXTIPV6="::1" + LISTEN_ADDRESS="::1" # Cannot set :: as this would also listen on IPv4 + NETWORK_ARGS="\ + --listen-address="${LISTEN_ADDRESS}" \ + --debug-enr-ipv6-address="${EXTIPV6}" \ + --nat:none \ + " + ;; + esac + # Running with bits-per-hop of 1 to make the lookups more likely requiring # to request to nodes over the network instead of having most of them in the # own routing table. + ./build/nimbus_portal_client \ - --listen-address:127.0.0.1 \ - --nat:extip:127.0.0.1 \ + ${NETWORK_ARGS} \ --log-level="${LOG_LEVEL}" \ --udp-port=$(( BASE_PORT + NUM_NODE )) \ --data-dir="${NODE_DATA_DIR}" \ diff --git a/vendor/nim-eth b/vendor/nim-eth index d86bb19440..47276a0cf9 160000 --- a/vendor/nim-eth +++ b/vendor/nim-eth @@ -1 +1 @@ -Subproject commit d86bb194402ac53a1dbd5e8a0e374c4da81afc6d +Subproject commit 47276a0cf9db05e0bdb7601927435f48cab9a3d3