add restricted nat retries

This commit is contained in:
John Smith 2021-11-23 20:19:16 -05:00
parent 21548771ab
commit e0a52bceb1
6 changed files with 126 additions and 79 deletions

View File

@ -105,108 +105,143 @@ impl Network {
None None
} }
pub async fn update_udpv4_dialinfo_task_routine(self, l: u64, t: u64) -> Result<(), String> { pub async fn update_udpv4_dialinfo_task_routine(self, _l: u64, _t: u64) -> Result<(), String> {
trace!("looking for udpv4 public dial info"); trace!("looking for udpv4 public dial info");
let routing_table = self.routing_table(); let routing_table = self.routing_table();
let mut retry_count = {
let c = self.config.get();
c.network.restricted_nat_retries
};
// Get our local address // Get our local address
let local1 = self.discover_local_address(ProtocolAddressType::UDPv4)?; let local1 = self.discover_local_address(ProtocolAddressType::UDPv4)?;
// Get our external address from some fast node, call it node B
let (external1, node_b) = self
.discover_external_address(ProtocolAddressType::UDPv4, None)
.await?;
let external1_dial_info = DialInfo::udp_from_socketaddr(external1);
// If local1 == external1 then there is no NAT in place // Loop for restricted NAT retries
if local1 == external1 { loop {
// No NAT // Get our external address from some fast node, call it node B
// Do a validate_dial_info on the external address from a routed node let (external1, node_b) = self
if self .discover_external_address(ProtocolAddressType::UDPv4, None)
.validate_dial_info(node_b.clone(), external1_dial_info.clone(), true, false) .await?;
.await let external1_dial_info = DialInfo::udp_from_socketaddr(external1);
{
// Add public dial info with Server network class
routing_table.register_public_dial_info(
external1_dial_info,
Some(NetworkClass::Server),
DialInfoOrigin::Discovered,
);
} else {
// UDP firewall?
warn!("UDP static public dial info not reachable. UDP firewall may be blocking inbound to {:?} for {:?}",external1_dial_info, node_b);
}
} else {
// There is -some NAT-
// Attempt a UDP port mapping via all available and enabled mechanisms
if let Some(external_mapped) = self
.try_port_mapping(local1.clone(), ProtocolAddressType::UDPv4)
.await
{
// Got a port mapping, let's use it
let external_mapped_dial_info = DialInfo::udp_from_socketaddr(external_mapped);
routing_table.register_public_dial_info(
external_mapped_dial_info,
Some(NetworkClass::Mapped),
DialInfoOrigin::Mapped,
);
} else {
// Port mapping was not possible, let's see what kind of NAT we have
// Does a redirected dial info validation find us? // If local1 == external1 then there is no NAT in place
if local1 == external1 {
// No NAT
// Do a validate_dial_info on the external address from a routed node
if self if self
.validate_dial_info(node_b.clone(), external1_dial_info.clone(), true, false) .validate_dial_info(node_b.clone(), external1_dial_info.clone(), true, false)
.await .await
{ {
// Yes, another machine can use the dial info directly, so Full Cone // Add public dial info with Server network class
// Add public dial info with full cone NAT network class
routing_table.register_public_dial_info( routing_table.register_public_dial_info(
external1_dial_info, external1_dial_info,
Some(NetworkClass::FullNAT), Some(NetworkClass::Server),
DialInfoOrigin::Discovered, DialInfoOrigin::Discovered,
); );
} else {
// No, we are restricted, determine what kind of restriction
// Get our external address from some fast node, that is not node B, call it node D // No more retries
let (external2, node_d) = self break;
.discover_external_address( } else {
ProtocolAddressType::UDPv4, // UDP firewall?
Some(node_b.node_id()), warn!("UDP static public dial info not reachable. UDP firewall may be blocking inbound to {:?} for {:?}",external1_dial_info, node_b);
}
} else {
// There is -some NAT-
// Attempt a UDP port mapping via all available and enabled mechanisms
if let Some(external_mapped) = self
.try_port_mapping(local1.clone(), ProtocolAddressType::UDPv4)
.await
{
// Got a port mapping, let's use it
let external_mapped_dial_info = DialInfo::udp_from_socketaddr(external_mapped);
routing_table.register_public_dial_info(
external_mapped_dial_info,
Some(NetworkClass::Mapped),
DialInfoOrigin::Mapped,
);
// No more retries
break;
} else {
// Port mapping was not possible, let's see what kind of NAT we have
// Does a redirected dial info validation find us?
if self
.validate_dial_info(
node_b.clone(),
external1_dial_info.clone(),
true,
false,
) )
.await?; .await
// If we have two different external addresses, then this is a symmetric NAT {
if external2 != external1 { // Yes, another machine can use the dial info directly, so Full Cone
// Symmetric NAT is outbound only, no public dial info will work // Add public dial info with full cone NAT network class
self.inner.lock().network_class = Some(NetworkClass::OutboundOnly); routing_table.register_public_dial_info(
external1_dial_info,
Some(NetworkClass::FullNAT),
DialInfoOrigin::Discovered,
);
// No more retries
break;
} else { } else {
// Address is the same, so it's address or port restricted // No, we are restricted, determine what kind of restriction
let external2_dial_info = DialInfo::udp_from_socketaddr(external2);
// Do a validate_dial_info on the external address from a routed node // Get our external address from some fast node, that is not node B, call it node D
if self let (external2, node_d) = self
.validate_dial_info( .discover_external_address(
node_d.clone(), ProtocolAddressType::UDPv4,
external2_dial_info.clone(), Some(node_b.node_id()),
false,
true,
) )
.await .await?;
{ // If we have two different external addresses, then this is a symmetric NAT
// Got a reply from a non-default port, which means we're only address restricted if external2 != external1 {
routing_table.register_public_dial_info( // Symmetric NAT is outbound only, no public dial info will work
external1_dial_info, self.inner.lock().network_class = Some(NetworkClass::OutboundOnly);
Some(NetworkClass::AddressRestrictedNAT),
DialInfoOrigin::Discovered, // No more retries
); break;
} else { } else {
// Didn't get a reply from a non-default port, which means we are also port restricted // If we're going to end up as a restricted NAT of some sort
routing_table.register_public_dial_info( // we should go through our retries before we assign a dial info
external1_dial_info, if retry_count == 0 {
Some(NetworkClass::PortRestrictedNAT), // Address is the same, so it's address or port restricted
DialInfoOrigin::Discovered, let external2_dial_info = DialInfo::udp_from_socketaddr(external2);
); // Do a validate_dial_info on the external address from a routed node
if self
.validate_dial_info(
node_d.clone(),
external2_dial_info.clone(),
false,
true,
)
.await
{
// Got a reply from a non-default port, which means we're only address restricted
routing_table.register_public_dial_info(
external1_dial_info,
Some(NetworkClass::AddressRestrictedNAT),
DialInfoOrigin::Discovered,
);
} else {
// Didn't get a reply from a non-default port, which means we are also port restricted
routing_table.register_public_dial_info(
external1_dial_info,
Some(NetworkClass::PortRestrictedNAT),
DialInfoOrigin::Discovered,
);
}
}
} }
} }
} }
if retry_count == 0 {
break;
}
retry_count -= 1;
} }
} }

