libp2p / specs

Technical specifications for the libp2p networking stack
https://libp2p.io
1.56k stars 273 forks source link

add a spec for autonat #362

Closed marten-seemann closed 2 years ago

mxinden commented 3 years ago

On address prioritization

I read quite a bunch of NAT traversal related RFCs (4787, 5128, 4614, 5382, 7604, 7857, 5389, 8445)

At the current state of Project Flare, I think the only thing useful would be RFC8445, more specifically section 5 on (address) candidate selection.

https://datatracker.ietf.org/doc/html/rfc8445#section-5

They differentiate in 4 candidate types, depending on the mechanism the address was discovered through:

Section 5.1.2.1 then follows with a formula to prioritize among the many addresses depending on their type.

Would (a) the candidate type differentiation and (b) the proposed prioritization formula be something worth adopting in this specification or our implementations? What do you think @marten-seemann?

elenaf9 commented 2 years ago

One case just popped up in my mind where I am not sure how to handle it for rust-autonat: Let's assume Peer A wants to know from Peer B whether they (A) can be dialed directly. At the moment A is listening via a relay and A and B have already established a connection where B is the Dialer, therefore B observes A with a relay address. If I understand the Golang implementation correctly, B would reject the DialRequest, and A would have to establish a new one to B where A is the dialer. Would it make sense that a DialRequest in general should only be sent on connections where the sender is also the dialer, so that the receiver observes the correct address? And what should happen if B can not be dialed directly either?

mxinden commented 2 years ago

Would it make sense that a DialRequest in general should only be sent on connections where the sender is also the dialer

In my eyes one should be able to send DialRequests both via a connection where one is the dialer and via a connection where one is the listener. Given that connection establishment is expensive, reusing existing connections, whether one is a dialer or listener, sounds reasonable.

so that the receiver observes the correct address?

I am not sure I follow. Both as a dialer and as a listener of a connection one knows the address of the remote peer. One might not observe the right port, though the AutoNAT protocol does not discriminate on the port, only on the IP. Am I missing something?

And what should happen if B can not be dialed directly either?

Off the top of my head, I don't think the overhead of establishing a new relayed connection is worth it, i.e. in case B can not be dialed directly, but only via a relay, I don't think they are a valid AutoNAT partner. I do think it is worth trying on an already established relayed connection.

mxinden commented 2 years ago

And what should happen if B can not be dialed directly either?

Off the top of my head, I don't think the overhead of establishing a new relayed connection is worth it, i.e. in case B can not be dialed directly, but only via a relay, I don't think they are a valid AutoNAT partner. I do think it is worth trying on an already established relayed connection.

Sorry. This obviously conflicts with https://github.com/libp2p/specs/pull/362/commits/048bdbfe2439058c355db612eac717fc8a46acbb. My bad. One should not send nor accept DialRequests via relayed connections.

elenaf9 commented 2 years ago

I am not sure I follow. Both as a dialer and as a listener of a connection one knows the address of the remote peer. One might not observe the right port, though the AutoNAT protocol does not discriminate on the port, only on the IP. Am I missing something?

Maybe I understand the relay protocol wrong. I assumed that if a peer A is listening via a relay, a remote observe them:

Is this the case, or does the remote observe them at a relayed address in both cases?

mxinden commented 2 years ago

I assumed that if a peer A is listening via a relay, a remote observe them:

  • at the relayed address if A is the Listener -> remote would reject the autonat request

Correct. The remote would observe A at /<relay-address>/p2p-circuit/p2p/<peer-id-of-A>.

  • at a "direct"/ non-relayed address if A is a Dialer, because then the connection is not relayed -> remote would do the autonat request

In the case where the remote is public and A can dial the remote directly, the remote would observe A as /<address-of-A>. The remote should then not reject the autonat request.

In the case where the remote is not publicly reachable and is as well listening via some relay, A would dial the remote via that relay. I don't think we defined how the remote would observe A. The remote would only learn the peer ID and not the address of A through the circuit relay v2 protocol:

the peer field contains a Peer struct with the peer ID of the connection initiator.

https://github.com/libp2p/specs/blob/master/relay/circuit-v2.md#stop-protocol

The remote could infer the address of A through some other means, e.g. retrieved via an identify protocol exchange. I would argue that the remote can not trust such address and thus should again reject the autonat request via the relayed address.

Does the above answer the question? Unfortunately this is not clearly defined. Thanks for raising it.

mxinden commented 2 years ago

Discussed out of band that this is ready to merge.