nimbus-eth2
nimbus-eth2 copied to clipboard
Improve external ip / nat configuration
Current default is to use upnp / nat pmp to discover external ip and to configure the port forwarding ( =mapping). This is not useful for systems not behind NAT as thus not a great default for those configurations. Additionally, we might no want to recommend upnp first option either for those behind NAT?
Discoveryv5 can help figuring out the external IP when https://github.com/status-im/nim-eth/issues/207 gets implemented. When the user is behind NAT, it would still require for manual port mapping or to be used in combination with upnp/natpmp
Regarding the --nat option:
- The
--nat:extip:option can still be used for those that want to hard set the ip and not rely on peers. - The
noneoption could be implemented such that the routable interface is discovered and its ip address is used (if not local address). anywould be a mix, and could thus e.g. rely on upnp + discv5 or finding of routable interface + discv5.
Default could still be any option.
Defaults:
- In case of a system with a public IP (upnp could be run, but wouldn't do any thing.) It would be enough to have the IP discovered through discovery v5. Additionally, the routable interface could be searched for and its IP could be used, but this would not be necessary. No port forwarding is needed.
- In case of a system behind a NAT:
- Either we don't default to use upnp: Discovery finds public IP. Users need to be instructed to configure their router manual for the port forwarding
- Or we do allow for upnp to be used default: upnp finds public IP & does port mapping. Discovery checks ip and keeps it updated?
Other clients:
- prysm: flag for setting external ip https://docs.prylabs.network/docs/prysm-usage/p2p-host-ip#setting-the---p2p-host-ip-flag + instructions to do the port forwarding.
- lh: port forwarding default through upnp: https://lighthouse-book.sigmaprime.io/advanced_networking.html#nat-traversal-port-forwarding , also discovery v5 based external ip configuration.
To make clear, in the current code with current options, the only real viable options are:
Systems not behind NAT:
- use
--nat:extip:x.x.x.x
Systems behind NAT:
- use
--nat:extip:x.x.x.xand make manually configure port forwarding in the router/gateway. - use the default (or set
upnporpmp) and make sure this functionality is enabled in your router. Port forwarding and external ip discovery will happen automated (but more issues might occur because of this).
Thinking about it further, the any option (defaulted), should basically allow for any of the two cases to work automatically. However, not guaranteed of course and issues may arise. It would be more important to have proper INF and WRN logs for this and educate the users into these message (in the book).
This would mean still using the upnp/pmp solution default & also discv5 solution. Basically what LH is doing.
(And perhaps we can also implement the none solution from above as backup).
Thinking about it further, the any option (defaulted), should basically allow for any of the two cases to work automatically. However, not guaranteed of course and issues may arise. It would be more important to have proper INF and WRN logs for this and educate the users into these message (in the book). This would mean still using the upnp/pmp solution default & also discv5 solution. Basically what LH is doing.
As discussed offline, I think this is the cleanest approach.
I think before starting NAT mechanism we should check our interfaces and routing table using chronos utilities. This will allow us to detect situation when we already have public IP address and we do not need to start NAT mechanism.
https://github.com/status-im/nim-chronos/blob/master/tests/testnet.nim#L477-L482
Using getBestRoute with some globally available IP address will reveal you interface's address which will be used to connect to this IP address. If this IP address is globally routable you can use it as "extip".
The only problem is that currently chronos is missing primitive to check if specific IP is actually global routable IP address.
Using
getBestRoutewith some globally available IP address will reveal you interface's address which will be used to connect to this IP address. If this IP address is globally routable you can use it as "extip".
Great, this is what I meant with:
* the routable interface could be searched for and its IP could be used, but this would not be necessary.
I wasn't aware that there was a getBestRoute call available in chronos already. It does seems to require sending an actual message, is that correct? Is there a version that just checks routable interface?
@kdeme this call do not send any messages, it just query OS for the BEST route. Because there could be many routable interfaces actually...
The bulk of this feature is being implemented here: https://github.com/status-im/nim-eth/pull/323
The current (new) logic is as follows:
any:- If a specific (not 0.0.0.0) listener address (
bindIP) is given, and this IP is public, use this IP in the ENR (advertised IP). (not sure if we should still help the user here and check if this is also best route IP, or allow for more exotic uses). - Else, check for best route IP and check if this IP is public. If so, use this IP in the ENR (advertised IP).
- Else, try UPnP and NAT-PMP to discover IP and do port mapping on gateway.
- If 1, 2 and 3 fails, no IP is set and warning is given.
- If a specific (not 0.0.0.0) listener address (
upnp: Try UPnP to discover IP and do port mapping. If it fails, no IP is set, warning is given.pmp: Try NAT-PMP to discover IP and do port mapping. If it fails, no IP is set, warning is given.none:- If a specific (not 0.0.0.0) listener address (
bindIP) is given, and this IP is public, use this IP in the ENR (advertised IP). (not sure if we should still help the user here and check if this is also best route IP, or allow for more exotic uses). Else warn that--noneoption should not be used. - Else, check for best route IP and check if this IP is public. If so, use this IP in the ENR (advertised IP). Else warn that
--noneoption should not be used.
- If a specific (not 0.0.0.0) listener address (
extip:<IP address>: Use this IP in ENR. Nothing else is done. This will allow for private IP usage also. (We use this in testing)
Additionally, external IP discovery should be added in discv5 protocol (and allowed to be disabled). This will help with dynamic IP that get changed and also in the case that external IP can not be found correctly (e.g. Container / VM usages or cases where the best route IP findings would go wrong?).
Now, some questions:
- The current
--nat:...options stem from geth. Are these clear enough for the user? I think they are not very self explanatory and it might be rather difficult to understand what to use here. On the other hand, the default isanyand that should be fine for the typical user. But we could decide to change to more individual based options however too, e.g. with the same default results (and keep the current options also available for a certain period for backwards compatibility) - Currently port mapping is always 1:1. Should we also support a different port mapping on host device <-> external ports. (Perhaps this can be useful for certain Docker/VM setups? LH supports this from what I can see in the options)
Some different use cases:
- User running nimbus on device behind gateway (NAT): Will need to "discover" external IP and setup port forwarding
- User running nimbus on server with public IP (for example cloud offerings): public IP to be used.
- User running nimbus on container on server with public IP: Will need to "discover" external IP, port mapping would normally be done at setup and can be 1:1 but also can be different ports
- User running nimbus on container on device behind gateway (NAT): Will need to "discover" external IP and setup port forwarding.
- ...
I'm leaving out IPv6 cases for now.
From my experience PMP works better than UPNP because PMP can give you other external port in case the requested one is already taken. But seems nimbus is trying UPNP first and errors out without fallback to PMP.