View File

@ -183,6 +183,7 @@ pub fn config_callback(key: String) -> Result<Box<dyn core::any::Any>, String> {
"network.upnp" => Ok(Box::new(false)), "network.upnp" => Ok(Box::new(false)),
"network.natpmp" => Ok(Box::new(false)), "network.natpmp" => Ok(Box::new(false)),
"network.address_filter" => Ok(Box::new(true)), "network.address_filter" => Ok(Box::new(true)),
"network.restricted_nat_retries" => Ok(Box::new(3u32)),
"network.tls.certificate_path" => Ok(Box::new(get_certfile_path())), "network.tls.certificate_path" => Ok(Box::new(get_certfile_path())),
"network.tls.private_key_path" => Ok(Box::new(get_keyfile_path())), "network.tls.private_key_path" => Ok(Box::new(get_keyfile_path())),
"network.tls.connection_initial_timeout" => Ok(Box::new(2_000_000u64)), "network.tls.connection_initial_timeout" => Ok(Box::new(2_000_000u64)),
@ -270,6 +271,7 @@ pub async fn test_config() {
assert_eq!(inner.network.upnp, false); assert_eq!(inner.network.upnp, false);
assert_eq!(inner.network.natpmp, false); assert_eq!(inner.network.natpmp, false);
assert_eq!(inner.network.address_filter, true); assert_eq!(inner.network.address_filter, true);
assert_eq!(inner.network.restricted_nat_retries, 3u32);
assert_eq!(inner.network.tls.certificate_path, get_certfile_path()); assert_eq!(inner.network.tls.certificate_path, get_certfile_path());
assert_eq!(inner.network.tls.private_key_path, get_keyfile_path()); assert_eq!(inner.network.tls.private_key_path, get_keyfile_path());
assert_eq!(inner.network.tls.connection_initial_timeout, 2_000_000u64); assert_eq!(inner.network.tls.connection_initial_timeout, 2_000_000u64);

View File

@ -128,6 +128,7 @@ pub struct VeilidConfigNetwork {
pub upnp: bool, pub upnp: bool,
pub natpmp: bool, pub natpmp: bool,
pub address_filter: bool, pub address_filter: bool,
pub restricted_nat_retries: u32,
pub tls: VeilidConfigTLS, pub tls: VeilidConfigTLS,
pub application: VeilidConfigApplication, pub application: VeilidConfigApplication,
pub protocol: VeilidConfigProtocol, pub protocol: VeilidConfigProtocol,
@ -222,6 +223,7 @@ impl VeilidConfig {
get_config!(inner.network.upnp); get_config!(inner.network.upnp);
get_config!(inner.network.natpmp); get_config!(inner.network.natpmp);
get_config!(inner.network.address_filter); get_config!(inner.network.address_filter);
get_config!(inner.network.restricted_nat_retries);
get_config!(inner.network.tls.certificate_path); get_config!(inner.network.tls.certificate_path);
get_config!(inner.network.tls.private_key_path); get_config!(inner.network.tls.private_key_path);
get_config!(inner.network.tls.connection_initial_timeout); get_config!(inner.network.tls.connection_initial_timeout);

View File

@ -63,6 +63,7 @@ core:
upnp: false upnp: false
natpmp: false natpmp: false
address_filter: true address_filter: true
restricted_nat_retries: 3
tls: tls:
certificate_path: "/etc/veilid/server.crt" certificate_path: "/etc/veilid/server.crt"
private_key_path: "/etc/veilid/private/server.key" private_key_path: "/etc/veilid/private/server.key"
@ -391,6 +392,7 @@ pub struct Network {
pub upnp: bool, pub upnp: bool,
pub natpmp: bool, pub natpmp: bool,
pub address_filter: bool, pub address_filter: bool,
pub restricted_nat_retries: u32,
pub tls: TLS, pub tls: TLS,
pub application: Application, pub application: Application,
pub protocol: Protocol, pub protocol: Protocol,
@ -638,6 +640,9 @@ impl Settings {
"network.upnp" => Ok(Box::new(inner.core.network.upnp)), "network.upnp" => Ok(Box::new(inner.core.network.upnp)),
"network.natpmp" => Ok(Box::new(inner.core.network.natpmp)), "network.natpmp" => Ok(Box::new(inner.core.network.natpmp)),
"network.address_filter" => Ok(Box::new(inner.core.network.address_filter)), "network.address_filter" => Ok(Box::new(inner.core.network.address_filter)),
"network.restricted_nat_retries" => {
Ok(Box::new(inner.core.network.restricted_nat_retries))
}
"network.tls.certificate_path" => Ok(Box::new( "network.tls.certificate_path" => Ok(Box::new(
inner inner
.core .core
@ -869,6 +874,7 @@ mod tests {
assert_eq!(s.core.network.upnp, false); assert_eq!(s.core.network.upnp, false);
assert_eq!(s.core.network.natpmp, false); assert_eq!(s.core.network.natpmp, false);
assert_eq!(s.core.network.address_filter, true); assert_eq!(s.core.network.address_filter, true);
assert_eq!(s.core.network.restricted_nat_retries, 3u32);
// //
assert_eq!( assert_eq!(
s.core.network.tls.certificate_path, s.core.network.tls.certificate_path,

View File

@ -120,6 +120,7 @@ impl JsVeilidCore {
"network.upnp" => Self::value_to_bool(val), "network.upnp" => Self::value_to_bool(val),
"network.natpmp" => Self::value_to_bool(val), "network.natpmp" => Self::value_to_bool(val),
"network.address_filter" => Self::value_to_bool(val), "network.address_filter" => Self::value_to_bool(val),
"network.restricted_nat_retries" => Self::value_to_u32(val),
"network.tls.certificate_path" => Self::value_to_string(val), "network.tls.certificate_path" => Self::value_to_string(val),
"network.tls.private_key_path" => Self::value_to_string(val), "network.tls.private_key_path" => Self::value_to_string(val),
"network.application.path" => Self::value_to_string(val), "network.application.path" => Self::value_to_string(val),

View File

@ -63,6 +63,7 @@ fn init_callbacks() {
case "network.upnp": return false; case "network.upnp": return false;
case "network.natpmp": return false; case "network.natpmp": return false;
case "network.address_filter": return true; case "network.address_filter": return true;
case "network.restricted_nat_retries": return 3;
case "network.tls.certificate_path": return ""; case "network.tls.certificate_path": return "";
case "network.tls.private_key_path": return ""; case "network.tls.private_key_path": return "";
case "network.application.path": return "/app"; case "network.application.path": return "/app";