core/src/transport: Add Transport::dial_as_listener (#2363)

Allows `NetworkBehaviour` implementations to dial a peer, but instruct
the dialed connection to be upgraded as if it were the listening
endpoint.

This is needed when establishing direct connections through NATs and/or
Firewalls (hole punching). When hole punching via TCP (QUIC is different
but similar) both ends dial the other at the same time resulting in a
simultaneously opened TCP connection. To disambiguate who is the dialer
and who the listener there are two options:

1. Use the Simultaneous Open Extension of Multistream Select. See
   [sim-open] specification and [sim-open-rust] Rust implementation.

2. Disambiguate the role (dialer or listener) based on the role within
   the DCUtR [dcutr] protocol. More specifically the node initiating the
   DCUtR process will act as a listener and the other as a dialer.

This commit enables (2), i.e. enables the DCUtR protocol to specify the
role used once the connection is established.

While on the positive side (2) requires one round trip less than (1), on
the negative side (2) only works for coordinated simultaneous dials.
I.e. when a simultaneous dial happens by chance, and not coordinated via
DCUtR, the connection attempt fails when only (2) is in place.

[sim-open]: https://github.com/libp2p/specs/blob/master/connections/simopen.md
[sim-open-rust]: https://github.com/libp2p/rust-libp2p/pull/2066
[dcutr]: https://github.com/libp2p/specs/blob/master/relay/DCUtR.md
This commit is contained in:
Max Inden
2022-01-17 16:35:14 +01:00
committed by GitHub
parent bdfbceb6ee
commit 96dbfcd1ad
43 changed files with 594 additions and 106 deletions

View File

@ -19,6 +19,7 @@
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
use libp2p_core::connection::Endpoint;
use libp2p_core::{Multiaddr, PeerId};
use std::num::NonZeroU8;
@ -51,6 +52,7 @@ impl DialOpts {
WithPeerId {
peer_id,
condition: Default::default(),
role_override: Endpoint::Dialer,
dial_concurrency_factor_override: Default::default(),
}
}
@ -108,6 +110,7 @@ pub(super) enum Opts {
pub struct WithPeerId {
pub(crate) peer_id: PeerId,
pub(crate) condition: PeerCondition,
pub(crate) role_override: Endpoint,
pub(crate) dial_concurrency_factor_override: Option<NonZeroU8>,
}
@ -132,10 +135,22 @@ impl WithPeerId {
condition: self.condition,
addresses,
extend_addresses_through_behaviour: false,
role_override: self.role_override,
dial_concurrency_factor_override: self.dial_concurrency_factor_override,
}
}
/// Override role of local node on connection. I.e. execute the dial _as a
/// listener_.
///
/// See
/// [`ConnectedPoint::Dialer`](libp2p_core::connection::ConnectedPoint::Dialer)
/// for details.
pub fn override_role(mut self) -> Self {
self.role_override = Endpoint::Listener;
self
}
/// Build the final [`DialOpts`].
///
/// Addresses to dial the peer are retrieved via
@ -151,6 +166,7 @@ pub struct WithPeerIdWithAddresses {
pub(crate) condition: PeerCondition,
pub(crate) addresses: Vec<Multiaddr>,
pub(crate) extend_addresses_through_behaviour: bool,
pub(crate) role_override: Endpoint,
pub(crate) dial_concurrency_factor_override: Option<NonZeroU8>,
}
@ -168,6 +184,17 @@ impl WithPeerIdWithAddresses {
self
}
/// Override role of local node on connection. I.e. execute the dial _as a
/// listener_.
///
/// See
/// [`ConnectedPoint::Dialer`](libp2p_core::connection::ConnectedPoint::Dialer)
/// for details.
pub fn override_role(mut self) -> Self {
self.role_override = Endpoint::Listener;
self
}
/// Override
/// [`NetworkConfig::with_dial_concurrency_factor`](libp2p_core::network::NetworkConfig::with_dial_concurrency_factor).
pub fn override_dial_concurrency_factor(mut self, factor: NonZeroU8) -> Self {
@ -187,16 +214,30 @@ pub struct WithoutPeerId {}
impl WithoutPeerId {
/// Specify a single address to dial the unknown peer.
pub fn address(self, address: Multiaddr) -> WithoutPeerIdWithAddress {
WithoutPeerIdWithAddress { address }
WithoutPeerIdWithAddress {
address,
role_override: Endpoint::Dialer,
}
}
}
#[derive(Debug)]
pub struct WithoutPeerIdWithAddress {
pub(crate) address: Multiaddr,
pub(crate) role_override: Endpoint,
}
impl WithoutPeerIdWithAddress {
/// Override role of local node on connection. I.e. execute the dial _as a
/// listener_.
///
/// See
/// [`ConnectedPoint::Dialer`](libp2p_core::connection::ConnectedPoint::Dialer)
/// for details.
pub fn override_role(mut self) -> Self {
self.role_override = Endpoint::Listener;
self
}
/// Build the final [`DialOpts`].
pub fn build(self) -> DialOpts {
DialOpts(Opts::WithoutPeerIdWithAddress(self))