-
Notifications
You must be signed in to change notification settings - Fork 1k
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
core: introduce a unified Transport::dial
interface
#4226
Comments
Havent had the chance to look over AutoNATv2 spec so i cannot comment to much on that part yet, but I do like the idea of merging both functions together and simplifying it here in |
The probing connection from the AutoNAT server to the local node needs to use a new port. |
Indeed, this is a nice benefit!
Note that I am not sure port-reuse should be a best-effort request. E.g. in the case of
I am fine with exploring the removal of Will remove the |
Creating a new port binding will always work so the AutoNAT case is handled. I agree with you that making it best-effort is probably not good and only complicates the interfaces. In the case of dcutr, we should probably check whether we actually have a listening interface and if we don't then we shouldn't even attempt to hole punch! That will solve this problem. libp2p/specs#562 has some interesting conversation around this problem too. go and nim both don't treat it as a connection-level setting but rather have an algorithm on how they decide what to do for new connections. I think we can copy that reasonably well with this approach. We can't access the transport directly but making it a connection-level setting allows us to pick the default in the Thoughts? |
An AutoNAT client needs to suggest external address candidates to the AutoNAT server in order for the server to dial the client on those candidates. Thus far we plan to collect the candidates via With the above strategy, namely to use a new port binding when one has an external address, As a result, unless I am missing something, I suggest to always reuse port bindings default. |
That is only if you don't establish connections where you override the port binding strategy. We can only reuse a port binding if we have a listen address, thus the default would be even more nuanced: Always reuse port bindings unless we don't have a listen address, then the default should be to create a new one. (The We could also be even smarter and track, how many connections we have to that peer already. If the user explicitly requests a new connection ( How about the following:
|
👍 if I am not mistaken that is the status quo in
👍 status quo on
Works for me, though as you say, "in the future", i.e. I don't think this should be a priority. |
Do we currently fail to create a new connection when port-reuse is enabled and we already have one? If yes, then I am happy to keep that behaviour in the first iteration. |
|
So the desired new interface for Transport has a configuration option, that has two options:
And just to make sure I understand everything correctly: |
Exactly right @umgefahren.
You can extend rust-libp2p/swarm/src/dial_opts.rs Lines 29 to 48 in 91fb6a1
|
I would have made the transports "dumb" and only pass two options on:
The smart behaviour would live in Uplifting this behaviour to the swarm will allow us to do #3953. For that, we need to know whether a given connection allocated a new port or not. I don't want to unnecessarily blow up the scope of this but we should work towards the correct API in my opinion. |
I have written up a first implementation of the new design. Please give feedback and I would propose continuing the design discussion on #4568. |
Resolves: libp2p#4226. Resolves: libp2p#3953. Resolves: libp2p#3889. Pull-Request: libp2p#4568.
This issue describes a design proposal on how we can modify the
Transport
trait to accommodate for the needs of AutoNATv2 and at the same time, can simplify address translation.In a nutshell, we need a way to control the port for outgoing connections.
Context
Socket implementations for TCP and UDP - on some operating systems - support a thing called "port reuse". That is, instead of allocating a new port for an outgoing connection, they can create a socket on a port that is already in use for listening. This allows for techniques like hole punching to work: NAT devices will allow for ingress traffic from an IP that we have previously sent a TCP packet too.
In AutoNATv2, we want to explicitly avoid this. To test whether an address is publicly reachable we need to avoid accidental hole-punching. By connecting to an AutoNAT server, we might establish a mapping for ingress traffic in a NAT device and for that particular peer, we appear to be reachable when in reality, the port is not forwarded and a connection attempt by any other peer will fail.
To support AutoNATv2 as a
NetworkBehaviour
, it needs to create a connection to the autonat server where we explicitly want a new port. Currently, we have thedial
anddial_as_listener
functions. We could now add adial_with_new_port
function but this doesn't seem to scale particularly well.In addition, there is also issue #3953 about address translation. For address translation to work correctly, we need to know whether an outgoing connection created an ephemeral port or is reusing an existing one.
The proposal
We can solve both of these problems by merging the two existing
dial
functions onTransport
and instead expose a singledial
that supports two configuration options:The default for these options would be:
The exciting thing is that we can use this model to solve several problems:
address_translation
fromTransport
because theSwarm
will know, which connections were established with a resused port and can thus perform the mapping correctly.dial
functions onTransport
Further considerations
With the reuse of an existing port as the default, new connections will fail if we don't have a listening port yet. We don't want outgoing connections to fail by default, meaning the transports should probably fall back to creating a new socket just for dialing in that case. This however means that each new connection should expose, whether it is using an ephemeral or an existing port and the "config" passed into
Transport::dial
is a mere "request" to the transport that might not get fulfilled.This would allow
Swarm
to correctly identify, whether a connection did end up reusing a port and can thus correctly implementaddress_translation
which is required for identify to report the correct observed address.The text was updated successfully, but these errors were encountered: