Add Nat PMP method to P2P module (#11210)
* Add Nat PMP method to P2P module * Add test fn * Use closure * print richer error messages. * reformat long code line * remove closures
This commit is contained in:
parent
2b1d148ceb
commit
e14d68e559
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -1379,6 +1379,7 @@ dependencies = [
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"natpmp 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-crypto 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-path 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2812,6 +2813,14 @@ name = "nan-preserving-float"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "natpmp"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "net2"
|
||||
version = "0.2.33"
|
||||
@ -5612,6 +5621,7 @@ dependencies = [
|
||||
"checksum multibase 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b9c35dac080fd6e16a99924c8dfdef0af89d797dd851adab25feaffacf7850d6"
|
||||
"checksum multihash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c62469025f45dee2464ef9fc845f4683c543993792c1993e7d903c17a4546b74"
|
||||
"checksum nan-preserving-float 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34d4f00fcc2f4c9efa8cc971db0da9e28290e28e97af47585e48691ef10ff31f"
|
||||
"checksum natpmp 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d85b74917d95eab8b26ab6fe28e21d3fede3a614411ca4d3b01265c05bf86a12"
|
||||
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
|
||||
"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
|
||||
"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
|
||||
|
@ -54,7 +54,7 @@ use network::{
|
||||
client_version::ClientVersion,
|
||||
NetworkProtocolHandler, NetworkContext, PeerId, ProtocolId,
|
||||
NetworkConfiguration as BasicNetworkConfiguration, NonReservedPeerMode, Error,
|
||||
ConnectionFilter, IpFilter
|
||||
ConnectionFilter, IpFilter, NatType
|
||||
};
|
||||
use snapshot::SnapshotService;
|
||||
use parking_lot::{RwLock, Mutex};
|
||||
@ -742,6 +742,8 @@ pub struct NetworkConfiguration {
|
||||
pub udp_port: Option<u16>,
|
||||
/// Enable NAT configuration
|
||||
pub nat_enabled: bool,
|
||||
/// Nat type
|
||||
pub nat_type: NatType,
|
||||
/// Enable discovery
|
||||
pub discovery_enabled: bool,
|
||||
/// List of initial node addresses
|
||||
@ -786,6 +788,7 @@ impl NetworkConfiguration {
|
||||
public_address: match self.public_address { None => None, Some(addr) => Some(SocketAddr::from_str(&addr)?) },
|
||||
udp_port: self.udp_port,
|
||||
nat_enabled: self.nat_enabled,
|
||||
nat_type: self.nat_type,
|
||||
discovery_enabled: self.discovery_enabled,
|
||||
boot_nodes: self.boot_nodes,
|
||||
use_secret: self.use_secret,
|
||||
@ -810,6 +813,7 @@ impl From<BasicNetworkConfiguration> for NetworkConfiguration {
|
||||
public_address: other.public_address.and_then(|addr| Some(format!("{}", addr))),
|
||||
udp_port: other.udp_port,
|
||||
nat_enabled: other.nat_enabled,
|
||||
nat_type: other.nat_type,
|
||||
discovery_enabled: other.discovery_enabled,
|
||||
boot_nodes: other.boot_nodes,
|
||||
use_secret: other.use_secret,
|
||||
|
@ -53,7 +53,7 @@ use export_hardcoded_sync::ExportHsyncCmd;
|
||||
use presale::ImportWallet;
|
||||
use account::{AccountCmd, NewAccount, ListAccounts, ImportAccounts, ImportFromGethAccounts};
|
||||
use snapshot_cmd::{self, SnapshotCommand};
|
||||
use network::{IpFilter};
|
||||
use network::{IpFilter, NatType};
|
||||
|
||||
const DEFAULT_MAX_PEERS: u16 = 50;
|
||||
const DEFAULT_MIN_PEERS: u16 = 25;
|
||||
@ -743,7 +743,13 @@ impl Configuration {
|
||||
|
||||
fn net_config(&self) -> Result<NetworkConfiguration, String> {
|
||||
let mut ret = NetworkConfiguration::new();
|
||||
ret.nat_enabled = self.args.arg_nat == "any" || self.args.arg_nat == "upnp";
|
||||
ret.nat_enabled = self.args.arg_nat == "any" || self.args.arg_nat == "upnp" || self.args.arg_nat == "natpmp";
|
||||
ret.nat_type = match &self.args.arg_nat[..] {
|
||||
"any" => NatType::Any,
|
||||
"upnp" => NatType::UPnP,
|
||||
"natpmp" => NatType::NatPMP,
|
||||
_ => NatType::Nothing,
|
||||
};
|
||||
ret.boot_nodes = to_bootnodes(&self.args.arg_bootnodes)?;
|
||||
let (listen, public) = self.net_addresses()?;
|
||||
ret.listen_address = Some(format!("{}", listen));
|
||||
|
@ -202,6 +202,7 @@ pub fn to_bootnodes(bootnodes: &Option<String>) -> Result<Vec<String>, String> {
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn default_network_config() -> ::sync::NetworkConfiguration {
|
||||
use network::NatType;
|
||||
use sync::{NetworkConfiguration};
|
||||
use super::network::IpFilter;
|
||||
NetworkConfiguration {
|
||||
@ -211,6 +212,7 @@ pub fn default_network_config() -> ::sync::NetworkConfiguration {
|
||||
public_address: None,
|
||||
udp_port: None,
|
||||
nat_enabled: true,
|
||||
nat_type: NatType::Any,
|
||||
discovery_enabled: true,
|
||||
boot_nodes: Vec::new(),
|
||||
use_secret: None,
|
||||
|
@ -32,6 +32,7 @@ parity-snappy = "0.1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
lru-cache = "0.1"
|
||||
natpmp = "0.2"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.5"
|
||||
|
@ -461,7 +461,7 @@ impl Host {
|
||||
let public_address = select_public_address(local_endpoint.address.port());
|
||||
let public_endpoint = NodeEndpoint { address: public_address, udp_port: local_endpoint.udp_port };
|
||||
if self.info.read().config.nat_enabled {
|
||||
match map_external_address(&local_endpoint) {
|
||||
match map_external_address(&local_endpoint, &self.info.read().config.nat_type) {
|
||||
Some(endpoint) => {
|
||||
info!("NAT mapped to external address {}", endpoint.address);
|
||||
endpoint
|
||||
|
@ -22,10 +22,17 @@ use std::time::Duration;
|
||||
|
||||
use igd::{PortMappingProtocol, search_gateway, SearchOptions};
|
||||
use ipnetwork::IpNetwork;
|
||||
use log::debug;
|
||||
use log::{trace, debug};
|
||||
use natpmp::{Natpmp, Protocol, Response};
|
||||
use network::NatType;
|
||||
|
||||
use crate::node_table::NodeEndpoint;
|
||||
|
||||
const NAT_PMP_PORT_MAPPING_LIFETIME: u32 = 30;
|
||||
// Waiting duration in milliseconds for response from router after sending port mapping request.
|
||||
// 50 milliseconds might be enough for low RTT.
|
||||
const NAT_PMP_PORT_MAPPING_WAITING_DURATION: u64 = 50;
|
||||
|
||||
/// Socket address extension for rustc beta. To be replaces with now unstable API
|
||||
pub trait SocketAddrExt {
|
||||
/// Returns true if the address appears to be globally routable.
|
||||
@ -310,13 +317,12 @@ pub fn select_public_address(port: u16) -> SocketAddr {
|
||||
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port))
|
||||
}
|
||||
|
||||
pub fn map_external_address(local: &NodeEndpoint) -> Option<NodeEndpoint> {
|
||||
fn search_upnp(local: &NodeEndpoint) -> Option<NodeEndpoint> {
|
||||
if let SocketAddr::V4(ref local_addr) = local.address {
|
||||
let local_ip = *local_addr.ip();
|
||||
let local_port = local_addr.port();
|
||||
let local_udp_port = local.udp_port;
|
||||
|
||||
let search_gateway_child = ::std::thread::spawn(move || {
|
||||
let search_options = SearchOptions {
|
||||
timeout: Some(Duration::new(5, 0)),
|
||||
// igd 0.7 used port 0 by default.
|
||||
@ -324,6 +330,7 @@ pub fn map_external_address(local: &NodeEndpoint) -> Option<NodeEndpoint> {
|
||||
bind_addr: SocketAddr::V4(SocketAddrV4::new(local_ip, 0)),
|
||||
..Default::default()
|
||||
};
|
||||
let search_gateway_child = ::std::thread::spawn(move || {
|
||||
match search_gateway(search_options) {
|
||||
Err(ref err) => debug!("Gateway search error: {}", err),
|
||||
Ok(gateway) => {
|
||||
@ -358,6 +365,82 @@ pub fn map_external_address(local: &NodeEndpoint) -> Option<NodeEndpoint> {
|
||||
None
|
||||
}
|
||||
|
||||
fn search_natpmp(local: &NodeEndpoint) -> Option<NodeEndpoint> {
|
||||
if let SocketAddr::V4(ref local_addr) = local.address {
|
||||
let local_port = local_addr.port();
|
||||
let local_udp_port = local.udp_port;
|
||||
|
||||
let search_gateway_child = ::std::thread::spawn(move || {
|
||||
let mut n = Natpmp::new()?;
|
||||
|
||||
// this function call want to receive `Response::Gateway` response from router, if other then it is an Error.
|
||||
n.send_public_address_request()?;
|
||||
::std::thread::sleep(Duration::from_millis(NAT_PMP_PORT_MAPPING_WAITING_DURATION));
|
||||
let gw = match n.read_response_or_retry() {
|
||||
Ok(Response::Gateway(gw)) => Ok(gw),
|
||||
Err(e) => {
|
||||
debug!(target: "network", "IP request error: {}", e);
|
||||
Err(e)
|
||||
},
|
||||
_ => Err(natpmp::Error::NATPMP_ERR_UNDEFINEDERROR.into())
|
||||
}?;
|
||||
|
||||
// this function call want to receive `Response::TCP` response from router, if other then it is an Error.
|
||||
n.send_port_mapping_request(Protocol::TCP, local_port, local_port, NAT_PMP_PORT_MAPPING_LIFETIME)?;
|
||||
::std::thread::sleep(Duration::from_millis(NAT_PMP_PORT_MAPPING_WAITING_DURATION));
|
||||
let tcp_r = match n.read_response_or_retry() {
|
||||
Ok(Response::TCP(tcp)) => Ok(tcp),
|
||||
Err(e) => {
|
||||
debug!(target: "network", "Port mapping for TCP error: {}", e);
|
||||
Err(e)
|
||||
},
|
||||
_ => Err(natpmp::Error::NATPMP_ERR_UNDEFINEDERROR.into())
|
||||
}?;
|
||||
|
||||
// this function call want to receive `Response::UDP` response from router, if other then it is an Error.
|
||||
n.send_port_mapping_request(Protocol::UDP, local_udp_port, local_udp_port, NAT_PMP_PORT_MAPPING_LIFETIME)?;
|
||||
::std::thread::sleep(Duration::from_millis(NAT_PMP_PORT_MAPPING_WAITING_DURATION));
|
||||
let udp_r = match n.read_response_or_retry() {
|
||||
Ok(Response::UDP(udp)) => Ok(udp),
|
||||
Err(e) => {
|
||||
debug!(target: "network", "Port mapping for UDP error: {}", e);
|
||||
Err(e)
|
||||
},
|
||||
_ => Err(natpmp::Error::NATPMP_ERR_UNDEFINEDERROR.into())
|
||||
}?;
|
||||
|
||||
Ok(NodeEndpoint {
|
||||
address: SocketAddr::V4(SocketAddrV4::new(*gw.public_address(), tcp_r.public_port())),
|
||||
udp_port: udp_r.public_port()
|
||||
})
|
||||
});
|
||||
|
||||
return search_gateway_child.join().ok()?
|
||||
.map_err(|e: natpmp::Error| debug!(target: "network", "NAT PMP port mapping error: {:?}", e))
|
||||
.ok();
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Port mapping using ether UPnP or Nat-PMP.
|
||||
/// NAT PMP has higher priority than UPnP.
|
||||
pub fn map_external_address(local: &NodeEndpoint, nat_type: &NatType) -> Option<NodeEndpoint> {
|
||||
match *nat_type {
|
||||
NatType::Any => {
|
||||
match search_natpmp(local) {
|
||||
Some(end_point) => Some(end_point),
|
||||
None => search_upnp(local),
|
||||
}
|
||||
},
|
||||
NatType::NatPMP => search_natpmp(local),
|
||||
NatType::UPnP => search_upnp(local),
|
||||
_ => {
|
||||
trace!(target: "network", "Can't map external address using NAT");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_select_public_address() {
|
||||
let pub_address = select_public_address(40477);
|
||||
@ -366,9 +449,16 @@ fn can_select_public_address() {
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn can_map_external_address_or_fail() {
|
||||
fn can_map_external_address_upnp_or_fail() {
|
||||
let pub_address = select_public_address(40478);
|
||||
let _ = map_external_address(&NodeEndpoint { address: pub_address, udp_port: 40478 });
|
||||
let _ = map_external_address(&NodeEndpoint { address: pub_address, udp_port: 40478 }, &NatType::UPnP);
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn can_map_external_address_natpmp_or_fail() {
|
||||
let pub_address = select_public_address(40479);
|
||||
let _ = map_external_address(&NodeEndpoint { address: pub_address, udp_port: 40479 }, &NatType::NatPMP);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -174,6 +174,15 @@ impl Ord for SessionCapabilityInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/// Type of NAT resolving method
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum NatType {
|
||||
Nothing,
|
||||
Any,
|
||||
UPnP,
|
||||
NatPMP,
|
||||
}
|
||||
|
||||
/// Network service configuration
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct NetworkConfiguration {
|
||||
@ -189,6 +198,8 @@ pub struct NetworkConfiguration {
|
||||
pub udp_port: Option<u16>,
|
||||
/// Enable NAT configuration
|
||||
pub nat_enabled: bool,
|
||||
/// Nat type
|
||||
pub nat_type: NatType,
|
||||
/// Enable discovery
|
||||
pub discovery_enabled: bool,
|
||||
/// List of initial node addresses
|
||||
@ -229,6 +240,7 @@ impl NetworkConfiguration {
|
||||
public_address: None,
|
||||
udp_port: None,
|
||||
nat_enabled: true,
|
||||
nat_type: NatType::Any,
|
||||
discovery_enabled: true,
|
||||
boot_nodes: Vec::new(),
|
||||
use_secret: None,
|
||||
|
Loading…
Reference in New Issue
Block a user