speed up public address detection
This commit is contained in:
parent
4eca53fd9b
commit
945215aba1
@ -90,7 +90,7 @@ pub static DEFAULT_LOG_IGNORE_LIST: [&str; 23] = [
|
|||||||
use cfg_if::*;
|
use cfg_if::*;
|
||||||
use enumset::*;
|
use enumset::*;
|
||||||
use eyre::{bail, eyre, Report as EyreReport, Result as EyreResult, WrapErr};
|
use eyre::{bail, eyre, Report as EyreReport, Result as EyreResult, WrapErr};
|
||||||
use futures_util::stream::FuturesUnordered;
|
use futures_util::stream::{FuturesOrdered, FuturesUnordered};
|
||||||
use parking_lot::*;
|
use parking_lot::*;
|
||||||
use schemars::{schema_for, JsonSchema};
|
use schemars::{schema_for, JsonSchema};
|
||||||
use serde::*;
|
use serde::*;
|
||||||
|
@ -27,12 +27,20 @@ struct DiscoveryContextInner {
|
|||||||
detected_public_dial_info: Option<DetectedPublicDialInfo>,
|
detected_public_dial_info: Option<DetectedPublicDialInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct DiscoveryContext {
|
pub struct DiscoveryContext {
|
||||||
routing_table: RoutingTable,
|
routing_table: RoutingTable,
|
||||||
net: Network,
|
net: Network,
|
||||||
inner: Arc<Mutex<DiscoveryContextInner>>,
|
inner: Arc<Mutex<DiscoveryContextInner>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct DetectedDialInfo {
|
||||||
|
dial_info: DialInfo,
|
||||||
|
dial_info_class: DialInfoClass,
|
||||||
|
network_class: NetworkClass,
|
||||||
|
}
|
||||||
|
|
||||||
impl DiscoveryContext {
|
impl DiscoveryContext {
|
||||||
pub fn new(routing_table: RoutingTable, net: Network) -> Self {
|
pub fn new(routing_table: RoutingTable, net: Network) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -412,53 +420,74 @@ impl DiscoveryContext {
|
|||||||
// If we know we are not behind NAT, check our firewall status
|
// If we know we are not behind NAT, check our firewall status
|
||||||
#[instrument(level = "trace", skip(self), err)]
|
#[instrument(level = "trace", skip(self), err)]
|
||||||
pub async fn protocol_process_no_nat(&self) -> EyreResult<()> {
|
pub async fn protocol_process_no_nat(&self) -> EyreResult<()> {
|
||||||
let (node_1, external_1_dial_info) = {
|
// Do these detections in parallel, but with ordering preference
|
||||||
let inner = self.inner.lock();
|
let mut ord = FuturesOrdered::new();
|
||||||
(
|
|
||||||
inner.node_1.as_ref().unwrap().clone(),
|
|
||||||
inner.external_1_dial_info.as_ref().unwrap().clone(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Attempt a port mapping via all available and enabled mechanisms
|
// UPNP Automatic Mapping
|
||||||
// Try this before the direct mapping in the event that we are restarting
|
///////////
|
||||||
// and may not have recorded a mapping created the last time
|
let this = self.clone();
|
||||||
if let Some(external_mapped_dial_info) = self.try_port_mapping().await {
|
let do_mapped_fut: SendPinBoxFuture<Option<DetectedDialInfo>> = Box::pin(async move {
|
||||||
// Got a port mapping, let's use it
|
// Attempt a port mapping via all available and enabled mechanisms
|
||||||
self.set_detected_public_dial_info(external_mapped_dial_info, DialInfoClass::Mapped);
|
// Try this before the direct mapping in the event that we are restarting
|
||||||
self.set_detected_network_class(NetworkClass::InboundCapable);
|
// and may not have recorded a mapping created the last time
|
||||||
}
|
if let Some(external_mapped_dial_info) = this.try_port_mapping().await {
|
||||||
// Do a validate_dial_info on the external address from a redirected node
|
// Got a port mapping, let's use it
|
||||||
else if self
|
return Some(DetectedDialInfo {
|
||||||
.validate_dial_info(node_1.clone(), external_1_dial_info.clone(), true)
|
dial_info: external_mapped_dial_info.clone(),
|
||||||
.await
|
dial_info_class: DialInfoClass::Mapped,
|
||||||
{
|
network_class: NetworkClass::InboundCapable,
|
||||||
// Add public dial info with Direct dialinfo class
|
});
|
||||||
self.set_detected_public_dial_info(external_1_dial_info, DialInfoClass::Direct);
|
}
|
||||||
self.set_detected_network_class(NetworkClass::InboundCapable);
|
None
|
||||||
} else {
|
});
|
||||||
// Add public dial info with Blocked dialinfo class
|
ord.push_back(do_mapped_fut);
|
||||||
self.set_detected_public_dial_info(external_1_dial_info, DialInfoClass::Blocked);
|
|
||||||
self.set_detected_network_class(NetworkClass::InboundCapable);
|
let this = self.clone();
|
||||||
|
let do_direct_fut: SendPinBoxFuture<Option<DetectedDialInfo>> = Box::pin(async move {
|
||||||
|
let (node_1, external_1_dial_info) = {
|
||||||
|
let inner = this.inner.lock();
|
||||||
|
(
|
||||||
|
inner.node_1.as_ref().unwrap().clone(),
|
||||||
|
inner.external_1_dial_info.as_ref().unwrap().clone(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
// Do a validate_dial_info on the external address from a redirected node
|
||||||
|
if this
|
||||||
|
.validate_dial_info(node_1.clone(), external_1_dial_info.clone(), true)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
// Add public dial info with Direct dialinfo class
|
||||||
|
Some(DetectedDialInfo {
|
||||||
|
dial_info: external_1_dial_info.clone(),
|
||||||
|
dial_info_class: DialInfoClass::Direct,
|
||||||
|
network_class: NetworkClass::InboundCapable,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Add public dial info with Blocked dialinfo class
|
||||||
|
Some(DetectedDialInfo {
|
||||||
|
dial_info: external_1_dial_info.clone(),
|
||||||
|
dial_info_class: DialInfoClass::Blocked,
|
||||||
|
network_class: NetworkClass::InboundCapable,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ord.push_back(do_direct_fut);
|
||||||
|
|
||||||
|
while let Some(res) = ord.next().await {
|
||||||
|
if let Some(ddi) = res {
|
||||||
|
self.set_detected_public_dial_info(ddi.dial_info, ddi.dial_info_class);
|
||||||
|
self.set_detected_network_class(ddi.network_class);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we know we are behind NAT check what kind
|
// If we know we are behind NAT check what kind
|
||||||
#[instrument(level = "trace", skip(self), ret, err)]
|
#[instrument(level = "trace", skip(self), ret, err)]
|
||||||
pub async fn protocol_process_nat(&self) -> EyreResult<bool> {
|
pub async fn protocol_process_nat(&self) -> EyreResult<bool> {
|
||||||
// Attempt a port mapping via all available and enabled mechanisms
|
|
||||||
// Try this before the direct mapping in the event that we are restarting
|
|
||||||
// and may not have recorded a mapping created the last time
|
|
||||||
if let Some(external_mapped_dial_info) = self.try_port_mapping().await {
|
|
||||||
// Got a port mapping, let's use it
|
|
||||||
self.set_detected_public_dial_info(external_mapped_dial_info, DialInfoClass::Mapped);
|
|
||||||
self.set_detected_network_class(NetworkClass::InboundCapable);
|
|
||||||
|
|
||||||
// No more retries
|
|
||||||
return Ok(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the external dial info for our use here
|
// Get the external dial info for our use here
|
||||||
let (node_1, external_1_dial_info, external_1_address, protocol_type, address_type) = {
|
let (node_1, external_1_dial_info, external_1_address, protocol_type, address_type) = {
|
||||||
let inner = self.inner.lock();
|
let inner = self.inner.lock();
|
||||||
@ -471,51 +500,111 @@ impl DiscoveryContext {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Do a validate_dial_info on the external address, but with the same port as the local port of local interface, from a redirected node
|
// Do these detections in parallel, but with ordering preference
|
||||||
// This test is to see if a node had manual port forwarding done with the same port number as the local listener
|
let mut ord = FuturesOrdered::new();
|
||||||
if let Some(local_port) = self.net.get_local_port(protocol_type) {
|
|
||||||
if external_1_dial_info.port() != local_port {
|
|
||||||
let mut external_1_dial_info_with_local_port = external_1_dial_info.clone();
|
|
||||||
external_1_dial_info_with_local_port.set_port(local_port);
|
|
||||||
|
|
||||||
if self
|
// UPNP Automatic Mapping
|
||||||
.validate_dial_info(
|
///////////
|
||||||
node_1.clone(),
|
let this = self.clone();
|
||||||
external_1_dial_info_with_local_port.clone(),
|
let do_mapped_fut: SendPinBoxFuture<Option<DetectedDialInfo>> = Box::pin(async move {
|
||||||
true,
|
// Attempt a port mapping via all available and enabled mechanisms
|
||||||
)
|
// Try this before the direct mapping in the event that we are restarting
|
||||||
.await
|
// and may not have recorded a mapping created the last time
|
||||||
{
|
if let Some(external_mapped_dial_info) = this.try_port_mapping().await {
|
||||||
// Add public dial info with Direct dialinfo class
|
// Got a port mapping, let's use it
|
||||||
self.set_detected_public_dial_info(
|
return Some(DetectedDialInfo {
|
||||||
external_1_dial_info_with_local_port,
|
dial_info: external_mapped_dial_info.clone(),
|
||||||
DialInfoClass::Direct,
|
dial_info_class: DialInfoClass::Mapped,
|
||||||
);
|
network_class: NetworkClass::InboundCapable,
|
||||||
self.set_detected_network_class(NetworkClass::InboundCapable);
|
});
|
||||||
return Ok(true);
|
}
|
||||||
}
|
None
|
||||||
|
});
|
||||||
|
ord.push_back(do_mapped_fut);
|
||||||
|
|
||||||
|
// Manual Mapping Detection
|
||||||
|
///////////
|
||||||
|
let this = self.clone();
|
||||||
|
if let Some(local_port) = this.net.get_local_port(protocol_type) {
|
||||||
|
if external_1_dial_info.port() != local_port {
|
||||||
|
let c_external_1_dial_info = external_1_dial_info.clone();
|
||||||
|
let c_node_1 = node_1.clone();
|
||||||
|
let do_manual_map_fut: SendPinBoxFuture<Option<DetectedDialInfo>> =
|
||||||
|
Box::pin(async move {
|
||||||
|
// Do a validate_dial_info on the external address, but with the same port as the local port of local interface, from a redirected node
|
||||||
|
// This test is to see if a node had manual port forwarding done with the same port number as the local listener
|
||||||
|
let mut external_1_dial_info_with_local_port =
|
||||||
|
c_external_1_dial_info.clone();
|
||||||
|
external_1_dial_info_with_local_port.set_port(local_port);
|
||||||
|
|
||||||
|
if this
|
||||||
|
.validate_dial_info(
|
||||||
|
c_node_1.clone(),
|
||||||
|
external_1_dial_info_with_local_port.clone(),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
// Add public dial info with Direct dialinfo class
|
||||||
|
return Some(DetectedDialInfo {
|
||||||
|
dial_info: external_1_dial_info_with_local_port,
|
||||||
|
dial_info_class: DialInfoClass::Direct,
|
||||||
|
network_class: NetworkClass::InboundCapable,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
});
|
||||||
|
ord.push_back(do_manual_map_fut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Port mapping was not possible, and things aren't accessible directly.
|
// Full Cone NAT Detection
|
||||||
// Let's see what kind of NAT we have
|
///////////
|
||||||
|
let this = self.clone();
|
||||||
|
let c_node_1 = node_1.clone();
|
||||||
|
let c_external_1_dial_info = external_1_dial_info.clone();
|
||||||
|
let do_full_cone_fut: SendPinBoxFuture<Option<DetectedDialInfo>> = Box::pin(async move {
|
||||||
|
// Let's see what kind of NAT we have
|
||||||
|
// Does a redirected dial info validation from a different address and a random port find us?
|
||||||
|
if this
|
||||||
|
.validate_dial_info(c_node_1.clone(), c_external_1_dial_info.clone(), true)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
// Yes, another machine can use the dial info directly, so Full Cone
|
||||||
|
// Add public dial info with full cone NAT network class
|
||||||
|
|
||||||
// Does a redirected dial info validation from a different address and a random port find us?
|
return Some(DetectedDialInfo {
|
||||||
if self
|
dial_info: c_external_1_dial_info,
|
||||||
.validate_dial_info(node_1.clone(), external_1_dial_info.clone(), true)
|
dial_info_class: DialInfoClass::FullConeNAT,
|
||||||
.await
|
network_class: NetworkClass::InboundCapable,
|
||||||
{
|
});
|
||||||
// Yes, another machine can use the dial info directly, so Full Cone
|
}
|
||||||
// Add public dial info with full cone NAT network class
|
None
|
||||||
self.set_detected_public_dial_info(external_1_dial_info, DialInfoClass::FullConeNAT);
|
});
|
||||||
self.set_detected_network_class(NetworkClass::InboundCapable);
|
ord.push_back(do_full_cone_fut);
|
||||||
|
|
||||||
// No more retries
|
// Run detections in parallel and take the first one, ordered by preference, that returns a result
|
||||||
return Ok(true);
|
while let Some(res) = ord.next().await {
|
||||||
|
if let Some(ddi) = res {
|
||||||
|
self.set_detected_public_dial_info(ddi.dial_info, ddi.dial_info_class);
|
||||||
|
self.set_detected_network_class(ddi.network_class);
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No, we are restricted, determine what kind of restriction
|
// We are restricted, determine what kind of restriction
|
||||||
|
// Get the external dial info for our use here
|
||||||
|
let (node_1, external_1_dial_info, external_1_address, protocol_type, address_type) = {
|
||||||
|
let inner = self.inner.lock();
|
||||||
|
(
|
||||||
|
inner.node_1.as_ref().unwrap().clone(),
|
||||||
|
inner.external_1_dial_info.as_ref().unwrap().clone(),
|
||||||
|
inner.external_1_address.unwrap(),
|
||||||
|
inner.protocol_type.unwrap(),
|
||||||
|
inner.address_type.unwrap(),
|
||||||
|
)
|
||||||
|
};
|
||||||
// Get our external address from some fast node, that is not node 1, call it node 2
|
// Get our external address from some fast node, that is not node 1, call it node 2
|
||||||
let (external_2_address, node_2) = match self
|
let (external_2_address, node_2) = match self
|
||||||
.discover_external_address(protocol_type, address_type, Some(node_1.node_ids()))
|
.discover_external_address(protocol_type, address_type, Some(node_1.node_ids()))
|
||||||
@ -589,7 +678,7 @@ impl Network {
|
|||||||
// Loop for restricted NAT retries
|
// Loop for restricted NAT retries
|
||||||
loop {
|
loop {
|
||||||
log_net!(debug
|
log_net!(debug
|
||||||
"=== update_protocol_dialino {:?} {:?} tries_left={} ===",
|
"=== update_protocol_dialinfo {:?} {:?} tries_left={} ===",
|
||||||
address_type,
|
address_type,
|
||||||
protocol_type,
|
protocol_type,
|
||||||
retry_count
|
retry_count
|
||||||
|
Loading…
Reference in New Issue
Block a user