aboutsummaryrefslogtreecommitdiffstats
path: root/src/transport
diff options
context:
space:
mode:
Diffstat (limited to 'src/transport')
-rw-r--r--src/transport/libp2p.rs56
1 files changed, 54 insertions, 2 deletions
diff --git a/src/transport/libp2p.rs b/src/transport/libp2p.rs
index 9b7c666..6602c2e 100644
--- a/src/transport/libp2p.rs
+++ b/src/transport/libp2p.rs
@@ -40,6 +40,10 @@
//! AutoNAT asks connected peers (nodes of the swarm) to dial it back. Once AutoNAT says
//! it can be dialled, it lets the relays it picked go.
//!
+//! With `--libp2p-upnp`, a node asks its router to forward its listen ports (UPnP), and
+//! publishes the router's public address for them with its own (`Book::direct`): then
+//! it can be dialled from elsewhere, whatever the other side's NAT.
+//!
//! With `--libp2p-mdns`, a node finds libp2p peers on its LAN (multicast DNS). Their
//! LAN addresses are noted, and those that turn out to speak continuum (identify says
//! so) are seeds while the node joins (`discovered_seeds`): nodes on one LAN find each
@@ -173,6 +177,8 @@ pub(crate) struct Libp2pConfig {
pub(crate) continuum: bool,
/// Find peers on the LAN (mDNS).
pub(crate) mdns: bool,
+ /// Ask the router to forward our listen ports (UPnP), and publish where.
+ pub(crate) upnp: bool,
/// Use public infrastructure: the IPFS DHT, public relays.
pub(crate) public: bool,
/// libp2p's in-memory transport instead of TCP and QUIC: for tests.
@@ -255,6 +261,8 @@ struct NamedRelay {
struct Book {
addresses: HashMap<PeerId, Vec<Multiaddr>>,
listening: Vec<Multiaddr>,
+ /// Where the router forwards to our listen addresses (UPnP, `Libp2pConfig::upnp`).
+ mapped: Vec<Multiaddr>,
/// `Libp2pConfig::announce`.
announced: Vec<Multiaddr>,
/// `Libp2pConfig::relay`: this node relays, and says so.
@@ -571,7 +579,8 @@ impl Book {
}
/// Where others could dial us directly: the announced addresses, or else those we
- /// listen on (`published`), without the peer ID.
+ /// listen on (`published`) and where the router forwards to them (`mapped`), without
+ /// the peer ID.
fn direct(&self) -> Vec<Multiaddr> {
if !self.announced.is_empty() {
return self.announced.clone();
@@ -582,7 +591,15 @@ impl Book {
.filter(|address| !is_relayed(address))
.cloned()
.collect();
- published(&direct)
+ let mut direct = published(&direct);
+ direct.extend(
+ self.mapped
+ .iter()
+ .filter(|address| !direct.contains(address))
+ .cloned()
+ .collect::<Vec<_>>(),
+ );
+ direct
}
}
@@ -939,6 +956,9 @@ struct Behaviour {
autonat: ::libp2p::autonat::Behaviour,
/// Only with `Libp2pConfig::mdns`.
mdns: ::libp2p::swarm::behaviour::toggle::Toggle<::libp2p::mdns::tokio::Behaviour>,
+ /// Only with `Libp2pConfig::upnp`: maps our listen ports on the router, and confirms
+ /// the mapped addresses as ours (identify then tells them).
+ upnp: ::libp2p::swarm::behaviour::toggle::Toggle<::libp2p::upnp::tokio::Behaviour>,
/// Only with `Libp2pConfig::public`: the IPFS DHT.
kad: ::libp2p::swarm::behaviour::toggle::Toggle<
::libp2p::kad::Behaviour<::libp2p::kad::store::MemoryStore>,
@@ -1095,6 +1115,7 @@ impl ::libp2p::swarm::NetworkBehaviour for HolePunching {
struct Features {
relay: bool,
mdns: bool,
+ upnp: bool,
public: bool,
}
@@ -1103,6 +1124,7 @@ impl Features {
Self {
relay: config.relay,
mdns: config.mdns,
+ upnp: config.upnp,
public: config.public,
}
}
@@ -1129,6 +1151,7 @@ impl Behaviour {
let Features {
relay,
mdns,
+ upnp,
public,
} = features;
let peer_id = key.public().to_peer_id();
@@ -1184,6 +1207,7 @@ impl Behaviour {
},
),
mdns: mdns.into(),
+ upnp: upnp.then(::libp2p::upnp::tokio::Behaviour::default).into(),
kad: kad.into(),
})
}
@@ -1772,6 +1796,31 @@ async fn run(
trace!(peer = %Addressed(SCHEME, &peer), connection = %connection, error = %error, "Connection's ping failed");
}
},
+ SwarmEvent::Behaviour(BehaviourEvent::Upnp(event)) => match event {
+ ::libp2p::upnp::Event::NewExternalAddr {
+ local_addr,
+ external_addr: address,
+ } => {
+ info!(address = %address, to = %local_addr, "The router forwards to us (UPnP)");
+ let mut book = lock(&book);
+ if !book.mapped.contains(&address) {
+ book.mapped.push(address);
+ }
+ }
+ ::libp2p::upnp::Event::ExpiredExternalAddr {
+ local_addr,
+ external_addr: address,
+ } => {
+ info!(address = %address, to = %local_addr, "The router no longer forwards to us (UPnP)");
+ lock(&book).mapped.retain(|mapped| *mapped != address);
+ }
+ ::libp2p::upnp::Event::GatewayNotFound => {
+ info!("No router that maps ports (UPnP) answered");
+ }
+ ::libp2p::upnp::Event::NonRoutableGateway => {
+ warn!("The router that maps ports (UPnP) is behind another NAT itself; its mappings don't reach us from outside");
+ }
+ },
SwarmEvent::Behaviour(BehaviourEvent::Dcutr(event)) => match event.result {
Ok(_) => {
lock(&book).counts.hole_punches.succeeded += 1;
@@ -2242,6 +2291,7 @@ mod tests {
relay,
continuum: true,
mdns: false,
+ upnp: false,
public: false,
memory: true,
})
@@ -2388,6 +2438,7 @@ mod tests {
relay: false,
continuum: true,
mdns: false,
+ upnp: false,
public: false,
memory: true,
})
@@ -2525,6 +2576,7 @@ mod tests {
relay: true,
continuum: false,
mdns: false,
+ upnp: false,
public: false,
memory: true,
})