Skip to content

Commit 40f07a5

Browse files
authored
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]: libp2p/rust-libp2p#2066 [dcutr]: https://github.com/libp2p/specs/blob/master/relay/DCUtR.md
1 parent ec3fb6c commit 40f07a5

File tree

43 files changed

+594
-106
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+594
-106
lines changed

core/CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,24 @@
2222

2323
- Implement `Serialize` and `Deserialize` for `PeerId` (see [PR 2408])
2424

25+
- Allow overriding role when dialing. This option is needed for NAT and firewall
26+
hole punching.
27+
28+
- Add `Transport::dial_as_listener`. As `Transport::dial` but
29+
overrides the role of the local node on the connection . I.e. has the
30+
local node act as a listener on the outgoing connection.
31+
32+
- Add `override_role` option to `DialOpts`.
33+
34+
See [PR 2363].
35+
2536
[PR 2339]: https://github.com/libp2p/rust-libp2p/pull/2339
2637
[PR 2350]: https://github.com/libp2p/rust-libp2p/pull/2350
2738
[PR 2352]: https://github.com/libp2p/rust-libp2p/pull/2352
2839
[PR 2392]: https://github.com/libp2p/rust-libp2p/pull/2392
2940
[PR 2404]: https://github.com/libp2p/rust-libp2p/pull/2404
3041
[PR 2408]: https://github.com/libp2p/rust-libp2p/pull/2408
42+
[PR 2363]: https://github.com/libp2p/rust-libp2p/pull/2363
3143

3244
# 0.30.1 [2021-11-16]
3345

