diff --git a/sn_networking/Cargo.toml b/sn_networking/Cargo.toml index fc0fb04c98..f826e14bbe 100644 --- a/sn_networking/Cargo.toml +++ b/sn_networking/Cargo.toml @@ -13,6 +13,7 @@ version = "0.14.4" [features] default = ["libp2p/quic"] local-discovery = ["libp2p/mdns"] +upnp = ["libp2p/upnp"] # tcp is automatically enabled when compiling for wasm32 websockets = ["libp2p/tcp"] open-metrics = ["libp2p/metrics", "prometheus-client", "hyper", "sysinfo"] diff --git a/sn_networking/src/driver.rs b/sn_networking/src/driver.rs index c104ec14cb..517639d9ad 100644 --- a/sn_networking/src/driver.rs +++ b/sn_networking/src/driver.rs @@ -182,6 +182,8 @@ pub enum VerificationKind { #[derive(NetworkBehaviour)] #[behaviour(to_swarm = "NodeEvent")] pub(super) struct NodeBehaviour { + #[cfg(feature = "upnp")] + pub(super) upnp: libp2p::swarm::behaviour::toggle::Toggle, pub(super) request_response: request_response::cbor::Behaviour, pub(super) kademlia: kad::Behaviour, #[cfg(feature = "local-discovery")] @@ -206,6 +208,8 @@ pub struct NetworkBuilder { metrics_registry: Option, #[cfg(feature = "open-metrics")] metrics_server_port: u16, + #[cfg(feature = "upnp")] + upnp: bool, } impl NetworkBuilder { @@ -223,6 +227,8 @@ impl NetworkBuilder { metrics_registry: None, #[cfg(feature = "open-metrics")] metrics_server_port: 0, + #[cfg(feature = "upnp")] + upnp: false, } } @@ -256,6 +262,11 @@ impl NetworkBuilder { self.metrics_server_port = port; } + #[cfg(feature = "upnp")] + pub fn upnp(&mut self, upnp: bool) { + self.upnp = upnp; + } + /// Creates a new `SwarmDriver` instance, along with a `Network` handle /// for sending commands and an `mpsc::Receiver` for receiving /// network events. It initializes the swarm, sets up the transport, and @@ -315,6 +326,8 @@ impl NetworkBuilder { }; let listen_addr = self.listen_addr; + #[cfg(feature = "upnp")] + let upnp = self.upnp; let (network, events_receiver, mut swarm_driver) = self.build( kad_cfg, @@ -322,6 +335,8 @@ impl NetworkBuilder { false, ProtocolSupport::Full, IDENTIFY_NODE_VERSION_STR.to_string(), + #[cfg(feature = "upnp")] + upnp, )?; // Listen on the provided address @@ -373,6 +388,8 @@ impl NetworkBuilder { true, ProtocolSupport::Outbound, IDENTIFY_CLIENT_VERSION_STR.to_string(), + #[cfg(feature = "upnp")] + false, )?; Ok((network, net_event_recv, driver)) @@ -386,6 +403,7 @@ impl NetworkBuilder { is_client: bool, req_res_protocol: ProtocolSupport, identify_version: String, + #[cfg(feature = "upnp")] upnp: bool, ) -> Result<(Network, mpsc::Receiver, SwarmDriver)> { let peer_id = PeerId::from(self.keypair.public()); // vdash metric (if modified please notify at https://github.com/happybeing/vdash/issues): @@ -485,6 +503,15 @@ impl NetworkBuilder { main_transport }; + #[cfg(feature = "upnp")] + let upnp = if !self.local && !is_client && upnp { + debug!("Enabling UPnP port opening behavior"); + Some(libp2p::upnp::tokio::Behaviour::default()) + } else { + None + } + .into(); // Into `Toggle` + let (relay_transport, relay_behaviour) = libp2p::relay::client::new(self.keypair.public().to_peer_id()); let relay_transport = relay_transport @@ -516,6 +543,8 @@ impl NetworkBuilder { let behaviour = NodeBehaviour { relay_client: relay_behaviour, relay_server, + #[cfg(feature = "upnp")] + upnp, request_response, kademlia, identify, diff --git a/sn_networking/src/event/mod.rs b/sn_networking/src/event/mod.rs index e4112d1549..4e1552edfd 100644 --- a/sn_networking/src/event/mod.rs +++ b/sn_networking/src/event/mod.rs @@ -35,6 +35,8 @@ use tokio::sync::oneshot; /// NodeEvent enum #[derive(CustomDebug)] pub(super) enum NodeEvent { + #[cfg(feature = "upnp")] + Upnp(libp2p::upnp::Event), MsgReceived(libp2p::request_response::Event), Kademlia(libp2p::kad::Event), #[cfg(feature = "local-discovery")] @@ -45,6 +47,13 @@ pub(super) enum NodeEvent { RelayServer(Box), } +#[cfg(feature = "upnp")] +impl From for NodeEvent { + fn from(event: libp2p::upnp::Event) -> Self { + NodeEvent::Upnp(event) + } +} + impl From> for NodeEvent { fn from(event: libp2p::request_response::Event) -> Self { NodeEvent::MsgReceived(event) diff --git a/sn_node/Cargo.toml b/sn_node/Cargo.toml index 36ac08adef..73b5f3c439 100644 --- a/sn_node/Cargo.toml +++ b/sn_node/Cargo.toml @@ -14,13 +14,15 @@ name = "safenode" path = "src/bin/safenode/main.rs" [features] -default = ["metrics"] +default = ["metrics", "upnp"] local-discovery = ["sn_networking/local-discovery"] otlp = ["sn_logging/otlp"] metrics = ["sn_logging/process-metrics"] network-contacts = ["sn_peers_acquisition/network-contacts"] open-metrics = ["sn_networking/open-metrics", "prometheus-client"] encrypt-records = ["sn_networking/encrypt-records"] +upnp = ["sn_networking/upnp"] + [dependencies] assert_fs = "1.0.0" diff --git a/sn_node/src/bin/safenode/main.rs b/sn_node/src/bin/safenode/main.rs index e4d23116e2..85fd8817b1 100644 --- a/sn_node/src/bin/safenode/main.rs +++ b/sn_node/src/bin/safenode/main.rs @@ -74,6 +74,11 @@ struct Opt { #[clap(long, default_value_t = false)] home_network: bool, + /// Try to use UPnP to open a port in the home router and allow incoming connections. + #[cfg(feature = "upnp")] + #[clap(long, default_value_t = false)] + upnp: bool, + /// Specify the logging output destination. /// /// Valid values are "stdout", "data-dir", or a custom path. @@ -200,6 +205,8 @@ fn main() -> Result<()> { opt.local, root_dir, opt.owner.clone(), + #[cfg(feature = "upnp")] + opt.upnp, ); node_builder.is_behind_home_network = opt.home_network; #[cfg(feature = "open-metrics")] diff --git a/sn_node/src/node.rs b/sn_node/src/node.rs index 559e41d081..cc93f9f564 100644 --- a/sn_node/src/node.rs +++ b/sn_node/src/node.rs @@ -68,6 +68,8 @@ pub struct NodeBuilder { /// Enable hole punching for nodes connecting from home networks. pub is_behind_home_network: bool, owner: String, + #[cfg(feature = "upnp")] + upnp: bool, } impl NodeBuilder { @@ -79,6 +81,7 @@ impl NodeBuilder { local: bool, root_dir: PathBuf, owner: String, + #[cfg(feature = "upnp")] upnp: bool, ) -> Self { Self { keypair, @@ -90,6 +93,8 @@ impl NodeBuilder { metrics_server_port: 0, is_behind_home_network: false, owner, + #[cfg(feature = "upnp")] + upnp, } } @@ -143,6 +148,9 @@ impl NodeBuilder { network_builder.initial_peers(self.initial_peers.clone()); network_builder.is_behind_home_network(self.is_behind_home_network); + #[cfg(feature = "upnp")] + network_builder.upnp(self.upnp); + let (network, network_event_receiver, swarm_driver) = network_builder.build_node()?; let node_events_channel = NodeEventsChannel::default(); let (node_cmds, _) = broadcast::channel(10);