windows upnp work

This commit is contained in:
John Smith 2023-10-13 17:57:38 -04:00
parent ebd36d82ef
commit d922bc1f5d
20 changed files with 138 additions and 111 deletions

16
Cargo.lock generated
View File

@ -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]]
@ -5406,9 +5405,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",
@ -5517,6 +5516,7 @@ dependencies = [
"wasm-bindgen-test",
"wasm-logger",
"wee_alloc",
"winapi",
]
[[package]]
@ -5781,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"

View File

@ -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"

View File

@ -170,7 +170,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"

View File

@ -12,6 +12,5 @@ pub use system::*;
#[cfg(target_os = "android")]
pub mod android;
pub mod network_interfaces;
use super::*;

View File

@ -26,7 +26,7 @@ struct PortMapValue {
struct IGDManagerInner {
local_ip_addrs: BTreeMap<AddressType, IpAddr>,
gateways: BTreeMap<AddressType, Arc<Gateway>>,
gateways: BTreeMap<IpAddr, Arc<Gateway>>,
port_maps: BTreeMap<PortMapKey, PortMapValue>,
}
@ -121,17 +121,21 @@ impl IGDManager {
fn find_gateway(
inner: &mut IGDManagerInner,
address_type: AddressType,
local_ip: IpAddr,
) -> Option<Arc<Gateway>> {
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<Arc<Gateway>> {
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(()) => {

View File

@ -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;
@ -316,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| {
@ -358,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<IpAddr> {
let addrs = self.unlocked_inner.interfaces.best_addresses();
pub fn get_stable_interface_addresses(&self) -> Vec<IpAddr> {
let addrs = self.unlocked_inner.interfaces.stable_addresses();
let addrs: Vec<IpAddr> = addrs
.into_iter()
.filter(|addr| {
@ -377,7 +376,13 @@ impl Network {
// See if our interface addresses have changed, if so redo public dial info if necessary
async fn check_interface_addresses(&self) -> EyreResult<bool> {
if !self.unlocked_inner.interfaces.refresh().await? {
if !self
.unlocked_inner
.interfaces
.refresh()
.await
.wrap_err("failed to check network interfaces")?
{
return Ok(false);
}
@ -723,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;

View File

@ -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)?;
}
}

View File

@ -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<IpAddr> {
pub fn get_stable_interface_addresses(&self) -> Vec<IpAddr> {
Vec::new()
}

View File

@ -1,4 +1,3 @@
pub mod test_host_interface;
pub mod test_protected_store;
pub mod test_veilid_config;
pub mod test_veilid_core;

View File

@ -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);

View File

@ -69,7 +69,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]
@ -93,9 +93,8 @@ 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]

View File

@ -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::*;

View File

@ -1,3 +1,4 @@
#![cfg(any(target_os = "macos", target_os = "ios"))]
#![allow(non_camel_case_types)]
use super::*;
@ -273,10 +274,10 @@ pub struct PlatformSupportApple {
}
impl PlatformSupportApple {
pub fn new() -> EyreResult<Self> {
Ok(PlatformSupportApple {
pub fn new() -> Self {
PlatformSupportApple {
default_route_interfaces: BTreeSet::new(),
})
}
}
async fn refresh_default_route_interfaces(&mut self) -> EyreResult<()> {
@ -433,11 +434,11 @@ impl PlatformSupportApple {
pub async fn get_interfaces(
&mut self,
interfaces: &mut BTreeMap<String, NetworkInterface>,
) -> EyreResult<()> {
) -> 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) }

View File

@ -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!");
@ -315,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 {
@ -344,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<bool> {
// returns false if refresh had no changes, true if changes were present
pub async fn refresh(&self) -> std::io::Result<bool> {
let mut last_interfaces = {
let mut last_interfaces = BTreeMap::<String, NetworkInterface>::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
};
@ -361,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 {
if old_stable_addresses != inner.interface_address_cache {
debug!(
"Network interface addresses changed: \nFrom: {:?}\n To: {:?}\n",
old_best_addresses, inner.interface_address_cache
old_stable_addresses, inner.interface_address_cache
);
return Ok(true);
}
@ -385,14 +393,14 @@ impl NetworkInterfaces {
f(&inner.interfaces)
}
pub fn best_addresses(&self) -> Vec<IpAddr> {
pub fn stable_addresses(&self) -> Vec<IpAddr> {
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() {

View File

@ -1,3 +1,5 @@
#![cfg(any(target_os = "linux", target_os = "android"))]
use super::*;
use alloc::collections::btree_map::Entry;
@ -27,7 +29,7 @@ use std::io;
use std::os::raw::c_int;
use tools::*;
fn get_interface_name(index: u32) -> EyreResult<String> {
fn get_interface_name(index: u32) -> io::Result<String> {
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")))] {
@ -69,12 +71,12 @@ pub struct PlatformSupportNetlink {
}
impl PlatformSupportNetlink {
pub fn new() -> EyreResult<Self> {
Ok(PlatformSupportNetlink {
pub fn new() -> Self {
PlatformSupportNetlink {
connection_jh: None,
handle: None,
default_route_interfaces: BTreeSet::new(),
})
}
}
// Figure out which interfaces have default routes
@ -245,18 +247,14 @@ impl PlatformSupportNetlink {
async fn get_interfaces_internal(
&mut self,
interfaces: &mut BTreeMap<String, NetworkInterface>,
) -> EyreResult<()> {
) -> io::Result<()> {
// Refresh the routes
self.refresh_default_route_interfaces().await?;
// Ask for all the addresses we have
let mut names = BTreeMap::<u32, String>::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? {
// 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) {
@ -314,10 +312,9 @@ impl PlatformSupportNetlink {
pub async fn get_interfaces(
&mut self,
interfaces: &mut BTreeMap<String, NetworkInterface>,
) -> EyreResult<()> {
) -> io::Result<()> {
// Get the netlink connection
let (connection, handle, _) = new_connection_with_socket::<RTNetLinkSocket>()
.wrap_err("failed to create rtnetlink socket")?;
let (connection, handle, _) = new_connection_with_socket::<RTNetLinkSocket>()?;
// Spawn a connection handler
let connection_jh = spawn(connection);

View File

@ -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 <LICENSE-MIT
@ -28,8 +30,8 @@ use winapi::um::iptypes::{
pub struct PlatformSupportWindows {}
impl PlatformSupportWindows {
pub fn new() -> EyreResult<Self> {
Ok(PlatformSupportWindows {})
pub fn new() -> Self {
PlatformSupportWindows {}
}
fn get_interface_flags(intf: &IpAdapterAddresses) -> InterfaceFlags {
@ -55,10 +57,9 @@ impl PlatformSupportWindows {
pub async fn get_interfaces(
&mut self,
interfaces: &mut BTreeMap<String, NetworkInterface>,
) -> EyreResult<()> {
) -> 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();

View File

@ -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() {

View File

@ -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");