From 91031531e4a6ca5457d457f96d60d4de9c273a00 Mon Sep 17 00:00:00 2001 From: Christien Rioux Date: Thu, 28 Sep 2023 21:54:31 -0400 Subject: [PATCH] address type detection --- veilid-core/src/network_manager/mod.rs | 4 +- .../native/discovery_context.rs | 137 ++++++++++++------ veilid-core/src/network_manager/native/mod.rs | 46 +++--- .../native/network_class_discovery.rs | 32 +++- veilid-core/src/network_manager/wasm/mod.rs | 25 ++-- 5 files changed, 158 insertions(+), 86 deletions(-) diff --git a/veilid-core/src/network_manager/mod.rs b/veilid-core/src/network_manager/mod.rs index 473d4d77..688c9c03 100644 --- a/veilid-core/src/network_manager/mod.rs +++ b/veilid-core/src/network_manager/mod.rs @@ -65,12 +65,14 @@ pub const PUBLIC_ADDRESS_INCONSISTENCY_PUNISHMENT_TIMEOUT_US: TimestampDuration pub const ADDRESS_FILTER_TASK_INTERVAL_SECS: u32 = 60; pub const BOOT_MAGIC: &[u8; 4] = b"BOOT"; -#[derive(Copy, Clone, Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct ProtocolConfig { pub outbound: ProtocolTypeSet, pub inbound: ProtocolTypeSet, pub family_global: AddressTypeSet, pub family_local: AddressTypeSet, + pub public_internet_capabilities: Vec, + pub local_network_capabilities: Vec, } // Things we get when we start up and go away when we shut down diff --git a/veilid-core/src/network_manager/native/discovery_context.rs b/veilid-core/src/network_manager/native/discovery_context.rs index 81c5ea3c..2837ed4c 100644 --- a/veilid-core/src/network_manager/native/discovery_context.rs +++ b/veilid-core/src/network_manager/native/discovery_context.rs @@ -14,6 +14,13 @@ pub enum DetectedDialInfo { Detected(DialInfoDetail), } +// Detection result of external address +#[derive(Clone, Debug)] +pub struct DetectionResult { + pub ddi: DetectedDialInfo, + pub external_address_types: AddressTypeSet, +} + // Result of checking external address #[derive(Clone, Debug)] struct ExternalInfo { @@ -380,28 +387,34 @@ impl DiscoveryContext { #[instrument(level = "trace", skip(self), ret)] async fn protocol_process_no_nat( &self, - unord: &mut FuturesUnordered>>, + unord: &mut FuturesUnordered>>, ) { let external_1 = self.inner.lock().external_1.as_ref().unwrap().clone(); let this = self.clone(); - let do_no_nat_fut: SendPinBoxFuture> = Box::pin(async move { + let do_no_nat_fut: SendPinBoxFuture> = Box::pin(async move { // Do a validate_dial_info on the external address from a redirected node if this .validate_dial_info(external_1.node.clone(), external_1.dial_info.clone(), true) .await { // Add public dial info with Direct dialinfo class - Some(DetectedDialInfo::Detected(DialInfoDetail { - dial_info: external_1.dial_info.clone(), - class: DialInfoClass::Direct, - })) + Some(DetectionResult { + ddi: DetectedDialInfo::Detected(DialInfoDetail { + dial_info: external_1.dial_info.clone(), + class: DialInfoClass::Direct, + }), + external_address_types: AddressTypeSet::only(external_1.address.address_type()), + }) } else { // Add public dial info with Blocked dialinfo class - Some(DetectedDialInfo::Detected(DialInfoDetail { - dial_info: external_1.dial_info.clone(), - class: DialInfoClass::Blocked, - })) + Some(DetectionResult { + ddi: DetectedDialInfo::Detected(DialInfoDetail { + dial_info: external_1.dial_info.clone(), + class: DialInfoClass::Blocked, + }), + external_address_types: AddressTypeSet::only(external_1.address.address_type()), + }) } }); unord.push(do_no_nat_fut); @@ -411,7 +424,7 @@ impl DiscoveryContext { #[instrument(level = "trace", skip(self), ret)] async fn protocol_process_nat( &self, - unord: &mut FuturesUnordered>>, + unord: &mut FuturesUnordered>>, ) { // Get the external dial info for our use here let (external_1, external_2) = { @@ -424,8 +437,17 @@ impl DiscoveryContext { // If we have two different external addresses, then this is a symmetric NAT if external_2.address.address() != external_1.address.address() { - let do_symmetric_nat_fut: SendPinBoxFuture> = - Box::pin(async move { Some(DetectedDialInfo::SymmetricNAT) }); + let do_symmetric_nat_fut: SendPinBoxFuture> = + Box::pin(async move { + Some(DetectionResult { + ddi: DetectedDialInfo::SymmetricNAT, + external_address_types: AddressTypeSet::only( + external_1.address.address_type(), + ) | AddressTypeSet::only( + external_2.address.address_type(), + ), + }) + }); unord.push(do_symmetric_nat_fut); return; } @@ -440,7 +462,7 @@ impl DiscoveryContext { { if external_1.dial_info.port() != local_port { let c_external_1 = external_1.clone(); - let do_manual_map_fut: SendPinBoxFuture> = + let do_manual_map_fut: SendPinBoxFuture> = 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 @@ -457,10 +479,15 @@ impl DiscoveryContext { .await { // Add public dial info with Direct dialinfo class - return Some(DetectedDialInfo::Detected(DialInfoDetail { - dial_info: external_1_dial_info_with_local_port, - class: DialInfoClass::Direct, - })); + return Some(DetectionResult { + ddi: DetectedDialInfo::Detected(DialInfoDetail { + dial_info: external_1_dial_info_with_local_port, + class: DialInfoClass::Direct, + }), + external_address_types: AddressTypeSet::only( + c_external_1.address.address_type(), + ), + }); } None @@ -475,7 +502,7 @@ impl DiscoveryContext { // Full Cone NAT Detection /////////// let this = self.clone(); - let do_nat_detect_fut: SendPinBoxFuture> = Box::pin(async move { + let do_nat_detect_fut: SendPinBoxFuture> = Box::pin(async move { let mut retry_count = { let c = this.unlocked_inner.net.config.get(); c.network.restricted_nat_retries @@ -487,7 +514,7 @@ impl DiscoveryContext { let c_this = this.clone(); let c_external_1 = external_1.clone(); - let do_full_cone_fut: SendPinBoxFuture> = + let do_full_cone_fut: SendPinBoxFuture> = 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? @@ -502,10 +529,15 @@ impl DiscoveryContext { // Yes, another machine can use the dial info directly, so Full Cone // Add public dial info with full cone NAT network class - return Some(DetectedDialInfo::Detected(DialInfoDetail { - dial_info: c_external_1.dial_info, - class: DialInfoClass::FullConeNAT, - })); + return Some(DetectionResult { + ddi: DetectedDialInfo::Detected(DialInfoDetail { + dial_info: c_external_1.dial_info, + class: DialInfoClass::FullConeNAT, + }), + external_address_types: AddressTypeSet::only( + c_external_1.address.address_type(), + ), + }); } None }); @@ -514,7 +546,7 @@ impl DiscoveryContext { let c_this = this.clone(); let c_external_1 = external_1.clone(); let c_external_2 = external_2.clone(); - let do_restricted_cone_fut: SendPinBoxFuture> = + let do_restricted_cone_fut: SendPinBoxFuture> = Box::pin(async move { // We are restricted, determine what kind of restriction @@ -531,33 +563,43 @@ impl DiscoveryContext { .await { // Got a reply from a non-default port, which means we're only address restricted - return Some(DetectedDialInfo::Detected(DialInfoDetail { - dial_info: c_external_1.dial_info.clone(), - class: DialInfoClass::AddressRestrictedNAT, - })); + return Some(DetectionResult { + ddi: DetectedDialInfo::Detected(DialInfoDetail { + dial_info: c_external_1.dial_info.clone(), + class: DialInfoClass::AddressRestrictedNAT, + }), + external_address_types: AddressTypeSet::only( + c_external_1.address.address_type(), + ), + }); } // Didn't get a reply from a non-default port, which means we are also port restricted - Some(DetectedDialInfo::Detected(DialInfoDetail { - dial_info: c_external_1.dial_info.clone(), - class: DialInfoClass::PortRestrictedNAT, - })) + Some(DetectionResult { + ddi: DetectedDialInfo::Detected(DialInfoDetail { + dial_info: c_external_1.dial_info.clone(), + class: DialInfoClass::PortRestrictedNAT, + }), + external_address_types: AddressTypeSet::only( + c_external_1.address.address_type(), + ), + }) }); ord.push_back(do_restricted_cone_fut); // Return the first result we get - let mut some_ddi = None; + let mut some_dr = None; while let Some(res) = ord.next().await { - if let Some(ddi) = res { - some_ddi = Some(ddi); + if let Some(dr) = res { + some_dr = Some(dr); break; } } - if let Some(ddi) = some_ddi { - if let DetectedDialInfo::Detected(did) = &ddi { + if let Some(dr) = some_dr { + if let DetectedDialInfo::Detected(did) = &dr.ddi { // If we got something better than restricted NAT or we're done retrying if did.class < DialInfoClass::AddressRestrictedNAT || retry_count == 0 { - return Some(ddi); + return Some(dr); } } } @@ -575,7 +617,7 @@ impl DiscoveryContext { /// Add discovery futures to an unordered set that may detect dialinfo when they complete pub async fn discover( &self, - unord: &mut FuturesUnordered>>, + unord: &mut FuturesUnordered>>, ) { let enable_upnp = { let c = self.unlocked_inner.net.config.get(); @@ -593,16 +635,21 @@ impl DiscoveryContext { /////////// if enable_upnp { let this = self.clone(); - let do_mapped_fut: SendPinBoxFuture> = Box::pin(async move { + let do_mapped_fut: SendPinBoxFuture> = Box::pin(async move { // 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) = this.try_upnp_port_mapping().await { // Got a port mapping, let's use it - return Some(DetectedDialInfo::Detected(DialInfoDetail { - dial_info: external_mapped_dial_info.clone(), - class: DialInfoClass::Mapped, - })); + return Some(DetectionResult { + ddi: DetectedDialInfo::Detected(DialInfoDetail { + dial_info: external_mapped_dial_info.clone(), + class: DialInfoClass::Mapped, + }), + external_address_types: AddressTypeSet::only( + external_mapped_dial_info.address_type(), + ), + }); } None }); diff --git a/veilid-core/src/network_manager/native/mod.rs b/veilid-core/src/network_manager/native/mod.rs index 0cd77f0b..072bbdcc 100644 --- a/veilid-core/src/network_manager/native/mod.rs +++ b/veilid-core/src/network_manager/native/mod.rs @@ -684,7 +684,7 @@ impl Network { ///////////////////////////////////////////////////////////////// pub fn get_protocol_config(&self) -> ProtocolConfig { - self.inner.lock().protocol_config + self.inner.lock().protocol_config.clone() } #[instrument(level = "debug", err, skip_all)] @@ -790,14 +790,33 @@ impl Network { family_local.insert(AddressType::IPV6); } + // set up the routing table's network config + // if we have static public dialinfo, upgrade our network class + let public_internet_capabilities = { + PUBLIC_INTERNET_CAPABILITIES + .iter() + .copied() + .filter(|cap| !c.capabilities.disable.contains(cap)) + .collect::>() + }; + let local_network_capabilities = { + LOCAL_NETWORK_CAPABILITIES + .iter() + .copied() + .filter(|cap| !c.capabilities.disable.contains(cap)) + .collect::>() + }; + ProtocolConfig { outbound, inbound, family_global, family_local, + public_internet_capabilities, + local_network_capabilities, } }; - inner.protocol_config = protocol_config; + inner.protocol_config = protocol_config.clone(); protocol_config }; @@ -835,36 +854,17 @@ impl Network { // that we have ports available to us self.free_bound_first_ports(); - // set up the routing table's network config - // if we have static public dialinfo, upgrade our network class - let public_internet_capabilities = { - let c = self.config.get(); - PUBLIC_INTERNET_CAPABILITIES - .iter() - .copied() - .filter(|cap| !c.capabilities.disable.contains(cap)) - .collect::>() - }; - let local_network_capabilities = { - let c = self.config.get(); - LOCAL_NETWORK_CAPABILITIES - .iter() - .copied() - .filter(|cap| !c.capabilities.disable.contains(cap)) - .collect::>() - }; - editor_public_internet.setup_network( protocol_config.outbound, protocol_config.inbound, protocol_config.family_global, - public_internet_capabilities, + protocol_config.public_internet_capabilities, ); editor_local_network.setup_network( protocol_config.outbound, protocol_config.inbound, protocol_config.family_local, - local_network_capabilities, + protocol_config.local_network_capabilities, ); let detect_address_changes = { let c = self.config.get(); diff --git a/veilid-core/src/network_manager/native/network_class_discovery.rs b/veilid-core/src/network_manager/native/network_class_discovery.rs index adcdb92d..c0336fdd 100644 --- a/veilid-core/src/network_manager/native/network_class_discovery.rs +++ b/veilid-core/src/network_manager/native/network_class_discovery.rs @@ -104,7 +104,7 @@ impl Network { // Figure out if we can optimize TCP/WS checking since they are often on the same port let (protocol_config, tcp_same_port) = { let inner = self.inner.lock(); - let protocol_config = inner.protocol_config; + let protocol_config = inner.protocol_config.clone(); let tcp_same_port = if protocol_config.inbound.contains(ProtocolType::TCP) && protocol_config.inbound.contains(ProtocolType::WS) { @@ -126,9 +126,16 @@ impl Network { .collect(); // Clear public dialinfo and network class in prep for discovery + let mut editor = self .routing_table() .edit_routing_domain(RoutingDomain::PublicInternet); + editor.setup_network( + protocol_config.outbound, + protocol_config.inbound, + protocol_config.family_global, + protocol_config.public_internet_capabilities.clone(), + ); editor.clear_dial_info_details(None, None); editor.set_network_class(None); editor.clear_relay_node(); @@ -227,14 +234,18 @@ impl Network { } // Wait for all discovery futures to complete and apply discoverycontexts + let mut all_address_types = AddressTypeSet::new(); loop { match unord.next().timeout_at(stop_token.clone()).await { - Ok(Some(Some(ddi))) => { + Ok(Some(Some(dr))) => { // Found some new dial info for this protocol/address combination - self.update_with_detected_dial_info(ddi.clone()).await?; + self.update_with_detected_dial_info(dr.ddi.clone()).await?; + + // Add the external address kinds to the set we've seen + all_address_types |= dr.external_address_types; // Add WS dialinfo as well if it is on the same port as TCP - if let DetectedDialInfo::Detected(did) = &ddi { + if let DetectedDialInfo::Detected(did) = &dr.ddi { if did.dial_info.protocol_type() == ProtocolType::TCP && tcp_same_port { // Make WS dialinfo as well with same socket address as TCP let ws_ddi = DetectedDialInfo::Detected(DialInfoDetail { @@ -263,7 +274,18 @@ impl Network { } } - // All done, see if things changed + // All done + + // Set the address types we've seen + editor.setup_network( + protocol_config.outbound, + protocol_config.inbound, + all_address_types, + protocol_config.public_internet_capabilities, + ); + editor.commit(true).await; + + // See if the dial info changed let new_public_dial_info: HashSet = self .routing_table() .all_filtered_dial_info_details( diff --git a/veilid-core/src/network_manager/wasm/mod.rs b/veilid-core/src/network_manager/wasm/mod.rs index 5924c5eb..3de5c95a 100644 --- a/veilid-core/src/network_manager/wasm/mod.rs +++ b/veilid-core/src/network_manager/wasm/mod.rs @@ -349,14 +349,24 @@ impl Network { let family_global = AddressTypeSet::from(AddressType::IPV4); let family_local = AddressTypeSet::from(AddressType::IPV4); + let public_internet_capabilities = { + PUBLIC_INTERNET_CAPABILITIES + .iter() + .copied() + .filter(|cap| !c.capabilities.disable.contains(cap)) + .collect::>() + }; + ProtocolConfig { outbound, inbound, family_global, family_local, + local_network_capabilities: vec![], + public_internet_capabilities, } }; - self.inner.lock().protocol_config = protocol_config; + self.inner.lock().protocol_config = protocol_config.clone(); // Start editing routing table let mut editor_public_internet = self @@ -367,20 +377,11 @@ impl Network { // set up the routing table's network config // if we have static public dialinfo, upgrade our network class - let public_internet_capabilities = { - let c = self.config.get(); - PUBLIC_INTERNET_CAPABILITIES - .iter() - .copied() - .filter(|cap| !c.capabilities.disable.contains(cap)) - .collect::>() - }; - editor_public_internet.setup_network( protocol_config.outbound, protocol_config.inbound, protocol_config.family_global, - public_internet_capabilities, + protocol_config.public_internet_capabilities.clone(), ); editor_public_internet.set_network_class(Some(NetworkClass::WebApp)); @@ -454,7 +455,7 @@ impl Network { } pub fn get_protocol_config(&self) -> ProtocolConfig { - self.inner.lock().protocol_config + self.inner.lock().protocol_config.clone() } //////////////////////////////////////////