core/src/connection.rs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,10 @@ pub enum PendingPoint {
9797
/// There is no single address associated with the Dialer of a pending
9898
/// connection. Addresses are dialed in parallel. Only once the first dial
9999
/// is successful is the address of the connection known.
100-
Dialer,
100+
Dialer {
101+
/// Same as [`ConnectedPoint::Dialer`] `role_override`.
102+
role_override: Endpoint,
103+
},
101104
/// The socket comes from a listener.
102105
Listener {
103106
/// Local connection address.
@@ -110,7 +113,7 @@ pub enum PendingPoint {
110113
impl From<ConnectedPoint> for PendingPoint {
111114
fn from(endpoint: ConnectedPoint) -> Self {
112115
match endpoint {
113-
ConnectedPoint::Dialer { .. } => PendingPoint::Dialer,
116+
ConnectedPoint::Dialer { role_override, .. } => PendingPoint::Dialer { role_override },
114117
ConnectedPoint::Listener {
115118
local_addr,
116119
send_back_addr,
@@ -129,6 +132,27 @@ pub enum ConnectedPoint {
129132
Dialer {
130133
/// Multiaddress that was successfully dialed.
131134
address: Multiaddr,
135+
/// Whether the role of the local node on the connection should be
136+
/// overriden. I.e. whether the local node should act as a listener on
137+
/// the outgoing connection.
138+
///
139+
/// This option is needed for NAT and firewall hole punching.
140+
///
141+
/// - [`Endpoint::Dialer`] represents the default non-overriding option.
142+
///
143+
/// - [`Endpoint::Listener`] represents the overriding option.
144+
/// Realization depends on the transport protocol. E.g. in the case of
145+
/// TCP, both endpoints dial each other, resulting in a _simultaneous
146+
/// open_ TCP connection. On this new connection both endpoints assume
147+
/// to be the dialer of the connection. This is problematic during the
148+
/// connection upgrade process where an upgrade assumes one side to be
149+
/// the listener. With the help of this option, both peers can
150+
/// negotiate the roles (dialer and listener) for the new connection
151+
/// ahead of time, through some external channel, e.g. the DCUtR
152+
/// protocol, and thus have one peer dial the other and upgrade the
153+
/// connection as a dialer and one peer dial the other and upgrade the
154+
/// connection _as a listener_ overriding its role.
155+
role_override: Endpoint,
132156
},
133157
/// We received the node.
134158
Listener {
@@ -179,7 +203,10 @@ impl ConnectedPoint {
179203
/// Returns true if the connection is relayed.
180204
pub fn is_relayed(&self) -> bool {
181205
match self {
182-
ConnectedPoint::Dialer { address } => address,
206+
ConnectedPoint::Dialer {
207+
address,
208+
role_override: _,
209+
} => address,
183210
ConnectedPoint::Listener { local_addr, .. } => local_addr,
184211
}
185212
.iter()
@@ -194,7 +221,7 @@ impl ConnectedPoint {
194221
/// not be usable to establish new connections.
195222
pub fn get_remote_address(&self) -> &Multiaddr {
196223
match self {
197-
ConnectedPoint::Dialer { address } => address,
224+
ConnectedPoint::Dialer { address, .. } => address,
198225
ConnectedPoint::Listener { send_back_addr, .. } => send_back_addr,
199226
}
200227
}
@@ -204,7 +231,7 @@ impl ConnectedPoint {
204231
/// For `Dialer`, this modifies `address`. For `Listener`, this modifies `send_back_addr`.
205232
pub fn set_remote_address(&mut self, new_address: Multiaddr) {
206233
match self {
207-
ConnectedPoint::Dialer { address } => *address = new_address,
234+
ConnectedPoint::Dialer { address, .. } => *address = new_address,
208235
ConnectedPoint::Listener { send_back_addr, .. } => *send_back_addr = new_address,
209236
}
210237
}

core/src/connection/listeners.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,13 @@ mod tests {
491491
panic!()
492492
}
493493

494+
fn dial_as_listener(
495+
self,
496+
_: Multiaddr,
497+
) -> Result<Self::Dial, transport::TransportError<Self::Error>> {
498+
panic!()
499+
}
500+
494501
fn address_translation(&self, _: &Multiaddr, _: &Multiaddr) -> Option<Multiaddr> {
495502
None
496503
}
@@ -542,6 +549,13 @@ mod tests {
542549
panic!()
543550
}
544551

552+
fn dial_as_listener(
553+
self,
554+
_: Multiaddr,
555+
) -> Result<Self::Dial, transport::TransportError<Self::Error>> {
556+
panic!()
557+
}
558+
545559
fn address_translation(&self, _: &Multiaddr, _: &Multiaddr) -> Option<Multiaddr> {
546560
None
547561
}

core/src/connection/pool.rs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
use crate::{
2323
connection::{
2424
handler::{THandlerError, THandlerInEvent, THandlerOutEvent},
25-
Connected, ConnectionError, ConnectionHandler, ConnectionId, ConnectionLimit, IncomingInfo,
26-
IntoConnectionHandler, PendingConnectionError, PendingInboundConnectionError,
25+
Connected, ConnectionError, ConnectionHandler, ConnectionId, ConnectionLimit, Endpoint,
26+
IncomingInfo, IntoConnectionHandler, PendingConnectionError, PendingInboundConnectionError,
2727
PendingOutboundConnectionError, PendingPoint, Substream,
2828
},
2929
muxing::StreamMuxer,
@@ -460,7 +460,7 @@ where
460460
local_addr,
461461
send_back_addr,
462462
}),
463-
PendingPoint::Dialer => None,
463+
PendingPoint::Dialer { .. } => None,
464464
})
465465
}
466466

@@ -535,6 +535,7 @@ where
535535
addresses: impl Iterator<Item = Multiaddr> + Send + 'static,
536536
peer: Option<PeerId>,
537537
handler: THandler,
538+
role_override: Endpoint,
538539
dial_concurrency_factor_override: Option<NonZeroU8>,
539540
) -> Result<ConnectionId, DialError<THandler>>
540541
where
@@ -550,6 +551,7 @@ where
550551
peer,
551552
addresses,
552553
dial_concurrency_factor_override.unwrap_or(self.dial_concurrency_factor),
554+
role_override,
553555
);
554556

555557
let connection_id = self.next_connection_id();
@@ -566,13 +568,15 @@ where
566568
.boxed(),
567569
);
568570

569-
self.counters.inc_pending(&PendingPoint::Dialer);
571+
let endpoint = PendingPoint::Dialer { role_override };
572+
573+
self.counters.inc_pending(&endpoint);
570574
self.pending.insert(
571575
connection_id,
572576
PendingConnectionInfo {
573577
peer_id: peer,
574578
handler,
575-
endpoint: PendingPoint::Dialer,
579+
endpoint: endpoint,
576580
_drop_notifier: drop_notifier,
577581
},
578582
);
@@ -745,9 +749,13 @@ where
745749
self.counters.dec_pending(&endpoint);
746750

747751
let (endpoint, concurrent_dial_errors) = match (endpoint, outgoing) {
748-
(PendingPoint::Dialer, Some((address, errors))) => {
749-
(ConnectedPoint::Dialer { address }, Some(errors))
750-
}
752+
(PendingPoint::Dialer { role_override }, Some((address, errors))) => (
753+
ConnectedPoint::Dialer {
754+
address,
755+
role_override,
756+
},
757+
Some(errors),
758+
),
751759
(
752760
PendingPoint::Listener {
753761
local_addr,
@@ -761,7 +769,7 @@ where
761769
},
762770
None,
763771
),
764-
(PendingPoint::Dialer, None) => unreachable!(
772+
(PendingPoint::Dialer { .. }, None) => unreachable!(
765773
"Established incoming connection via pending outgoing connection."
766774
),
767775
(PendingPoint::Listener { .. }, Some(_)) => unreachable!(
@@ -910,7 +918,7 @@ where
910918
self.counters.dec_pending(&endpoint);
911919

912920
match (endpoint, error) {
913-
(PendingPoint::Dialer, Either::Left(error)) => {
921+
(PendingPoint::Dialer { .. }, Either::Left(error)) => {
914922
return Poll::Ready(PoolEvent::PendingOutboundConnectionError {
915923
id,
916924
error,
@@ -933,7 +941,7 @@ where
933941
local_addr,
934942
});
935943
}
936-
(PendingPoint::Dialer, Either::Right(_)) => {
944+
(PendingPoint::Dialer { .. }, Either::Right(_)) => {
937945
unreachable!("Inbound error for outbound connection.")
938946
}
939947
(PendingPoint::Listener { .. }, Either::Left(_)) => {
@@ -1176,7 +1184,7 @@ impl ConnectionCounters {
11761184

11771185
fn inc_pending(&mut self, endpoint: &PendingPoint) {
11781186
match endpoint {
1179-
PendingPoint::Dialer => {
1187+
PendingPoint::Dialer { .. } => {
11801188
self.pending_outgoing += 1;
11811189
}
11821190
PendingPoint::Listener { .. } => {
@@ -1191,7 +1199,7 @@ impl ConnectionCounters {
11911199

11921200
fn dec_pending(&mut self, endpoint: &PendingPoint) {
11931201
match endpoint {
1194-
PendingPoint::Dialer => {
1202+
PendingPoint::Dialer { .. } => {
11951203
self.pending_outgoing -= 1;
11961204
}
11971205
PendingPoint::Listener { .. } => {

core/src/connection/pool/concurrent_dial.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@
1818
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1919
// DEALINGS IN THE SOFTWARE.
2020

21-
pub use crate::connection::{ConnectionCounters, ConnectionLimits};
22-
2321
use crate::{
22+
connection::Endpoint,
2423
transport::{Transport, TransportError},
2524
Multiaddr, PeerId,
2625
};
@@ -63,14 +62,21 @@ where
6362
peer: Option<PeerId>,
6463
addresses: impl Iterator<Item = Multiaddr> + Send + 'static,
6564
concurrency_factor: NonZeroU8,
65+
role_override: Endpoint,
6666
) -> Self {
6767
let mut pending_dials = addresses.map(move |address| match p2p_addr(peer, address) {
68-
Ok(address) => match transport.clone().dial(address.clone()) {
69-
Ok(fut) => fut
70-
.map(|r| (address, r.map_err(|e| TransportError::Other(e))))
71-
.boxed(),
72-
Err(err) => futures::future::ready((address, Err(err))).boxed(),
73-
},
68+
Ok(address) => {
69+
let dial = match role_override {
70+
Endpoint::Dialer => transport.clone().dial(address.clone()),
71+
Endpoint::Listener => transport.clone().dial_as_listener(address.clone()),
72+
};
73+
match dial {
74+
Ok(fut) => fut
75+
.map(|r| (address, r.map_err(|e| TransportError::Other(e))))
76+
.boxed(),
77+
Err(err) => futures::future::ready((address, Err(err))).boxed(),
78+
}
79+
}
7480
Err(address) => futures::future::ready((
7581
address.clone(),
7682
Err(TransportError::MultiaddrNotSupported(address)),

core/src/either.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,25 @@ where
529529
}
530530
}
531531

532+
fn dial_as_listener(self, addr: Multiaddr) -> Result<Self::Dial, TransportError<Self::Error>>
533+
where
534+
Self: Sized,
535+
{
536+
use TransportError::*;
537+
match self {
538+
EitherTransport::Left(a) => match a.dial_as_listener(addr) {
539+
Ok(connec) => Ok(EitherFuture::First(connec)),
540+
Err(MultiaddrNotSupported(addr)) => Err(MultiaddrNotSupported(addr)),
541+
Err(Other(err)) => Err(Other(EitherError::A(err))),
542+
},
543+
EitherTransport::Right(b) => match b.dial_as_listener(addr) {
544+
Ok(connec) => Ok(EitherFuture::Second(connec)),
545+
Err(MultiaddrNotSupported(addr)) => Err(MultiaddrNotSupported(addr)),
546+
Err(Other(err)) => Err(Other(EitherError::B(err))),
547+
},
548+
}
549+
}
550+
532551
fn address_translation(&self, server: &Multiaddr, observed: &Multiaddr) -> Option<Multiaddr> {
533552
match self {
534553
EitherTransport::Left(a) => a.address_translation(server, observed),

0 commit comments

Comments
 (0)