diff --git a/Cargo.lock b/Cargo.lock index 0fcaf2e1..db727ae8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -499,14 +499,13 @@ checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" [[package]] name = "attohttpc" -version = "0.16.3" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb8867f378f33f78a811a8eb9bf108ad99430d7aad43315dd9319c827ef6247" +checksum = "8d9a9bf8b79a749ee0b911b91b671cc2b6c670bdbc7e3dfd537576ddc94bb2a2" dependencies = [ "http", "log", "url", - "wildmatch", ] [[package]] @@ -5290,8 +5289,8 @@ dependencies = [ "flume", "futures-util", "getrandom", + "glob", "hex", - "ifstructs", "jni", "jni-sys", "js-sys", @@ -5306,8 +5305,6 @@ dependencies = [ "lz4_flex", "ndk", "ndk-glue", - "netlink-packet-route", - "netlink-sys", "nix 0.27.1", "num-traits", "once_cell", @@ -5316,7 +5313,6 @@ dependencies = [ "parking_lot 0.12.1", "paste", "range-set-blaze", - "rtnetlink", "rustls", "rustls-pemfile", "schemars", @@ -5380,6 +5376,7 @@ dependencies = [ "opentelemetry", "opentelemetry-otlp", "opentelemetry-semantic-conventions", + "paranoid-android", "parking_lot 0.12.1", "serde", "serde_json", @@ -5404,9 +5401,9 @@ dependencies = [ [[package]] name = "veilid-igd" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28428a3f826ed334f995522e554d7c8c1a5a0e0a0248fc795a31022ddf436c9d" +checksum = "ce2b3c073da0025538ff4cf5bea61a7a7a046c1bf060e2d0981c71800747551d" dependencies = [ "attohttpc", "log", @@ -5482,6 +5479,7 @@ dependencies = [ "fn_name", "futures-util", "getrandom", + "ifstructs", "jni", "jni-sys", "js-sys", @@ -5490,6 +5488,8 @@ dependencies = [ "log", "ndk", "ndk-glue", + "netlink-packet-route", + "netlink-sys", "nix 0.27.1", "once_cell", "oslog", @@ -5498,6 +5498,7 @@ dependencies = [ "rand", "rand_core", "range-set-blaze", + "rtnetlink", "send_wrapper 0.6.0", "serial_test", "simplelog", @@ -5515,6 +5516,7 @@ dependencies = [ "wasm-bindgen-test", "wasm-logger", "wee_alloc", + "winapi", ] [[package]] @@ -5779,12 +5781,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" -[[package]] -name = "wildmatch" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f44b95f62d34113cf558c93511ac93027e03e9c29a60dd0fd70e6e025c7270a" - [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 90d036a5..b9b4a26c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ cursive_core = { git = "https://gitlab.com/veilid/cursive.git" } # keyvaluedb-memorydb = { path = "../keyvaluedb/keyvaluedb-memorydb" } # keyvaluedb-sqlite = { path = "../keyvaluedb/keyvaluedb-sqlite" } # keyvaluedb-web = { path = "../keyvaluedb/keyvaluedb-web" } +# igd = { package = "veilid-igd", path = "../rust-igd" } [profile.release] opt-level = "s" diff --git a/veilid-core/Cargo.toml b/veilid-core/Cargo.toml index 783d4ead..aaf053b9 100644 --- a/veilid-core/Cargo.toml +++ b/veilid-core/Cargo.toml @@ -25,7 +25,6 @@ rt-async-std = [ "async-std-resolver", "trust-dns-resolver", "async_executors/async_std", - "rtnetlink/smol_socket", "veilid-tools/rt-async-std", ] rt-tokio = [ @@ -36,7 +35,6 @@ rt-tokio = [ "async_executors/tokio_tp", "async_executors/tokio_io", "async_executors/tokio_timer", - "rtnetlink/tokio_socket", "veilid-tools/rt-tokio", ] @@ -170,7 +168,7 @@ keyvaluedb-sqlite = "0.1.1" # Network async-tungstenite = { version = "0.23.0", features = ["async-tls"] } -igd = { package = "veilid-igd", version = "0.1.0" } +igd = { package = "veilid-igd", version = "0.1.1" } async-tls = "0.12.0" webpki = "0.22.1" webpki-roots = "0.25.2" @@ -238,16 +236,6 @@ ndk = { version = "0.7.0" } ndk-glue = { version = "0.7.0", features = ["logger"] } paranoid-android = { version = "0.2.1", optional = true } -# Dependenices for all Unix (Linux, Android, MacOS, iOS) -[target.'cfg(unix)'.dependencies] -ifstructs = "0.1.1" - -# Dependencies for Linux or Android -[target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies] -rtnetlink = { version = "=0.13.1", default-features = false } -netlink-sys = { version = "=0.8.5" } -netlink-packet-route = { version = "=0.17.1" } - # Dependencies for Windows [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3.9", features = ["iptypes", "iphlpapi"] } @@ -279,6 +267,7 @@ wasm-logger = "0.2.0" [build-dependencies] capnpc = "0.18.0" +glob = "0.3.1" [package.metadata.wasm-pack.profile.release] wasm-opt = ["-O", "--enable-mutable-globals"] diff --git a/veilid-core/build.rs b/veilid-core/build.rs index 745f3e52..5f38c28e 100644 --- a/veilid-core/build.rs +++ b/veilid-core/build.rs @@ -1,4 +1,8 @@ -use std::process::{Command, Stdio}; +use glob::glob; +use std::{ + env, + process::{Command, Stdio}, +}; const CAPNP_VERSION: &str = "1.0.1"; // Keep in sync with scripts/install_capnp.sh const PROTOC_VERSION: &str = "24.3"; // Keep in sync with scripts/install_protoc.sh @@ -111,4 +115,25 @@ fn main() { .run() .expect("compiling schema"); } + + // Fix for missing __extenddftf2 on Android x86_64 Emulator + let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); + // if target_os == "android" || target_os == "linux" { + // println!("cargo:rustc-link-lib=stdc++"); + // } else { + // println!("cargo:rustc-link-lib=c++"); + // } + let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); + if target_arch == "x86_64" && target_os == "android" { + let missing_library = "clang_rt.builtins-x86_64-android"; + let android_ndk_home = env::var("ANDROID_NDK_HOME").expect("ANDROID_NDK_HOME not set"); + let lib_path = glob(&format!("{android_ndk_home}/**/lib{missing_library}.a")) + .expect("failed to glob") + .next() + .expect("Need libclang_rt.builtins-x86_64-android.a") + .unwrap(); + let lib_dir = lib_path.parent().unwrap(); + println!("cargo:rustc-link-search={}", lib_dir.display()); + println!("cargo:rustc-link-lib=static={missing_library}"); + } } diff --git a/veilid-core/src/intf/native/mod.rs b/veilid-core/src/intf/native/mod.rs index d8cc6811..35c6c602 100644 --- a/veilid-core/src/intf/native/mod.rs +++ b/veilid-core/src/intf/native/mod.rs @@ -12,6 +12,5 @@ pub use system::*; #[cfg(target_os = "android")] pub mod android; -pub mod network_interfaces; use super::*; diff --git a/veilid-core/src/intf/native/protected_store.rs b/veilid-core/src/intf/native/protected_store.rs index 7faa0f49..c952d9c5 100644 --- a/veilid-core/src/intf/native/protected_store.rs +++ b/veilid-core/src/intf/native/protected_store.rs @@ -48,11 +48,19 @@ impl ProtectedStore { // Attempt to open the secure keyring cfg_if! { if #[cfg(target_os = "android")] { - inner.keyring_manager = KeyringManager::new_secure(&c.program_name, crate::intf::android::get_android_globals()).ok(); + let maybe_km = KeyringManager::new_secure(&c.program_name, crate::intf::android::get_android_globals()); } else { - inner.keyring_manager = KeyringManager::new_secure(&c.program_name).ok(); + let maybe_km = KeyringManager::new_secure(&c.program_name); } } + + inner.keyring_manager = match maybe_km { + Ok(v) => Some(v), + Err(e) => { + log_pstore!(error "Failed to create secure keyring manager: {}", e); + None + } + }; } if (c.protected_store.always_use_insecure_storage || c.protected_store.allow_insecure_fallback) @@ -78,6 +86,7 @@ impl ProtectedStore { ); } if inner.keyring_manager.is_none() { + log_pstore!(error "QWERQWER"); bail!("Could not initialize the protected store."); } c.protected_store.delete diff --git a/veilid-core/src/network_manager/native/discovery_context.rs b/veilid-core/src/network_manager/native/discovery_context.rs index 2837ed4c..e92ef48e 100644 --- a/veilid-core/src/network_manager/native/discovery_context.rs +++ b/veilid-core/src/network_manager/native/discovery_context.rs @@ -39,8 +39,11 @@ struct DiscoveryContextInner { struct DiscoveryContextUnlockedInner { routing_table: RoutingTable, net: Network, + clear_network_callback: ClearNetworkCallback, + // per-protocol intf_addrs: Vec, + existing_external_address: Option, protocol_type: ProtocolType, address_type: AddressType, } @@ -51,21 +54,45 @@ pub struct DiscoveryContext { inner: Arc>, } +pub type ClearNetworkCallback = Arc SendPinBoxFuture<()> + Send + Sync>; + impl DiscoveryContext { pub fn new( routing_table: RoutingTable, net: Network, protocol_type: ProtocolType, address_type: AddressType, + clear_network_callback: ClearNetworkCallback, ) -> Self { let intf_addrs = Self::get_local_addresses(routing_table.clone(), protocol_type, address_type); + // Get the existing external address to check to see if it has changed + let existing_dial_info = routing_table.all_filtered_dial_info_details( + RoutingDomain::PublicInternet.into(), + &DialInfoFilter::default() + .with_address_type(address_type) + .with_protocol_type(protocol_type), + ); + let existing_external_address = if existing_dial_info.len() == 1 { + Some( + existing_dial_info + .first() + .unwrap() + .dial_info + .socket_address(), + ) + } else { + None + }; + Self { unlocked_inner: Arc::new(DiscoveryContextUnlockedInner { routing_table, net, + clear_network_callback, intf_addrs, + existing_external_address, protocol_type, address_type, }), @@ -631,6 +658,30 @@ impl DiscoveryContext { return; } + // Did external address change from the last time we made dialinfo? + // Disregard port for this because we only need to know if the ip address has changed + // If the port has changed it will change only for this protocol and will be overwritten individually by each protocol discover() + let some_clear_network_callback = { + let inner = self.inner.lock(); + let ext_1 = inner.external_1.as_ref().unwrap().address.address(); + let ext_2 = inner.external_2.as_ref().unwrap().address.address(); + if (ext_1 != ext_2) + || Some(ext_1) + != self + .unlocked_inner + .existing_external_address + .map(|ea| ea.address()) + { + // External address was not found, or has changed, go ahead and clear the network so we can do better + Some(self.unlocked_inner.clear_network_callback.clone()) + } else { + None + } + }; + if let Some(clear_network_callback) = some_clear_network_callback { + clear_network_callback().await; + } + // UPNP Automatic Mapping /////////// if enable_upnp { diff --git a/veilid-core/src/network_manager/native/igd_manager.rs b/veilid-core/src/network_manager/native/igd_manager.rs index 32e3e3f2..302dba33 100644 --- a/veilid-core/src/network_manager/native/igd_manager.rs +++ b/veilid-core/src/network_manager/native/igd_manager.rs @@ -26,7 +26,7 @@ struct PortMapValue { struct IGDManagerInner { local_ip_addrs: BTreeMap, - gateways: BTreeMap>, + gateways: BTreeMap>, port_maps: BTreeMap, } @@ -121,17 +121,21 @@ impl IGDManager { fn find_gateway( inner: &mut IGDManagerInner, - address_type: AddressType, + local_ip: IpAddr, ) -> Option> { - if let Some(gw) = inner.gateways.get(&address_type) { + + if let Some(gw) = inner.gateways.get(&local_ip) { return Some(gw.clone()); } - let gateway = match address_type { - AddressType::IPV4 => { - match igd::search_gateway(SearchOptions::new_v4( + let gateway = match local_ip { + IpAddr::V4(v4) => { + let mut opts = SearchOptions::new_v4( UPNP_GATEWAY_DETECT_TIMEOUT_MS as u64, - )) { + ); + opts.bind_addr = SocketAddr::V4(SocketAddrV4::new(v4, 0)); + + match igd::search_gateway(opts) { Ok(v) => v, Err(e) => { log_net!(debug "couldn't find ipv4 igd: {}", e); @@ -139,11 +143,14 @@ impl IGDManager { } } } - AddressType::IPV6 => { - match igd::search_gateway(SearchOptions::new_v6( + IpAddr::V6(v6) => { + let mut opts = SearchOptions::new_v6( Ipv6SearchScope::LinkLocal, UPNP_GATEWAY_DETECT_TIMEOUT_MS as u64, - )) { + ); + opts.bind_addr = SocketAddr::V6(SocketAddrV6::new(v6, 0, 0, 0)); + + match igd::search_gateway(opts) { Ok(v) => v, Err(e) => { log_net!(debug "couldn't find ipv6 igd: {}", e); @@ -151,17 +158,18 @@ impl IGDManager { } } } + }; let gw = Arc::new(gateway); - inner.gateways.insert(address_type, gw.clone()); + inner.gateways.insert(local_ip, gw.clone()); Some(gw) } fn get_gateway( inner: &mut IGDManagerInner, - address_type: AddressType, + local_ip: IpAddr, ) -> Option> { - if let Some(gw) = inner.gateways.get(&address_type) { + if let Some(gw) = inner.gateways.get(&local_ip) { return Some(gw.clone()); } None @@ -191,8 +199,12 @@ impl IGDManager { let pmk = found?; let _pmv = inner.port_maps.remove(&pmk).expect("key found but remove failed"); + + // Get local ip address + let local_ip = Self::find_local_ip(&mut inner, at)?; + // Find gateway - let gw = Self::find_gateway(&mut inner, at)?; + let gw = Self::find_gateway(&mut inner, local_ip)?; // Unmap port match gw.remove_port(convert_llpt(llpt), mapped_port) { @@ -233,7 +245,7 @@ impl IGDManager { let local_ip = Self::find_local_ip(&mut inner, at)?; // Find gateway - let gw = Self::find_gateway(&mut inner, at)?; + let gw = Self::find_gateway(&mut inner, local_ip)?; // Get external address let ext_ip = match gw.get_external_ip() { @@ -325,14 +337,6 @@ impl IGDManager { // Process full renewals for (k, v) in full_renews { - - // Get gateway for address type - let gw = match Self::get_gateway(&mut inner, k.at) { - Some(gw) => gw, - None => { - return Err(eyre!("gateway missing for address type")); - } - }; // Get local ip for address type let local_ip = match Self::get_local_ip(&mut inner, k.at) { @@ -341,6 +345,14 @@ impl IGDManager { return Err(eyre!("local ip missing for address type")); } }; + + // Get gateway for interface + let gw = match Self::get_gateway(&mut inner, local_ip) { + Some(gw) => gw, + None => { + return Err(eyre!("gateway missing for interface")); + } + }; // Delete the mapping if it exists, ignore any errors here let _ = gw.remove_port(convert_llpt(k.llpt), v.mapped_port); @@ -370,14 +382,6 @@ impl IGDManager { // Process normal renewals for (k, mut v) in renews { - // Get gateway for address type - let gw = match Self::get_gateway(&mut inner, k.at) { - Some(gw) => gw, - None => { - return Err(eyre!("gateway missing for address type")); - } - }; - // Get local ip for address type let local_ip = match Self::get_local_ip(&mut inner, k.at) { Some(ip) => ip, @@ -386,6 +390,14 @@ impl IGDManager { } }; + // Get gateway for interface + let gw = match Self::get_gateway(&mut inner, local_ip) { + Some(gw) => gw, + None => { + return Err(eyre!("gateway missing for address type")); + } + }; + let desc = this.get_description(k.llpt, k.local_port); match gw.add_port(convert_llpt(k.llpt), v.mapped_port, SocketAddr::new(local_ip, k.local_port), (UPNP_MAPPING_LIFETIME_MS + 999) / 1000, &desc) { Ok(()) => { diff --git a/veilid-core/src/network_manager/native/mod.rs b/veilid-core/src/network_manager/native/mod.rs index ead28274..3eab81f8 100644 --- a/veilid-core/src/network_manager/native/mod.rs +++ b/veilid-core/src/network_manager/native/mod.rs @@ -10,7 +10,6 @@ use super::*; use crate::routing_table::*; use connection_manager::*; use discovery_context::*; -use network_interfaces::*; use network_tcp::*; use protocol::tcp::RawTcpProtocolHandler; use protocol::udp::RawUdpProtocolHandler; @@ -99,6 +98,8 @@ struct NetworkInner { enable_ipv6_local: bool, /// set if we need to calculate our public dial info again needs_public_dial_info_check: bool, + /// set if we have yet to clear the network during public dial info checking + network_already_cleared: bool, /// the punishment closure to enax public_dial_info_check_punishment: Option>, /// udp socket record for bound-first sockets, which are used to guarantee a port is available before @@ -148,6 +149,7 @@ impl Network { network_started: false, network_needs_restart: false, needs_public_dial_info_check: false, + network_already_cleared: false, public_dial_info_check_punishment: None, protocol_config: Default::default(), static_public_dialinfo: ProtocolTypeSet::empty(), @@ -313,7 +315,7 @@ impl Network { if !from.ip().is_unspecified() { vec![*from] } else { - let addrs = self.get_usable_interface_addresses(); + let addrs = self.get_stable_interface_addresses(); addrs .iter() .filter_map(|a| { @@ -355,13 +357,13 @@ impl Network { }) } - pub fn is_usable_interface_address(&self, addr: IpAddr) -> bool { - let usable_addrs = self.get_usable_interface_addresses(); - usable_addrs.contains(&addr) + pub fn is_stable_interface_address(&self, addr: IpAddr) -> bool { + let stable_addrs = self.get_stable_interface_addresses(); + stable_addrs.contains(&addr) } - pub fn get_usable_interface_addresses(&self) -> Vec { - let addrs = self.unlocked_inner.interfaces.best_addresses(); + pub fn get_stable_interface_addresses(&self) -> Vec { + let addrs = self.unlocked_inner.interfaces.stable_addresses(); let addrs: Vec = addrs .into_iter() .filter(|addr| { @@ -372,15 +374,20 @@ impl Network { addrs } - // See if our interface addresses have changed, if so we need to punt the network - // and redo all our addresses. This is overkill, but anything more accurate - // would require inspection of routing tables that we dont want to bother with + // See if our interface addresses have changed, if so redo public dial info if necessary async fn check_interface_addresses(&self) -> EyreResult { - if !self.unlocked_inner.interfaces.refresh().await? { + if !self + .unlocked_inner + .interfaces + .refresh() + .await + .wrap_err("failed to check network interfaces")? + { return Ok(false); } - self.inner.lock().network_needs_restart = true; + self.inner.lock().needs_public_dial_info_check = true; + Ok(true) } @@ -697,7 +704,7 @@ impl Network { self.unlocked_inner .interfaces .with_interfaces(|interfaces| { - trace!("interfaces: {:#?}", interfaces); + debug!("interfaces: {:#?}", interfaces); for intf in interfaces.values() { // Skip networks that we should never encounter @@ -721,7 +728,7 @@ impl Network { { let mut inner = self.inner.lock(); inner.enable_ipv4 = false; - for addr in self.get_usable_interface_addresses() { + for addr in self.get_stable_interface_addresses() { if addr.is_ipv4() { log_net!(debug "enable address {:?} as ipv4", addr); inner.enable_ipv4 = true; @@ -975,9 +982,8 @@ impl Network { _l: u64, _t: u64, ) -> EyreResult<()> { - if self.check_interface_addresses().await? { - info!("interface addresses changed, restarting network"); - } + self.check_interface_addresses().await?; + Ok(()) } 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 c0336fdd..e0bdf9ec 100644 --- a/veilid-core/src/network_manager/native/network_class_discovery.rs +++ b/veilid-core/src/network_manager/native/network_class_discovery.rs @@ -22,7 +22,6 @@ impl Network { editor.clear_dial_info_details(None, None); editor.set_network_class(Some(NetworkClass::OutboundOnly)); - editor.clear_relay_node(); editor.commit(true).await; } } @@ -51,13 +50,11 @@ impl Network { let mut add = false; if let Some(edi) = existing_dial_info.get(&(pt, at)) { - if did.class < edi.class { - // Better dial info class was found, clear existing dialinfo for this pt/at pair + if did.class <= edi.class { + // Better or same dial info class was found, clear existing dialinfo for this pt/at pair + // Only keep one dial info per protocol/address type combination clear = true; add = true; - } else if did.class == edi.class { - // Same dial info class, just add dial info - add = true; } // Otherwise, don't upgrade, don't add, this is worse than what we have already } else { @@ -103,7 +100,7 @@ impl Network { ) -> EyreResult<()> { // 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 mut inner = self.inner.lock(); let protocol_config = inner.protocol_config.clone(); let tcp_same_port = if protocol_config.inbound.contains(ProtocolType::TCP) && protocol_config.inbound.contains(ProtocolType::WS) @@ -112,6 +109,10 @@ impl Network { } else { false }; + // Allow network to be cleared if external addresses change + inner.network_already_cleared = false; + + // (protocol_config, tcp_same_port) }; @@ -125,8 +126,7 @@ impl Network { .into_iter() .collect(); - // Clear public dialinfo and network class in prep for discovery - + // Set most permissive network config let mut editor = self .routing_table() .edit_routing_domain(RoutingDomain::PublicInternet); @@ -136,11 +136,30 @@ impl Network { 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(); editor.commit(true).await; + // Create a callback to clear the network if we need to 'start over' + let this = self.clone(); + let clear_network_callback: ClearNetworkCallback = Arc::new(move || { + let this = this.clone(); + Box::pin(async move { + // Ensure we only do this once per network class discovery + { + let mut inner = this.inner.lock(); + if inner.network_already_cleared { + return; + } + inner.network_already_cleared = true; + } + let mut editor = this + .routing_table() + .edit_routing_domain(RoutingDomain::PublicInternet); + editor.clear_dial_info_details(None, None); + editor.set_network_class(None); + editor.commit(true).await; + }) + }); + // Process all protocol and address combinations let mut unord = FuturesUnordered::new(); // Do UDPv4+v6 at the same time as everything else @@ -152,6 +171,7 @@ impl Network { self.clone(), ProtocolType::UDP, AddressType::IPV4, + clear_network_callback.clone(), ); udpv4_context .discover(&mut unord) @@ -166,6 +186,7 @@ impl Network { self.clone(), ProtocolType::UDP, AddressType::IPV6, + clear_network_callback.clone(), ); udpv6_context .discover(&mut unord) @@ -182,6 +203,7 @@ impl Network { self.clone(), ProtocolType::TCP, AddressType::IPV4, + clear_network_callback.clone(), ); tcpv4_context .discover(&mut unord) @@ -195,6 +217,7 @@ impl Network { self.clone(), ProtocolType::WS, AddressType::IPV4, + clear_network_callback.clone(), ); wsv4_context .discover(&mut unord) @@ -211,6 +234,7 @@ impl Network { self.clone(), ProtocolType::TCP, AddressType::IPV6, + clear_network_callback.clone(), ); tcpv6_context .discover(&mut unord) @@ -225,6 +249,7 @@ impl Network { self.clone(), ProtocolType::WS, AddressType::IPV6, + clear_network_callback.clone(), ); wsv6_context .discover(&mut unord) diff --git a/veilid-core/src/network_manager/native/start_protocols.rs b/veilid-core/src/network_manager/native/start_protocols.rs index c5511563..85575fd5 100644 --- a/veilid-core/src/network_manager/native/start_protocols.rs +++ b/veilid-core/src/network_manager/native/start_protocols.rs @@ -342,7 +342,7 @@ impl Network { // See if this public address is also a local interface address we haven't registered yet let is_interface_address = (|| { - for ip_addr in self.get_usable_interface_addresses() { + for ip_addr in self.get_stable_interface_addresses() { if pdi_addr.ip() == ip_addr { return true; } @@ -438,7 +438,7 @@ impl Network { // See if this public address is also a local interface address if !registered_addresses.contains(&gsa.ip()) - && self.is_usable_interface_address(gsa.ip()) + && self.is_stable_interface_address(gsa.ip()) { editor_local_network.register_dial_info(pdi, DialInfoClass::Direct)?; } @@ -552,7 +552,7 @@ impl Network { // See if this public address is also a local interface address if !registered_addresses.contains(&gsa.ip()) - && self.is_usable_interface_address(gsa.ip()) + && self.is_stable_interface_address(gsa.ip()) { editor_local_network.register_dial_info(pdi, DialInfoClass::Direct)?; } @@ -653,7 +653,7 @@ impl Network { } // See if this public address is also a local interface address - if self.is_usable_interface_address(pdi_addr.ip()) { + if self.is_stable_interface_address(pdi_addr.ip()) { editor_local_network.register_dial_info(pdi, DialInfoClass::Direct)?; } } diff --git a/veilid-core/src/network_manager/wasm/mod.rs b/veilid-core/src/network_manager/wasm/mod.rs index 3de5c95a..78439cb2 100644 --- a/veilid-core/src/network_manager/wasm/mod.rs +++ b/veilid-core/src/network_manager/wasm/mod.rs @@ -425,11 +425,11 @@ impl Network { trace!("network stopped"); } - pub fn is_usable_interface_address(&self, _addr: IpAddr) -> bool { + pub fn is_stable_interface_address(&self, _addr: IpAddr) -> bool { false } - pub fn get_usable_interface_addresses(&self) -> Vec { + pub fn get_stable_interface_addresses(&self) -> Vec { Vec::new() } diff --git a/veilid-core/src/routing_table/tasks/bootstrap.rs b/veilid-core/src/routing_table/tasks/bootstrap.rs index e69a0524..329d40a0 100644 --- a/veilid-core/src/routing_table/tasks/bootstrap.rs +++ b/veilid-core/src/routing_table/tasks/bootstrap.rs @@ -384,6 +384,22 @@ impl RoutingTable { let routing_table = self.clone(); unord.push( async move { + // Get what contact method would be used for contacting the bootstrap + let bsdi = match routing_table + .network_manager() + .get_node_contact_method(nr.clone()) + { + Ok(NodeContactMethod::Direct(v)) => v, + Ok(v) => { + log_rtab!(warn "invalid contact method for bootstrap: {:?}", v); + return; + } + Err(e) => { + log_rtab!(warn "unable to bootstrap: {}", e); + return; + } + }; + // Need VALID signed peer info, so ask bootstrap to find_node of itself // which will ensure it has the bootstrap's signed peer info as part of the response let _ = routing_table.find_target(crypto_kind, nr.clone()).await; @@ -391,7 +407,10 @@ impl RoutingTable { // Ensure we got the signed peer info if !nr.signed_node_info_has_valid_signature(RoutingDomain::PublicInternet) { log_rtab!(warn "bootstrap server is not responding"); - log_rtab!(debug "bootstrap server is not responding: {}", nr); + log_rtab!(debug "bootstrap server is not responding for dialinfo: {}", bsdi); + + // Try a different dialinfo next time + routing_table.network_manager().address_filter().set_dial_info_failed(bsdi); } else { // otherwise this bootstrap is valid, lets ask it to find ourselves now routing_table.reverse_find_node(crypto_kind, nr, true).await diff --git a/veilid-core/src/tests/common/mod.rs b/veilid-core/src/tests/common/mod.rs index 13d151cd..2c507282 100644 --- a/veilid-core/src/tests/common/mod.rs +++ b/veilid-core/src/tests/common/mod.rs @@ -1,4 +1,3 @@ -pub mod test_host_interface; pub mod test_protected_store; pub mod test_veilid_config; pub mod test_veilid_core; diff --git a/veilid-core/src/tests/native/mod.rs b/veilid-core/src/tests/native/mod.rs index bcb7d701..558904a5 100644 --- a/veilid-core/src/tests/native/mod.rs +++ b/veilid-core/src/tests/native/mod.rs @@ -13,8 +13,6 @@ use crate::*; #[allow(dead_code)] pub async fn run_all_tests() { // iOS and Android tests also run these. - info!("TEST: test_host_interface"); - test_host_interface::test_all().await; info!("TEST: test_types"); test_types::test_all().await; info!("TEST: test_veilid_core"); @@ -114,8 +112,6 @@ cfg_if! { }); } - run_test!(test_host_interface); - run_test!(test_types); run_test!(test_veilid_core); diff --git a/veilid-flutter/lib/veilid.dart b/veilid-flutter/lib/veilid.dart index 84709a11..e17ec7e9 100644 --- a/veilid-flutter/lib/veilid.dart +++ b/veilid-flutter/lib/veilid.dart @@ -2,8 +2,8 @@ import 'dart:async'; import 'dart:typed_data'; import 'package:equatable/equatable.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:fixnum/fixnum.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; ////////////////////////////////////////////////////////// diff --git a/veilid-flutter/rust/Cargo.toml b/veilid-flutter/rust/Cargo.toml index 11fb0f05..4b45d99e 100644 --- a/veilid-flutter/rust/Cargo.toml +++ b/veilid-flutter/rust/Cargo.toml @@ -63,3 +63,4 @@ hostname = "^0" # Dependencies for Android builds only [target.'cfg(target_os = "android")'.dependencies] jni = "^0" +paranoid-android = "0.2.1" diff --git a/veilid-flutter/rust/src/dart_ffi.rs b/veilid-flutter/rust/src/dart_ffi.rs index 812f8ab9..00388f67 100644 --- a/veilid-flutter/rust/src/dart_ffi.rs +++ b/veilid-flutter/rust/src/dart_ffi.rs @@ -166,14 +166,26 @@ pub extern "C" fn initialize_veilid_core(platform_config: FfiStr) { // Terminal logger if platform_config.logging.terminal.enabled { - let filter = - veilid_core::VeilidLayerFilter::new(platform_config.logging.terminal.level, None); - let layer = tracing_subscriber::fmt::Layer::new() - .compact() - .with_writer(std::io::stdout) - .with_filter(filter.clone()); - filters.insert("terminal", filter); - layers.push(layer.boxed()); + cfg_if! { + if #[cfg(target_os = "android")] { + let filter = + veilid_core::VeilidLayerFilter::new(platform_config.logging.terminal.level, None); + let layer = paranoid_android::layer("veilid-flutter") + .with_ansi(false) + .with_filter(filter.clone()); + filters.insert("terminal", filter); + layers.push(layer.boxed()); + } else { + let filter = + veilid_core::VeilidLayerFilter::new(platform_config.logging.terminal.level, None); + let layer = tracing_subscriber::fmt::Layer::new() + .compact() + .with_writer(std::io::stdout) + .with_filter(filter.clone()); + filters.insert("terminal", filter); + layers.push(layer.boxed()); + } + } }; // OpenTelemetry logger @@ -237,6 +249,7 @@ pub extern "C" fn initialize_veilid_core(platform_config: FfiStr) { .try_init() .map_err(|e| format!("failed to initialize logging: {}", e)) .expect("failed to initalize ffi platform"); + } #[no_mangle] diff --git a/veilid-tools/Cargo.toml b/veilid-tools/Cargo.toml index ea8fd23e..5f32a588 100644 --- a/veilid-tools/Cargo.toml +++ b/veilid-tools/Cargo.toml @@ -14,10 +14,15 @@ crate-type = ["cdylib", "staticlib", "rlib"] [features] default = ["rt-tokio"] -rt-async-std = ["async-std", "async_executors/async_std"] +rt-async-std = [ + "async-std", + "async_executors/async_std", + "rtnetlink/smol_socket", +] rt-tokio = [ "tokio", "tokio-util", + "rtnetlink/tokio_socket", "async_executors/tokio_tp", "async_executors/tokio_io", "async_executors/tokio_timer", @@ -25,7 +30,7 @@ rt-tokio = [ rt-wasm-bindgen = ["async_executors/bindgen", "async_executors/timer"] veilid_tools_android_tests = ["dep:paranoid-android"] -veilid_tools_ios_tests = ["dep:oslog", "dep:tracing-oslog"] +veilid_tools_ios_tests = ["dep:tracing", "dep:oslog", "dep:tracing-oslog"] tracing = ["dep:tracing", "dep:tracing-subscriber"] network-result-extra = [] network-result-info = [] @@ -69,7 +74,7 @@ futures-util = { version = "0.3.28", default-features = false, features = [ chrono = "0.4.31" libc = "0.2.148" -nix = { version = "0.27.1", features = [ "user" ] } +nix = { version = "0.27.1", features = ["user"] } # Dependencies for WASM builds only [target.'cfg(target_arch = "wasm32")'.dependencies] @@ -82,6 +87,12 @@ getrandom = { version = "0.2", features = ["js"] } async-lock = "2.8.0" send_wrapper = { version = "0.6.0", features = ["futures"] } +# Dependencies for Linux or Android +[target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies] +rtnetlink = { version = "=0.13.1", default-features = false } +netlink-sys = { version = "=0.8.5" } +netlink-packet-route = { version = "=0.17.1" } + # Dependencies for Android [target.'cfg(target_os = "android")'.dependencies] jni = "0.21.1" @@ -93,15 +104,18 @@ paranoid-android = { version = "0.2.1", optional = true } android_logger = "0.13.3" # Dependencies for Windows -# [target.'cfg(target_os = "windows")'.dependencies] -# windows = { version = "^0", features = [ "Win32_NetworkManagement_Dns", "Win32_Foundation", "alloc" ]} -# windows-permissions = "^0" +[target.'cfg(target_os = "windows")'.dependencies] +winapi = { version = "0.3.9", features = ["iptypes", "iphlpapi"] } # Dependencies for iOS [target.'cfg(target_os = "ios")'.dependencies] oslog = { version = "0.2.0", optional = true } tracing-oslog = { version = "0.1.2", optional = true } +# Dependenices for all Unix (Linux, Android, MacOS, iOS) +[target.'cfg(unix)'.dependencies] +ifstructs = "0.1.1" + ### DEV DEPENDENCIES [dev-dependencies] diff --git a/veilid-tools/src/lib.rs b/veilid-tools/src/lib.rs index e9cb44b1..8573aedd 100644 --- a/veilid-tools/src/lib.rs +++ b/veilid-tools/src/lib.rs @@ -40,6 +40,7 @@ pub mod log_thru; pub mod must_join_handle; pub mod must_join_single_future; pub mod mutable_future; +pub mod network_interfaces; pub mod network_result; pub mod random; pub mod single_shot_eventual; @@ -182,6 +183,8 @@ pub use must_join_single_future::*; #[doc(inline)] pub use mutable_future::*; #[doc(inline)] +pub use network_interfaces::*; +#[doc(inline)] pub use network_result::*; #[doc(inline)] pub use random::*; diff --git a/veilid-tools/src/log_thru.rs b/veilid-tools/src/log_thru.rs index 89ef8233..9988e7d5 100644 --- a/veilid-tools/src/log_thru.rs +++ b/veilid-tools/src/log_thru.rs @@ -177,6 +177,14 @@ macro_rules! log_pstore { (warn $fmt:literal, $($arg:expr),+) => { warn!(target:"pstore", $fmt, $($arg),+); }; + (debug $text:expr) => { debug!( + target: "pstore", + "{}", + $text, + )}; + (debug $fmt:literal, $($arg:expr),+) => { + debug!(target:"pstore", $fmt, $($arg),+); + }; ($text:expr) => {trace!( target: "pstore", "{}", diff --git a/veilid-core/src/intf/native/network_interfaces/apple.rs b/veilid-tools/src/network_interfaces/apple.rs similarity index 85% rename from veilid-core/src/intf/native/network_interfaces/apple.rs rename to veilid-tools/src/network_interfaces/apple.rs index 88422f60..1a7d5a75 100644 --- a/veilid-core/src/intf/native/network_interfaces/apple.rs +++ b/veilid-tools/src/network_interfaces/apple.rs @@ -1,11 +1,12 @@ +#![cfg(any(target_os = "macos", target_os = "ios"))] #![allow(non_camel_case_types)] use super::*; use libc::{ close, freeifaddrs, getifaddrs, if_nametoindex, ifaddrs, ioctl, pid_t, sockaddr, sockaddr_in6, - socket, sysctl, time_t, AF_INET6, CTL_NET, IFF_BROADCAST, IFF_LOOPBACK, IFF_RUNNING, IFNAMSIZ, - NET_RT_FLAGS, PF_ROUTE, RTAX_DST, RTAX_GATEWAY, RTAX_MAX, RTA_DST, RTA_GATEWAY, RTF_GATEWAY, - SOCK_DGRAM, + socket, sysctl, time_t, AF_INET6, CTL_NET, IFF_BROADCAST, IFF_LOOPBACK, IFF_POINTOPOINT, + IFF_RUNNING, IFNAMSIZ, NET_RT_FLAGS, PF_ROUTE, RTAX_DST, RTAX_GATEWAY, RTAX_MAX, RTA_DST, + RTA_GATEWAY, RTF_GATEWAY, SOCK_DGRAM, }; use sockaddr_tools::SockAddr; use std::ffi::CStr; @@ -13,12 +14,15 @@ use std::io; use std::os::raw::{c_int, c_uchar, c_ulong, c_ushort, c_void}; const SIOCGIFAFLAG_IN6: c_ulong = 0xC1206949; +const SIOCGIFALIFETIME_IN6: c_ulong = 0xC1206951; const IN6_IFF_TENTATIVE: c_ushort = 0x0002; const IN6_IFF_DUPLICATED: c_ushort = 0x0004; const IN6_IFF_DETACHED: c_ushort = 0x0008; +const IN6_IFF_AUTOCONF: c_ushort = 0x0040; const IN6_IFF_TEMPORARY: c_ushort = 0x0080; const IN6_IFF_DEPRECATED: c_ushort = 0x0010; const IN6_IFF_DYNAMIC: c_ushort = 0x0100; +const IN6_IFF_SECURED: c_ushort = 0x0400; macro_rules! set_name { ($name_field:expr, $name_str:expr) => {{ @@ -198,6 +202,9 @@ impl in6_ifreq { pub fn get_flags6(&self) -> c_ushort { unsafe { self.ifr_ifru.ifru_flags6 as c_ushort } } + pub fn get_ia6t_expire(&self) -> time_t { + unsafe { self.ifr_ifru.ifru_lifetime.ia6t_expire as time_t } + } } pub fn do_broadcast(ifaddr: &ifaddrs) -> Option { @@ -267,13 +274,13 @@ pub struct PlatformSupportApple { } impl PlatformSupportApple { - pub fn new() -> EyreResult { - Ok(PlatformSupportApple { + pub fn new() -> Self { + PlatformSupportApple { default_route_interfaces: BTreeSet::new(), - }) + } } - async fn refresh_default_route_interfaces(&mut self) -> EyreResult<()> { + async fn refresh_default_route_interfaces(&mut self) { self.default_route_interfaces.clear(); let mut mib = [CTL_NET, PF_ROUTE, 0, 0, NET_RT_FLAGS, RTF_GATEWAY]; @@ -293,7 +300,8 @@ impl PlatformSupportApple { ) } < 0 { - bail!("Unable to get memory size for routing table"); + error!("Unable to get memory size for routing table"); + return; } // Allocate a buffer @@ -311,7 +319,8 @@ impl PlatformSupportApple { ) } < 0 { - bail!("Unable to get memory size for routing table"); + error!("Unable to get memory size for routing table"); + return; } // Process each routing message @@ -360,61 +369,68 @@ impl PlatformSupportApple { mib_ptr = unsafe { mib_ptr.add((*rt).rtm_msglen.into()) }; } - - Ok(()) } - fn get_interface_flags(&self, index: u32, flags: c_int) -> EyreResult { - Ok(InterfaceFlags { + fn get_interface_flags(&self, index: u32, flags: c_int) -> InterfaceFlags { + InterfaceFlags { is_loopback: (flags & IFF_LOOPBACK) != 0, is_running: (flags & IFF_RUNNING) != 0, + is_point_to_point: (flags & IFF_POINTOPOINT) != 0, has_default_route: self.default_route_interfaces.contains(&index), - }) + } } - fn get_address_flags(ifname: &str, addr: sockaddr_in6) -> EyreResult { + fn get_address_flags(ifname: &str, addr: sockaddr_in6) -> io::Result { + let sock = unsafe { socket(AF_INET6, SOCK_DGRAM, 0) }; + if sock < 0 { + return Err(io::Error::last_os_error()); + } + let mut req = in6_ifreq::from_name(ifname).unwrap(); req.set_addr(addr); - let sock = unsafe { socket(AF_INET6, SOCK_DGRAM, 0) }; - if sock < 0 { - bail!("Socket error {:?}", io::Error::last_os_error()); - } - let res = unsafe { ioctl(sock, SIOCGIFAFLAG_IN6, &mut req) }; - unsafe { close(sock) }; if res < 0 { - bail!( - "SIOCGIFAFLAG_IN6 failed with error on device '{}': {:?}", - ifname, - io::Error::last_os_error() - ); + unsafe { close(sock) }; + return Err(io::Error::last_os_error()); } - let flags = req.get_flags6(); + let mut req = in6_ifreq::from_name(ifname).unwrap(); + req.set_addr(addr); + + let res = unsafe { ioctl(sock, SIOCGIFALIFETIME_IN6, &mut req) }; + unsafe { close(sock) }; + if res < 0 { + return Err(io::Error::last_os_error()); + } + let expire = req.get_ia6t_expire(); + + let is_auto_generated_random_address = + flags & (IN6_IFF_SECURED | IN6_IFF_AUTOCONF) == (IN6_IFF_SECURED | IN6_IFF_AUTOCONF); + + let is_temporary = + (flags & IN6_IFF_TEMPORARY) != 0 || (expire != 0 && is_auto_generated_random_address); + let is_dynamic = (flags & (IN6_IFF_DYNAMIC | IN6_IFF_AUTOCONF)) != 0; + let is_preferred = (flags + & (IN6_IFF_TENTATIVE | IN6_IFF_DUPLICATED | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED)) + == 0; + Ok(AddressFlags { - is_temporary: (flags & IN6_IFF_TEMPORARY) != 0, - is_dynamic: (flags & IN6_IFF_DYNAMIC) != 0, - is_preferred: (flags - & (IN6_IFF_TENTATIVE | IN6_IFF_DUPLICATED | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED)) - == 0, + is_temporary, + is_dynamic, + is_preferred, }) } pub async fn get_interfaces( &mut self, interfaces: &mut BTreeMap, - ) -> EyreResult<()> { - self.refresh_default_route_interfaces().await?; - - // If we have no routes, this isn't going to work - if self.default_route_interfaces.is_empty() { - bail!("no routes available for NetworkInterfaces"); - } + ) -> io::Result<()> { + self.refresh_default_route_interfaces().await; // Ask for all the addresses we have - let ifaddrs = IfAddrs::new().wrap_err("failed to get interface addresses")?; + let ifaddrs = IfAddrs::new()?; for ifaddr in ifaddrs.iter() { // Get the interface name let ifname = unsafe { CStr::from_ptr(ifaddr.ifa_name) } @@ -427,7 +443,7 @@ impl PlatformSupportApple { // Map the name to a NetworkInterface if !interfaces.contains_key(&ifname) { // If we have no NetworkInterface yet, make one - let flags = self.get_interface_flags(ifindex, ifaddr.ifa_flags as c_int)?; + let flags = self.get_interface_flags(ifindex, ifaddr.ifa_flags as c_int); interfaces.insert(ifname.clone(), NetworkInterface::new(ifname.clone(), flags)); } let intf = interfaces.get_mut(&ifname).unwrap(); diff --git a/veilid-core/src/intf/native/network_interfaces/mod.rs b/veilid-tools/src/network_interfaces/mod.rs similarity index 90% rename from veilid-core/src/intf/native/network_interfaces/mod.rs rename to veilid-tools/src/network_interfaces/mod.rs index 4c53cab9..7ca44f47 100644 --- a/veilid-core/src/intf/native/network_interfaces/mod.rs +++ b/veilid-tools/src/network_interfaces/mod.rs @@ -1,18 +1,17 @@ +mod apple; +mod netlink; +mod sockaddr_tools; mod tools; +mod windows; use crate::*; cfg_if::cfg_if! { if #[cfg(any(target_os = "linux", target_os = "android"))] { - mod netlink; use self::netlink::PlatformSupportNetlink as PlatformSupport; } else if #[cfg(target_os = "windows")] { - mod windows; - mod sockaddr_tools; use self::windows::PlatformSupportWindows as PlatformSupport; } else if #[cfg(any(target_os = "macos", target_os = "ios"))] { - mod apple; - mod sockaddr_tools; use self::apple::PlatformSupportApple as PlatformSupport; } else { compile_error!("No network interfaces support for this platform!"); @@ -74,6 +73,7 @@ pub struct Ifv6Addr { pub struct InterfaceFlags { pub is_loopback: bool, pub is_running: bool, + pub is_point_to_point: bool, pub has_default_route: bool, } @@ -261,6 +261,10 @@ impl NetworkInterface { self.flags.is_loopback } + pub fn is_point_to_point(&self) -> bool { + self.flags.is_point_to_point + } + pub fn is_running(&self) -> bool { self.flags.is_running } @@ -310,13 +314,22 @@ impl fmt::Debug for NetworkInterfaces { .finish()?; if f.alternate() { writeln!(f)?; - writeln!(f, "// best_addresses: {:?}", inner.interface_address_cache)?; + writeln!( + f, + "// stable_addresses: {:?}", + inner.interface_address_cache + )?; } Ok(()) } } -#[allow(dead_code)] +impl Default for NetworkInterfaces { + fn default() -> Self { + Self::new() + } +} + impl NetworkInterfaces { pub fn new() -> Self { Self { @@ -339,14 +352,14 @@ impl NetworkInterfaces { inner.interface_address_cache.clear(); inner.valid = false; } - // returns Ok(false) if refresh had no changes, Ok(true) if changes were present - pub async fn refresh(&self) -> EyreResult { + // returns false if refresh had no changes, true if changes were present + pub async fn refresh(&self) -> std::io::Result { let mut last_interfaces = { let mut last_interfaces = BTreeMap::::new(); - let mut platform_support = PlatformSupport::new()?; - if let Err(e) = platform_support.get_interfaces(&mut last_interfaces).await { - debug!("no network interfaces are enabled: {}", e); - } + let mut platform_support = PlatformSupport::new(); + platform_support + .get_interfaces(&mut last_interfaces) + .await?; last_interfaces }; @@ -356,16 +369,16 @@ impl NetworkInterfaces { if last_interfaces != inner.interfaces { // get last address cache - let old_best_addresses = inner.interface_address_cache.clone(); + let old_stable_addresses = inner.interface_address_cache.clone(); // redo the address cache - Self::cache_best_addresses(&mut inner); + Self::cache_stable_addresses(&mut inner); // See if our best addresses have changed - if old_best_addresses != inner.interface_address_cache { - trace!( - "Network interface addresses changed: {:?}", - inner.interface_address_cache + if old_stable_addresses != inner.interface_address_cache { + debug!( + "Network interface addresses changed: \nFrom: {:?}\n To: {:?}\n", + old_stable_addresses, inner.interface_address_cache ); return Ok(true); } @@ -380,18 +393,22 @@ impl NetworkInterfaces { f(&inner.interfaces) } - pub fn best_addresses(&self) -> Vec { + pub fn stable_addresses(&self) -> Vec { let inner = self.inner.lock(); inner.interface_address_cache.clone() } ///////////////////////////////////////////// - fn cache_best_addresses(inner: &mut NetworkInterfacesInner) { + fn cache_stable_addresses(inner: &mut NetworkInterfacesInner) { // Reduce interfaces to their best routable ip addresses let mut intf_addrs = Vec::new(); for intf in inner.interfaces.values() { - if !intf.is_running() || !intf.has_default_route() || intf.is_loopback() { + if !intf.is_running() + || !intf.has_default_route() + || intf.is_loopback() + || intf.is_point_to_point() + { continue; } if let Some(pipv4) = intf.primary_ipv4() { diff --git a/veilid-core/src/intf/native/network_interfaces/netlink.rs b/veilid-tools/src/network_interfaces/netlink.rs similarity index 86% rename from veilid-core/src/intf/native/network_interfaces/netlink.rs rename to veilid-tools/src/network_interfaces/netlink.rs index e1f0c750..8354a775 100644 --- a/veilid-core/src/intf/native/network_interfaces/netlink.rs +++ b/veilid-tools/src/network_interfaces/netlink.rs @@ -1,17 +1,19 @@ +#![cfg(any(target_os = "linux", target_os = "android"))] + use super::*; -use alloc::collections::btree_map::Entry; use futures_util::stream::TryStreamExt; use ifstructs::ifreq; use libc::{ - close, if_indextoname, ioctl, socket, IFF_LOOPBACK, IFF_RUNNING, IF_NAMESIZE, SIOCGIFFLAGS, - SOCK_DGRAM, + close, if_indextoname, ioctl, socket, IFF_LOOPBACK, IFF_POINTOPOINT, IFF_RUNNING, IF_NAMESIZE, + SIOCGIFFLAGS, SOCK_DGRAM, }; use netlink_packet_route::{ nlas::address::Nla, AddressMessage, AF_INET, AF_INET6, IFA_F_DADFAILED, IFA_F_DEPRECATED, IFA_F_OPTIMISTIC, IFA_F_PERMANENT, IFA_F_TEMPORARY, IFA_F_TENTATIVE, }; use rtnetlink::{new_connection_with_socket, Handle, IpVersion}; +use std::collections::btree_map::Entry; cfg_if! { if #[cfg(feature="rt-async-std")] { use netlink_sys::{SmolSocket as RTNetLinkSocket}; @@ -27,16 +29,16 @@ use std::io; use std::os::raw::c_int; use tools::*; -fn get_interface_name(index: u32) -> EyreResult { +fn get_interface_name(index: u32) -> io::Result { let mut ifnamebuf = [0u8; (IF_NAMESIZE + 1)]; cfg_if! { if #[cfg(all(any(target_os = "android", target_os="linux"), any(target_arch = "arm", target_arch = "aarch64")))] { if unsafe { if_indextoname(index, ifnamebuf.as_mut_ptr()) }.is_null() { - bail!("if_indextoname returned null"); + bail_io_error_other!("if_indextoname returned null"); } } else { if unsafe { if_indextoname(index, ifnamebuf.as_mut_ptr() as *mut i8) }.is_null() { - bail!("if_indextoname returned null"); + bail_io_error_other!("if_indextoname returned null"); } } } @@ -44,11 +46,11 @@ fn get_interface_name(index: u32) -> EyreResult { let ifnamebuflen = ifnamebuf .iter() .position(|c| *c == 0u8) - .ok_or_else(|| eyre!("null not found in interface name"))?; + .ok_or_else(|| io_error_other!("null not found in interface name"))?; let ifname_str = CStr::from_bytes_with_nul(&ifnamebuf[0..=ifnamebuflen]) - .wrap_err("failed to convert interface name")? + .map_err(|e| io_error_other!(e))? .to_str() - .wrap_err("invalid characters in interface name")?; + .map_err(|e| io_error_other!(e))?; Ok(ifname_str.to_owned()) } @@ -69,16 +71,16 @@ pub struct PlatformSupportNetlink { } impl PlatformSupportNetlink { - pub fn new() -> EyreResult { - Ok(PlatformSupportNetlink { + pub fn new() -> Self { + PlatformSupportNetlink { connection_jh: None, handle: None, default_route_interfaces: BTreeSet::new(), - }) + } } // Figure out which interfaces have default routes - async fn refresh_default_route_interfaces(&mut self) -> EyreResult<()> { + async fn refresh_default_route_interfaces(&mut self) { self.default_route_interfaces.clear(); let mut routesv4 = self .handle @@ -110,15 +112,14 @@ impl PlatformSupportNetlink { } } } - Ok(()) } - fn get_interface_flags(&self, index: u32, ifname: &str) -> EyreResult { - let mut req = ifreq::from_name(ifname).wrap_err("failed to convert interface name")?; + fn get_interface_flags(&self, index: u32, ifname: &str) -> io::Result { + let mut req = ifreq::from_name(ifname)?; let sock = unsafe { socket(AF_INET as i32, SOCK_DGRAM, 0) }; if sock < 0 { - return Err(io::Error::last_os_error()).wrap_err("failed to create socket"); + return Err(io::Error::last_os_error()); } cfg_if! { @@ -130,7 +131,7 @@ impl PlatformSupportNetlink { } unsafe { close(sock) }; if res < 0 { - return Err(io::Error::last_os_error()).wrap_err("failed to close socket"); + return Err(io::Error::last_os_error()); } let flags = req.get_flags() as c_int; @@ -138,6 +139,7 @@ impl PlatformSupportNetlink { Ok(InterfaceFlags { is_loopback: (flags & IFF_LOOPBACK) != 0, is_running: (flags & IFF_RUNNING) != 0, + is_point_to_point: (flags & IFF_POINTOPOINT) != 0, has_default_route: self.default_route_interfaces.contains(&index), }) } @@ -244,23 +246,14 @@ impl PlatformSupportNetlink { async fn get_interfaces_internal( &mut self, interfaces: &mut BTreeMap, - ) -> EyreResult<()> { + ) -> io::Result<()> { // Refresh the routes - self.refresh_default_route_interfaces().await?; - - // If we have no routes, this isn't going to work - if self.default_route_interfaces.is_empty() { - bail!("no routes available for NetworkInterfaces"); - } + self.refresh_default_route_interfaces().await; // Ask for all the addresses we have let mut names = BTreeMap::::new(); let mut addresses = self.handle.as_ref().unwrap().address().get().execute(); - while let Some(msg) = addresses - .try_next() - .await - .wrap_err("failed to iterate interface addresses")? - { + while let Some(msg) = addresses.try_next().await.map_err(|e| io_error_other!(e))? { // Have we seen this interface index yet? // Get the name from the index, cached, if we can let ifname = match names.entry(msg.header.index) { @@ -318,10 +311,9 @@ impl PlatformSupportNetlink { pub async fn get_interfaces( &mut self, interfaces: &mut BTreeMap, - ) -> EyreResult<()> { + ) -> io::Result<()> { // Get the netlink connection - let (connection, handle, _) = new_connection_with_socket::() - .wrap_err("failed to create rtnetlink socket")?; + let (connection, handle, _) = new_connection_with_socket::()?; // Spawn a connection handler let connection_jh = spawn(connection); diff --git a/veilid-core/src/intf/native/network_interfaces/sockaddr_tools.rs b/veilid-tools/src/network_interfaces/sockaddr_tools.rs similarity index 82% rename from veilid-core/src/intf/native/network_interfaces/sockaddr_tools.rs rename to veilid-tools/src/network_interfaces/sockaddr_tools.rs index 605c6c32..3b61a985 100644 --- a/veilid-core/src/intf/native/network_interfaces/sockaddr_tools.rs +++ b/veilid-tools/src/network_interfaces/sockaddr_tools.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] // Copyright 2018 MaidSafe.net limited. // // This SAFE Network Software is licensed to you under the MIT license > 16) & 255) as u8, ((sa.sin_addr.s_addr >> 24) & 255) as u8, ))), - Some(SockAddrIn::In6(sa)) => { - // Ignore all fe80:: addresses as these are link locals - if sa.sin6_addr.s6_addr[0] == 0xfe && sa.sin6_addr.s6_addr[1] == 0x80 { - return None; - } - Some(IpAddr::V6(Ipv6Addr::from(sa.sin6_addr.s6_addr))) - } + Some(SockAddrIn::In6(sa)) => Some(IpAddr::V6(Ipv6Addr::from(sa.sin6_addr.s6_addr))), None => None, } } @@ -64,10 +59,6 @@ impl SockAddr { match self.sockaddr_in() { Some(SockAddrIn::In(sa)) => { let s_addr = unsafe { sa.sin_addr.S_un.S_addr() }; - // Ignore all 169.254.x.x addresses as these are not active interfaces - if s_addr & 65535 == 0xfea9 { - return None; - } Some(IpAddr::V4(Ipv4Addr::new( (s_addr & 255u32) as u8, ((s_addr >> 8) & 255u32) as u8, @@ -77,10 +68,6 @@ impl SockAddr { } Some(SockAddrIn::In6(sa)) => { let s6_addr = unsafe { sa.sin6_addr.u.Byte() }; - // Ignore all fe80:: addresses as these are link locals - if s6_addr[0] == 0xfe && s6_addr[1] == 0x80 { - return None; - } Some(IpAddr::V6(Ipv6Addr::from(*s6_addr))) } None => None, diff --git a/veilid-core/src/intf/native/network_interfaces/tools.rs b/veilid-tools/src/network_interfaces/tools.rs similarity index 100% rename from veilid-core/src/intf/native/network_interfaces/tools.rs rename to veilid-tools/src/network_interfaces/tools.rs diff --git a/veilid-core/src/intf/native/network_interfaces/windows.rs b/veilid-tools/src/network_interfaces/windows.rs similarity index 95% rename from veilid-core/src/intf/native/network_interfaces/windows.rs rename to veilid-tools/src/network_interfaces/windows.rs index 53b74ba2..661f8d69 100644 --- a/veilid-core/src/intf/native/network_interfaces/windows.rs +++ b/veilid-tools/src/network_interfaces/windows.rs @@ -1,3 +1,5 @@ +#![cfg(target_os = "windows")] + // Copyright 2018 MaidSafe.net limited. // // This SAFE Network Software is licensed to you under the MIT license EyreResult { - Ok(PlatformSupportWindows {}) + pub fn new() -> Self { + PlatformSupportWindows {} } fn get_interface_flags(intf: &IpAdapterAddresses) -> InterfaceFlags { InterfaceFlags { is_loopback: intf.get_flag_loopback(), is_running: intf.get_flag_running(), + is_point_to_point: intf.get_flag_point_to_point(), has_default_route: intf.get_has_default_route(), } } @@ -54,17 +57,9 @@ impl PlatformSupportWindows { pub async fn get_interfaces( &mut self, interfaces: &mut BTreeMap, - ) -> EyreResult<()> { - //self.refresh_default_route_interfaces().await?; - - // If we have no routes, this isn't going to work - // if self.default_route_interfaces.is_empty() { - // return Err("no routes available for NetworkInterfaces".to_owned()); - // } - + ) -> io::Result<()> { // Iterate all the interfaces - let windows_interfaces = - WindowsInterfaces::new().wrap_err("failed to get windows interfaces")?; + let windows_interfaces = WindowsInterfaces::new()?; for windows_interface in windows_interfaces.iter() { // Get name let intf_name = windows_interface.name(); @@ -224,6 +219,9 @@ impl IpAdapterAddresses { pub fn get_flag_running(&self) -> bool { unsafe { (*self.data).OperStatus == IfOperStatusUp } } + pub fn get_flag_point_to_point(&self) -> bool { + unsafe { (*self.data).IfType == IF_TYPE_TUNNEL } + } pub fn get_has_default_route(&self) -> bool { unsafe { !(*self.data).FirstGatewayAddress.is_null() } } diff --git a/veilid-tools/src/tests/ios/mod.rs b/veilid-tools/src/tests/ios/mod.rs index 77534395..ce4780df 100644 --- a/veilid-tools/src/tests/ios/mod.rs +++ b/veilid-tools/src/tests/ios/mod.rs @@ -15,20 +15,23 @@ pub extern "C" fn run_veilid_tools_tests() { pub fn veilid_tools_setup_ios_tests() { cfg_if! { if #[cfg(feature = "tracing")] { + use tracing::level_filters::LevelFilter; use tracing_oslog::OsLogger; use tracing_subscriber::prelude::*; + use tracing_subscriber::filter::Targets; - let mut filters = filter::Targets::new(); + let mut filters = Targets::new(); for ig in DEFAULT_LOG_IGNORE_LIST { - filters = filters.with_target(ig, filter::LevelFilter::OFF); + filters = filters.with_target(ig, LevelFilter::OFF); } tracing_subscriber::registry() + .with(OsLogger::new("com.veilid.veilidtools-tests", "default")) + .with(LevelFilter::TRACE) .with(filters) - .with(filter::LevelFilter::TRACE) - .with(OsLogger::new("com.veilid.veilidtools-tests", "")) .init(); } else { use oslog::OsLogger; + use log::LevelFilter; OsLogger::new("com.veilid.veilidtools-tests") .level_filter(LevelFilter::Trace) diff --git a/veilid-tools/src/tests/native/mod.rs b/veilid-tools/src/tests/native/mod.rs index 65da6ca5..7fcf2e9e 100644 --- a/veilid-tools/src/tests/native/mod.rs +++ b/veilid-tools/src/tests/native/mod.rs @@ -3,6 +3,7 @@ mod test_assembly_buffer; mod test_async_peek_stream; +mod test_network_interfaces; use super::*; @@ -13,6 +14,8 @@ use super::*; pub async fn run_all_tests() { info!("TEST: exec_test_host_interface"); test_host_interface::test_all().await; + info!("TEST: exec_test_network_interfaces"); + test_network_interfaces::test_all().await; info!("TEST: exec_test_async_peek_stream"); test_async_peek_stream::test_all().await; info!("TEST: exec_test_async_tag_lock"); @@ -82,6 +85,15 @@ cfg_if! { }); } + #[test] + #[serial] + fn run_test_network_interfaces() { + setup(); + block_on(async { + test_network_interfaces::test_all().await; + }); + } + #[test] #[serial] fn run_test_async_peek_stream() { diff --git a/veilid-core/src/tests/common/test_host_interface.rs b/veilid-tools/src/tests/native/test_network_interfaces.rs similarity index 94% rename from veilid-core/src/tests/common/test_host_interface.rs rename to veilid-tools/src/tests/native/test_network_interfaces.rs index d1a52cdc..4ba1844b 100644 --- a/veilid-core/src/tests/common/test_host_interface.rs +++ b/veilid-tools/src/tests/native/test_network_interfaces.rs @@ -2,7 +2,7 @@ use crate::*; cfg_if! { if #[cfg(not(target_arch = "wasm32"))] { - use intf::network_interfaces::NetworkInterfaces; + use network_interfaces::NetworkInterfaces; pub async fn test_network_interfaces() { info!("testing network interfaces");