diff --git a/veilid-core/src/network_manager/mod.rs b/veilid-core/src/network_manager/mod.rs index f5f8ef49..9ec0a8cf 100644 --- a/veilid-core/src/network_manager/mod.rs +++ b/veilid-core/src/network_manager/mod.rs @@ -45,6 +45,7 @@ pub const PUBLIC_ADDRESS_CHANGE_DETECTION_COUNT: usize = 3; pub const PUBLIC_ADDRESS_CHECK_CACHE_SIZE: usize = 8; pub const PUBLIC_ADDRESS_CHECK_TASK_INTERVAL_SECS: u32 = 60; pub const PUBLIC_ADDRESS_INCONSISTENCY_TIMEOUT_US: u64 = 300_000_000u64; // 5 minutes +pub const PUBLIC_ADDRESS_INCONSISTENCY_PUNISHMENT_TIMEOUT_US: u64 = 3600_000_000u64; // 60 minutes pub const BOOT_MAGIC: &[u8; 4] = b"BOOT"; pub const BOOTSTRAP_TXT_VERSION: u8 = 0; @@ -1702,6 +1703,9 @@ impl NetworkManager { let network_class = net.get_network_class().unwrap_or(NetworkClass::Invalid); // Determine if our external address has likely changed + let mut bad_public_address_detection_punishment: Option< + Box, + > = None; let needs_public_address_detection = if matches!(network_class, NetworkClass::InboundCapable) { // Get the dial info filter for this connection so we can check if we have any public dialinfo that may have changed @@ -1721,7 +1725,6 @@ impl NetworkManager { // then we zap the network class and re-detect it let inner = &mut *self.inner.lock(); let mut inconsistencies = Vec::new(); - let mut inconsistent = false; // Iteration goes from most recent to least recent node/address pair let pacc = inner .public_address_check_cache @@ -1737,24 +1740,41 @@ impl NetworkManager { if !current_addresses.contains(a) && !pait.contains_key(reporting_ip_block) { // Record the origin of the inconsistency inconsistencies.push(*reporting_ip_block); - - // If we have enough inconsistencies to consider changing our public dial info, - // add them to our denylist (throttling) and go ahead and check for new - // public dialinfo - if inconsistencies.len() >= PUBLIC_ADDRESS_CHANGE_DETECTION_COUNT { - let exp_ts = - intf::get_timestamp() + PUBLIC_ADDRESS_INCONSISTENCY_TIMEOUT_US; - for i in inconsistencies { - pait.insert(i, exp_ts); - } - - inconsistent = true; - break; - } } } + + // If we have enough inconsistencies to consider changing our public dial info, + // add them to our denylist (throttling) and go ahead and check for new + // public dialinfo + let inconsistent = if inconsistencies.len() >= PUBLIC_ADDRESS_CHANGE_DETECTION_COUNT + { + let exp_ts = intf::get_timestamp() + PUBLIC_ADDRESS_INCONSISTENCY_TIMEOUT_US; + for i in &inconsistencies { + pait.insert(*i, exp_ts); + } + + // Run this routine if the inconsistent nodes turn out to be lying + let this = self.clone(); + bad_public_address_detection_punishment = Some(Box::new(move || { + let mut inner = this.inner.lock(); + let pait = inner + .public_address_inconsistencies_table + .entry(key) + .or_insert_with(|| HashMap::new()); + let exp_ts = intf::get_timestamp() + + PUBLIC_ADDRESS_INCONSISTENCY_PUNISHMENT_TIMEOUT_US; + for i in inconsistencies { + pait.insert(i, exp_ts); + } + })); + + true + } else { + false + }; + // // debug code - // if changed { + // if inconsistent { // trace!("public_address_check_cache: {:#?}\ncurrent_addresses: {:#?}\ninconsistencies: {}", inner // .public_address_check_cache, current_addresses, inconsistencies); // } @@ -1799,9 +1819,8 @@ impl NetworkManager { let mut inner = self.inner.lock(); inner.public_address_check_cache.clear(); - // Reset the network class and dial info so we can re-detect it - routing_table.clear_dial_info_details(RoutingDomain::PublicInternet); - net.reset_network_class(); + // Re-detect the public dialinfo + net.set_needs_public_dial_info_check(bad_public_address_detection_punishment); } else { let inner = self.inner.lock(); warn!("Public address may have changed. Restarting the server may be required."); diff --git a/veilid-core/src/network_manager/native/mod.rs b/veilid-core/src/network_manager/native/mod.rs index c8ab9101..d04309ef 100644 --- a/veilid-core/src/network_manager/native/mod.rs +++ b/veilid-core/src/network_manager/native/mod.rs @@ -48,6 +48,10 @@ struct NetworkInner { enable_ipv4: bool, enable_ipv6_global: bool, enable_ipv6_local: bool, + // public dial info check + needs_public_dial_info_check: bool, + doing_public_dial_info_check: bool, + public_dial_info_check_punishment: Option>, // udp bound_first_udp: BTreeMap>, inbound_udp_protocol_handlers: BTreeMap, @@ -89,6 +93,9 @@ impl Network { NetworkInner { network_started: false, network_needs_restart: false, + needs_public_dial_info_check: false, + doing_public_dial_info_check: false, + public_dial_info_check_punishment: None, protocol_config: None, static_public_dialinfo: ProtocolTypeSet::empty(), network_class: None, @@ -770,17 +777,30 @@ impl Network { } ////////////////////////////////////////// + pub fn set_needs_public_dial_info_check( + &self, + punishment: Option>, + ) { + let mut inner = self.inner.lock(); + inner.needs_public_dial_info_check = true; + inner.public_dial_info_check_punishment = punishment; + } + + fn needs_public_dial_info_check(&self) -> bool { + let inner = self.inner.lock(); + inner.needs_public_dial_info_check + } + + pub fn doing_public_dial_info_check(&self) -> bool { + let inner = self.inner.lock(); + inner.doing_public_dial_info_check + } + pub fn get_network_class(&self) -> Option { let inner = self.inner.lock(); inner.network_class } - #[instrument(level = "debug", skip_all)] - pub fn reset_network_class(&self) { - let mut inner = self.inner.lock(); - inner.network_class = None; - } - ////////////////////////////////////////// #[instrument(level = "trace", skip(self), err)] @@ -842,7 +862,8 @@ impl Network { // If we need to figure out our network class, tick the task for it if detect_address_changes { let network_class = self.get_network_class().unwrap_or(NetworkClass::Invalid); - if network_class == NetworkClass::Invalid { + let needs_public_dial_info_check = self.needs_public_dial_info_check(); + if network_class == NetworkClass::Invalid || needs_public_dial_info_check { let routing_table = self.routing_table(); let rth = routing_table.get_routing_table_health(); 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 afc2b3dc..0d715080 100644 --- a/veilid-core/src/network_manager/native/network_class_discovery.rs +++ b/veilid-core/src/network_manager/native/network_class_discovery.rs @@ -605,16 +605,13 @@ impl Network { } #[instrument(level = "trace", skip(self), err)] - pub async fn update_network_class_task_routine( - self, + pub async fn do_public_dial_info_check( + &self, stop_token: StopToken, _l: u64, _t: u64, ) -> EyreResult<()> { - // Ensure we aren't trying to update this without clearing it first - let old_network_class = self.inner.lock().network_class; - assert_eq!(old_network_class, None); - + // Figure out if we can optimize TCP/WS checking since they are often on the same port let protocol_config = self.inner.lock().protocol_config.unwrap_or_default(); let tcp_same_port = if protocol_config.inbound.contains(ProtocolType::TCP) && protocol_config.inbound.contains(ProtocolType::WS) @@ -823,6 +820,26 @@ impl Network { network_manager.send_node_info_updates(true).await; } + if !changed {} + Ok(()) } + #[instrument(level = "trace", skip(self), err)] + pub async fn update_network_class_task_routine( + self, + stop_token: StopToken, + l: u64, + t: u64, + ) -> EyreResult<()> { + // Note that we are doing the public dial info check + self.inner.lock().doing_public_dial_info_check = true; + + // Do the public dial info check + let out = self.do_public_dial_info_check(stop_token, l, t); + + // Done with public dial info check + self.inner.lock().doing_public_dial_info_check = false; + + out + } } diff --git a/veilid-core/src/network_manager/wasm/mod.rs b/veilid-core/src/network_manager/wasm/mod.rs index 89c0a0b2..cc77391d 100644 --- a/veilid-core/src/network_manager/wasm/mod.rs +++ b/veilid-core/src/network_manager/wasm/mod.rs @@ -291,6 +291,11 @@ impl Network { } ////////////////////////////////////////// + + pub fn set_needs_public_dial_info_check(&self) { + // + } + pub fn get_network_class(&self) -> Option { // xxx eventually detect tor browser? return if self.inner.lock().network_started { @@ -300,11 +305,6 @@ impl Network { }; } - pub fn reset_network_class(&self) { - //let mut inner = self.inner.lock(); - //inner.network_class = None; - } - pub fn get_protocol_config(&self) -> Option { self.inner.lock().protocol_config.clone() }