mirror of https://github.com/status-im/nim-eth.git
nat: getPublicRoutePrefSrcOrExternalIP() (#496)
* nat: getPublicRoutePrefSrcOrExternalIP() And some refactoring to minimise code duplication.
This commit is contained in:
parent
5a78e2fe23
commit
ea03e66485
|
@ -23,6 +23,13 @@ type
|
||||||
NatPmp
|
NatPmp
|
||||||
NatNone
|
NatNone
|
||||||
|
|
||||||
|
PrefSrcStatus = enum
|
||||||
|
NoRoutingInfo
|
||||||
|
PrefSrcIsPublic
|
||||||
|
PrefSrcIsPrivate
|
||||||
|
BindAddressIsPublic
|
||||||
|
BindAddressIsPrivate
|
||||||
|
|
||||||
const
|
const
|
||||||
UPNP_TIMEOUT = 200 # ms
|
UPNP_TIMEOUT = 200 # ms
|
||||||
PORT_MAPPING_INTERVAL = 20 * 60 # seconds
|
PORT_MAPPING_INTERVAL = 20 * 60 # seconds
|
||||||
|
@ -102,6 +109,42 @@ proc getExternalIP*(natStrategy: NatStrategy, quiet = false): Option[IpAddress]
|
||||||
error "parseIpAddress() exception", err = e.msg
|
error "parseIpAddress() exception", err = e.msg
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# 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: ValidIpAddress): (Option[ValidIpAddress], PrefSrcStatus) =
|
||||||
|
let bindAddress = initTAddress(bindIp, Port(0))
|
||||||
|
|
||||||
|
if bindAddress.isAnyLocal():
|
||||||
|
let ip = 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(ValidIpAddress), NoRoutingInfo)
|
||||||
|
elif ip.get().isPublic():
|
||||||
|
return (some(ip.get()), PrefSrcIsPublic)
|
||||||
|
else:
|
||||||
|
return (none(ValidIpAddress), PrefSrcIsPrivate)
|
||||||
|
elif bindAddress.isPublic():
|
||||||
|
return (some(ValidIpAddress.init(bindIp)), BindAddressIsPublic)
|
||||||
|
else:
|
||||||
|
return (none(ValidIpAddress), BindAddressIsPrivate)
|
||||||
|
|
||||||
|
# Try to detect a public IP assigned to this host, before trying NAT traversal.
|
||||||
|
proc getPublicRoutePrefSrcOrExternalIP*(natStrategy: NatStrategy, bindIp: ValidIpAddress, quiet = true): Option[ValidIpAddress] =
|
||||||
|
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(ValidIpAddress.init(extIp.get))
|
||||||
|
|
||||||
proc doPortMapping(tcpPort, udpPort: Port, description: string): Option[(Port, Port)] {.gcsafe.} =
|
proc doPortMapping(tcpPort, udpPort: Port, description: string): Option[(Port, Port)] {.gcsafe.} =
|
||||||
var
|
var
|
||||||
extTcpPort: Port
|
extTcpPort: Port
|
||||||
|
@ -319,46 +362,24 @@ proc setupAddress*(natConfig: NatConfig, bindIp: ValidIpAddress,
|
||||||
|
|
||||||
case natConfig.nat:
|
case natConfig.nat:
|
||||||
of NatAny:
|
of NatAny:
|
||||||
let bindAddress = initTAddress(bindIp, Port(0))
|
let (prefSrcIp, prefSrcStatus) = getRoutePrefSrc(bindIp)
|
||||||
if bindAddress.isAnyLocal():
|
|
||||||
let ip = getRouteIpv4()
|
case prefSrcStatus:
|
||||||
if ip.isErr():
|
of NoRoutingInfo, PrefSrcIsPublic, BindAddressIsPublic:
|
||||||
# No route was found, log error and continue without IP.
|
return (prefSrcIp, some(tcpPort), some(udpPort))
|
||||||
error "No routable IP address found, check your network connection",
|
of PrefSrcIsPrivate, BindAddressIsPrivate:
|
||||||
error = ip.error
|
|
||||||
return (none(ValidIpAddress), some(tcpPort), some(udpPort))
|
|
||||||
elif ip.get().isPublic():
|
|
||||||
return (some(ip.get()), some(tcpPort), some(udpPort))
|
|
||||||
else:
|
|
||||||
# Best route IP is not public, might be an internal network and the
|
|
||||||
# node is either behind a gateway with NAT or for example a container
|
|
||||||
# or VM bridge (or both). Lets try UPnP and NAT-PMP for the case where
|
|
||||||
# the node is behind a gateway with UPnP or NAT-PMP support.
|
|
||||||
return setupNat(natConfig.nat, tcpPort, udpPort, clientId)
|
return setupNat(natConfig.nat, tcpPort, udpPort, clientId)
|
||||||
elif bindAddress.isPublic():
|
|
||||||
# When a specific public interface is provided, use that one.
|
|
||||||
return (some(ValidIpAddress.init(bindIp)), some(tcpPort), some(udpPort))
|
|
||||||
else:
|
|
||||||
return setupNat(natConfig.nat, tcpPort, udpPort, clientId)
|
|
||||||
of NatNone:
|
of NatNone:
|
||||||
let bindAddress = initTAddress(bindIp, Port(0))
|
let (prefSrcIp, prefSrcStatus) = getRoutePrefSrc(bindIp)
|
||||||
if bindAddress.isAnyLocal():
|
|
||||||
let ip = getRouteIpv4()
|
case prefSrcStatus:
|
||||||
if ip.isErr():
|
of NoRoutingInfo, PrefSrcIsPublic, BindAddressIsPublic:
|
||||||
# No route was found, log error and continue without IP.
|
return (prefSrcIp, some(tcpPort), some(udpPort))
|
||||||
error "No routable IP address found, check your network connection",
|
of PrefSrcIsPrivate:
|
||||||
error = ip.error
|
|
||||||
return (none(ValidIpAddress), some(tcpPort), some(udpPort))
|
|
||||||
elif ip.get().isPublic():
|
|
||||||
return (some(ip.get()), some(tcpPort), some(udpPort))
|
|
||||||
else:
|
|
||||||
error "No public IP address found. Should not use --nat:none option"
|
error "No public IP address found. Should not use --nat:none option"
|
||||||
return (none(ValidIpAddress), some(tcpPort), some(udpPort))
|
return (none(ValidIpAddress), some(tcpPort), some(udpPort))
|
||||||
elif bindAddress.isPublic():
|
of BindAddressIsPrivate:
|
||||||
# When a specific public interface is provided, use that one.
|
error "Bind IP is not a public IP address. Should not use --nat:none option"
|
||||||
return (some(ValidIpAddress.init(bindIp)), some(tcpPort), some(udpPort))
|
return (none(ValidIpAddress), some(tcpPort), some(udpPort))
|
||||||
else:
|
|
||||||
error "Bind IP is not a public IP address. Should not use --nat:none option"
|
|
||||||
return (none(ValidIpAddress), some(tcpPort), some(udpPort))
|
|
||||||
of NatUpnp, NatPmp:
|
of NatUpnp, NatPmp:
|
||||||
return setupNat(natConfig.nat, tcpPort, udpPort, clientId)
|
return setupNat(natConfig.nat, tcpPort, udpPort, clientId)
|
||||||
|
|
Loading…
Reference in New Issue