Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an automatic port forwarding feature, discover node's external address #1218

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ version = "4.0"
[dependencies.hex]
version = "0.4.1"

[dependencies.igd]
version = "0.12"

[dependencies.num_cpus]
version = "1"

Expand Down
3 changes: 3 additions & 0 deletions network/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ version = "1.3.16"
[dependencies.defer]
version = "0.1"

[dependencies.igd]
version = "0.12"

[dependencies.async-trait]
version = "0.1"

Expand Down
9 changes: 9 additions & 0 deletions network/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use crate::NetworkError;

use arc_swap::ArcSwap;
use igd::Gateway;
use serde::{Deserialize, Serialize};
use std::{
net::SocketAddr,
Expand Down Expand Up @@ -56,6 +57,10 @@ pub struct Config {
sync_providers: ArcSwap<Vec<SocketAddr>>,
/// The interval between each peer sync.
peer_sync_interval: Duration,
/// Details of the node's network gateway, if it's UPnP-enabled.
pub gateway: Option<Gateway>,
/// Indicates whether the node should attempt to automatically forward its listening port.
pub auto_port_forwarding: bool,
}

impl Config {
Expand All @@ -70,6 +75,8 @@ impl Config {
beacon_addresses: Vec<String>,
sync_provider_addresses: Vec<String>,
peer_sync_interval: Duration,
gateway: Option<Gateway>,
auto_port_forwarding: bool,
) -> Result<Self, NetworkError> {
// Convert the given seeded nodes into socket addresses.
let beacons: Vec<SocketAddr> = beacon_addresses
Expand All @@ -91,6 +98,8 @@ impl Config {
beacons: ArcSwap::new(Arc::new(beacons)),
sync_providers: ArcSwap::new(Arc::new(sync_providers)),
peer_sync_interval,
gateway,
auto_port_forwarding,
})
}

Expand Down
20 changes: 19 additions & 1 deletion network/src/inbound/inbound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with the snarkOS library. If not, see <https://www.gnu.org/licenses/>.

use std::time::Duration;
use std::{net::SocketAddr, time::Duration};

use igd::{AddPortError, PortMappingProtocol};
use tokio::{net::TcpListener, task};

use snarkos_metrics::{self as metrics, connections};
Expand All @@ -25,6 +26,23 @@ use crate::{errors::NetworkError, Node};
impl Node {
/// This method handles new inbound connection requests.
pub async fn listen(&self) -> Result<(), NetworkError> {
if self.config.auto_port_forwarding {
if let Some(ref gateway) = self.config.gateway {
if let SocketAddr::V4(internal_addr) = self.config.desired_address {
let external_port = internal_addr.port();

match gateway.add_port(PortMappingProtocol::TCP, external_port, internal_addr, 0, "snarkOS") {
Err(AddPortError::PortInUse) => warn!("Port {} is already forwarded", external_port),
Err(e) => error!(
"Can't map external port {} to address {}: {}",
external_port, internal_addr, e
),
Ok(_) => info!("Enabled port forwarding via UPnP"),
}
}
}
}

let listener = TcpListener::bind(&self.config.desired_address).await?;

// Update the node's listening address.
Expand Down
12 changes: 11 additions & 1 deletion network/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use snarkos_storage::DynStorage;
#[cfg(not(feature = "test"))]
use std::time::Duration;
use std::{
net::SocketAddr,
net::{Ipv4Addr, SocketAddr},
ops::Deref,
sync::{
atomic::{AtomicBool, AtomicU8, Ordering},
Expand Down Expand Up @@ -56,6 +56,8 @@ pub struct InnerNode {
pub id: u64,
/// The current state of the node.
state: StateCode,
/// The external address of the node, if the gateway is UPnP-enabled.
pub external_addr: Option<Ipv4Addr>,
/// The local address of this node.
pub local_addr: OnceCell<SocketAddr>,
/// The pre-configured parameters of this node.
Expand Down Expand Up @@ -119,9 +121,17 @@ impl Node {
impl Node {
/// Creates a new instance of `Node`.
pub async fn new(config: Config, storage: DynStorage) -> Result<Self, NetworkError> {
let external_addr = config.gateway.as_ref().and_then(|gateway| {
gateway
.get_external_ip()
.map_err(|e| error!("Can't obtain external node address: {}", e))
.ok()
});

let node = Self(Arc::new(InnerNode {
id: config.node_id.unwrap_or_else(|| thread_rng().gen()),
state: Default::default(),
external_addr,
local_addr: Default::default(),
config,
storage: storage.clone(),
Expand Down
9 changes: 9 additions & 0 deletions snarkos/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ pub struct Node {
pub db: String,
pub ip: String,
pub port: u16,
pub auto_port_forwarding: bool,
pub verbose: u8,
}

Expand Down Expand Up @@ -133,6 +134,7 @@ impl Default for Config {
db: "snarkos_testnet1".into(),
ip: "0.0.0.0".into(),
port: 4131,
auto_port_forwarding: false,
verbose: 2,
},
miner: Miner {
Expand Down Expand Up @@ -238,6 +240,7 @@ impl Config {
fn parse(&mut self, arguments: &ArgMatches, options: &[&str]) {
options.iter().for_each(|option| match *option {
// Flags
"auto-port-forwarding" => self.auto_port_forwarding(arguments.is_present(option)),
"is-miner" => self.is_miner(arguments.is_present(option)),
"no-jsonrpc" => self.no_jsonrpc(arguments.is_present(option)),
"trim-storage" => self.trim_storage(arguments.is_present(option)),
Expand Down Expand Up @@ -312,6 +315,10 @@ impl Config {
}
}

fn auto_port_forwarding(&mut self, argument: bool) {
self.node.auto_port_forwarding = argument;
}

fn is_miner(&mut self, argument: bool) {
self.miner.is_miner = argument;
}
Expand Down Expand Up @@ -440,6 +447,7 @@ impl CLI for ConfigCli {

const ABOUT: AboutType = "Run an Aleo node (include -h for more options)";
const FLAGS: &'static [FlagType] = &[
flag::AUTO_PORT_FORWARDING,
flag::NO_JSONRPC,
flag::IS_MINER,
flag::TRIM_STORAGE,
Expand Down Expand Up @@ -471,6 +479,7 @@ impl CLI for ConfigCli {
fn parse(arguments: &ArgMatches) -> Result<Self::Config, CliError> {
let mut config = Config::read_config()?;
config.parse(arguments, &[
"auto-port-forwarding",
"network",
"no-jsonrpc",
"export-canon-blocks",
Expand Down
16 changes: 16 additions & 0 deletions snarkos/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,20 @@ pub async fn init_node(config: &Config, storage: DynStorage) -> anyhow::Result<N
let address = format!("{}:{}", config.node.ip, config.node.port);
let desired_address = address.parse::<SocketAddr>()?;

let gateway_search_opts = igd::SearchOptions {
timeout: Some(Duration::from_secs(1)),
..Default::default()
};

let gateway = igd::search_gateway(gateway_search_opts)
.map_err(|e| {
warn!(
"Can't obtain the node's gateway details: {}; perhaps it's not UPnP-enabled?",
e
)
})
.ok();

let node_config = NodeConfig::new(
None,
config.node.kind,
Expand All @@ -241,6 +255,8 @@ pub async fn init_node(config: &Config, storage: DynStorage) -> anyhow::Result<N
config.p2p.sync_providers.clone(),
// Set sync intervals for peers.
Duration::from_secs(config.p2p.peer_sync_interval.into()),
gateway,
config.node.auto_port_forwarding,
)?;

let node = Node::new(node_config, storage).await?;
Expand Down
3 changes: 3 additions & 0 deletions snarkos/parameters/flag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@ pub const LIST: &str = "[list] -l --list 'List all available releases of snarkOS
pub const TRIM_STORAGE: &str = "[trim-storage] --trim-storage 'Remove non-canon items from the node's storage'";

pub const VALIDATE_STORAGE: &str = "[validate-storage] --validate-storage 'Check the integrity of the node's storage and attempt to fix encountered issues'";

pub const AUTO_PORT_FORWARDING: &str =
"[auto-port-forwarding] --auto-port-forwarding 'Automatically forward the node's listening port using UPnP'";
2 changes: 2 additions & 0 deletions testing/src/network/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ pub fn test_config(setup: TestSetup) -> Config {
setup.beacons,
setup.sync_providers,
Duration::from_secs(setup.peer_sync_interval),
None,
false,
)
.unwrap()
}
Expand Down