From 062f243e504222d8318f953f1805b6b712f9b994 Mon Sep 17 00:00:00 2001 From: John Smith Date: Fri, 14 Jan 2022 09:14:49 -0500 Subject: [PATCH] support routing table for ios/macos --- Cargo.lock | 2 +- setup_ios.sh | 2 +- veilid-core/Cargo.toml | 4 + veilid-core/ios_build.sh | 4 +- .../native/utils/network_interfaces/apple.rs | 474 +++++++++++++++++- .../deprecated/old_android_get_if_addrs.rs | 146 ------ .../deprecated/old_linux.rs | 232 --------- .../native/utils/network_interfaces/mod.rs | 2 +- .../network_interfaces/sockaddr_tools.rs | 117 +++++ .../project.pbxproj | 12 +- 10 files changed, 603 insertions(+), 392 deletions(-) delete mode 100644 veilid-core/src/intf/native/utils/network_interfaces/deprecated/old_android_get_if_addrs.rs delete mode 100644 veilid-core/src/intf/native/utils/network_interfaces/deprecated/old_linux.rs create mode 100644 veilid-core/src/intf/native/utils/network_interfaces/sockaddr_tools.rs diff --git a/Cargo.lock b/Cargo.lock index b19ac206..e2908933 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1855,7 +1855,7 @@ dependencies = [ "security-framework-sys", "serde 1.0.133", "serde_cbor", - "serial_test 0.4.0", + "serial_test 0.5.1", "simplelog", "snailquote", "tempfile", diff --git a/setup_ios.sh b/setup_ios.sh index 1bd73ca9..13970e08 100755 --- a/setup_ios.sh +++ b/setup_ios.sh @@ -10,7 +10,7 @@ fi rustup target add aarch64-apple-darwin aarch64-apple-ios x86_64-apple-darwin x86_64-apple-ios echo Manual Step: -echo install +ios-arm64-nightly-YYYY-MM-DD toolchain for bitcode from https://github.com/getditto/rust-bitcode/releases/latest and unzip +echo install +ios-arm64-1.57.0 toolchain for bitcode from https://github.com/getditto/rust-bitcode/releases/latest and unzip echo xattr -d -r com.apple.quarantine . echo ./install.sh diff --git a/veilid-core/Cargo.toml b/veilid-core/Cargo.toml index 1cd06d85..d1eaaaf4 100644 --- a/veilid-core/Cargo.toml +++ b/veilid-core/Cargo.toml @@ -122,6 +122,10 @@ ifstructs = "^0" [target.'cfg(any(target_os = "android",target_os = "linux"))'.dependencies] rtnetlink = { version = "^0", default-features = false, features = [ "smol_socket" ] } +# Dependencies for MacOS and iOS +#[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] +# XXX + # Dependencies for iOS [target.'cfg(target_os = "ios")'.dependencies] simplelog = { version = "^0", optional = true } diff --git a/veilid-core/ios_build.sh b/veilid-core/ios_build.sh index 1e4d7280..a0d985e5 100755 --- a/veilid-core/ios_build.sh +++ b/veilid-core/ios_build.sh @@ -15,7 +15,7 @@ do if [ "$arch" == "arm64" ]; then echo arm64 CARGO_TARGET=aarch64-apple-ios - CARGO_TOOLCHAIN=+ios-arm64-nightly-2021-06-12 + CARGO_TOOLCHAIN=+ios-arm64-1.57.0 #CARGO_TOOLCHAIN= elif [ "$arch" == "x86_64" ]; then echo x86_64 @@ -25,6 +25,6 @@ do echo Unsupported ARCH: $arch continue fi - env -i PATH=/usr/bin:/bin:/usr/local/bin ~/.cargo/bin/cargo $CARGO_TOOLCHAIN build $EXTRA_CARGO_OPTIONS --target $CARGO_TARGET --manifest-path $CARGO_MANIFEST_PATH + env -i PATH=/usr/bin:/bin:/usr/local/bin:/opt/homebrew/bin ~/.cargo/bin/cargo $CARGO_TOOLCHAIN build $EXTRA_CARGO_OPTIONS --target $CARGO_TARGET --manifest-path $CARGO_MANIFEST_PATH done diff --git a/veilid-core/src/intf/native/utils/network_interfaces/apple.rs b/veilid-core/src/intf/native/utils/network_interfaces/apple.rs index ef544b54..9faaa9c0 100644 --- a/veilid-core/src/intf/native/utils/network_interfaces/apple.rs +++ b/veilid-core/src/intf/native/utils/network_interfaces/apple.rs @@ -1,19 +1,487 @@ 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, +}; +use sockaddr_tools::SockAddr; +use std::ffi::CStr; +use std::io; +use std::os::raw::{c_int, c_uchar, c_ulong, c_ushort, c_void}; + +//const SIOCGIFFLAGS:c_ulong = 0xC0206911; +const SIOCGIFAFLAG_IN6: c_ulong = 0xC1206949; +const IN6_IFF_TEMPORARY: c_ushort = 0x0080; +const IN6_IFF_DEPRECATED: c_ushort = 0x0010; +const IN6_IFF_DYNAMIC: c_ushort = 0x0100; + +macro_rules! set_name { + ($name_field:expr, $name_str:expr) => {{ + let name_c = &::std::ffi::CString::new($name_str.to_owned()).map_err(|_| { + ::std::io::Error::new( + ::std::io::ErrorKind::InvalidInput, + "malformed interface name", + ) + })?; + let name_slice = name_c.as_bytes_with_nul(); + if name_slice.len() > IFNAMSIZ { + return Err(io::Error::new(::std::io::ErrorKind::InvalidInput, "")); + } + $name_field[..name_slice.len()].clone_from_slice(name_slice); + + Ok(()) + }}; +} + +macro_rules! round_up { + ($a:expr) => { + if $a > 0 { + 1 + (($a - 1) | 3) + } else { + 4 + } + }; +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +struct rt_msghdr { + rtm_msglen: c_ushort, + rtm_version: c_uchar, + rtm_type: c_uchar, + rtm_index: c_ushort, + rtm_flags: c_int, + rtm_addrs: c_int, + rtm_pid: pid_t, + rtm_seq: c_int, + rtm_errno: c_int, + rtm_use: c_int, + rtm_inits: u32, + rtm_rmx: rt_metrics, +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +struct rt_metrics { + rmx_locks: u32, + rmx_mtu: u32, + rmx_hopcount: u32, + rmx_expire: i32, + rmx_recvpipe: u32, + rmx_sendpipe: u32, + rmx_ssthresh: u32, + rmx_rtt: u32, + rmx_rttvar: u32, + rmx_pksent: u32, + rmx_state: u32, + rmx_filler: [u32; 3], +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +struct in6_addrlifetime { + ia6t_expire: time_t, + ia6t_preferred: time_t, + ia6t_vltime: u32, + ia6t_pltime: u32, +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +struct in6_ifstat { + ifs6_in_receive: u64, + ifs6_in_hdrerr: u64, + ifs6_in_toobig: u64, + ifs6_in_noroute: u64, + ifs6_in_addrerr: u64, + ifs6_in_protounknown: u64, + ifs6_in_truncated: u64, + ifs6_in_discard: u64, + ifs6_in_deliver: u64, + ifs6_out_forward: u64, + ifs6_out_request: u64, + ifs6_out_discard: u64, + ifs6_out_fragok: u64, + ifs6_out_fragfail: u64, + ifs6_out_fragcreat: u64, + ifs6_reass_reqd: u64, + ifs6_reass_ok: u64, + ifs6_atmfrag_rcvd: u64, + ifs6_reass_fail: u64, + ifs6_in_mcast: u64, + ifs6_out_mcast: u64, + ifs6_cantfoward_icmp6: u64, + ifs6_addr_expiry_cnt: u64, + ifs6_pfx_expiry_cnt: u64, + ifs6_defrtr_expiry_cnt: u64, +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +struct icmp6_ifstat { + ifs6_in_msg: u64, + ifs6_in_error: u64, + ifs6_in_dstunreach: u64, + ifs6_in_adminprohib: u64, + ifs6_in_timeexceed: u64, + ifs6_in_paramprob: u64, + ifs6_in_pkttoobig: u64, + ifs6_in_echo: u64, + ifs6_in_echoreply: u64, + ifs6_in_routersolicit: u64, + ifs6_in_routeradvert: u64, + ifs6_in_neighborsolicit: u64, + ifs6_in_neighboradvert: u64, + ifs6_in_redirect: u64, + ifs6_in_mldquery: u64, + ifs6_in_mldreport: u64, + ifs6_in_mlddone: u64, + ifs6_out_msg: u64, + ifs6_out_error: u64, + ifs6_out_dstunreach: u64, + ifs6_out_adminprohib: u64, + ifs6_out_timeexceed: u64, + ifs6_out_paramprob: u64, + ifs6_out_pkttoobig: u64, + ifs6_out_echo: u64, + ifs6_out_echoreply: u64, + ifs6_out_routersolicit: u64, + ifs6_out_routeradvert: u64, + ifs6_out_neighborsolicit: u64, + ifs6_out_neighboradvert: u64, + ifs6_out_redirect: u64, + ifs6_out_mldquery: u64, + ifs6_out_mldreport: u64, + ifs6_out_mlddone: u64, +} + +#[derive(Clone, Copy)] +#[repr(C)] +union IfrIfru { + ifru_addr: sockaddr_in6, + ifru_dstaddr: sockaddr_in6, + ifru_flags: c_int, + ifru_flags6: c_int, + ifru_metric: c_int, + ifru_intval: c_int, + ifru_data: *mut c_uchar, // caddr_t + ifru_lifetime: in6_addrlifetime, + ifru_stat: in6_ifstat, + ifru_icmp6stat: icmp6_ifstat, + ifru_scope_id: [u32; 16], +} + +#[derive(Clone)] +#[repr(C)] +struct in6_ifreq { + ifr_name: [c_uchar; IFNAMSIZ], + ifr_ifru: IfrIfru, +} + +impl in6_ifreq { + pub fn from_name(name: &str) -> io::Result { + let mut req: in6_ifreq = unsafe { mem::zeroed() }; + req.set_name(name)?; + Ok(req) + } + pub fn set_name(&mut self, name: &str) -> io::Result<()> { + set_name!(self.ifr_name, name) + } + pub fn set_addr(&mut self, addr: sockaddr_in6) { + self.ifr_ifru.ifru_addr = addr; + } + pub fn get_flags6(&self) -> c_ushort { + unsafe { self.ifr_ifru.ifru_flags6 as c_ushort } + } +} + +pub fn do_broadcast(ifaddr: &ifaddrs) -> Option { + sockaddr_tools::to_ipaddr(ifaddr.ifa_dstaddr) +} + +/////////////////////////////////////////////////// + +pub struct IfAddrs { + inner: *mut ifaddrs, +} + +impl IfAddrs { + #[allow(unsafe_code, clippy::new_ret_no_self)] + pub fn new() -> io::Result { + let mut ifaddrs = mem::MaybeUninit::uninit(); + + unsafe { + if -1 == getifaddrs(ifaddrs.as_mut_ptr()) { + return Err(io::Error::last_os_error()); + } + Ok(Self { + inner: ifaddrs.assume_init(), + }) + } + } + + pub fn iter(&self) -> IfAddrsIterator { + IfAddrsIterator { next: self.inner } + } +} + +impl Drop for IfAddrs { + #[allow(unsafe_code)] + fn drop(&mut self) { + unsafe { + freeifaddrs(self.inner); + } + } +} + +pub struct IfAddrsIterator { + next: *mut ifaddrs, +} + +impl Iterator for IfAddrsIterator { + type Item = ifaddrs; + + #[allow(unsafe_code)] + fn next(&mut self) -> Option { + if self.next.is_null() { + return None; + }; + + Some(unsafe { + let result = *self.next; + self.next = (*self.next).ifa_next; + + result + }) + } +} + +/////////////////////////////////////////////////// pub struct PlatformSupportApple { - // + default_route_interfaces: BTreeSet, } impl PlatformSupportApple { pub fn new() -> Result { - Ok(PlatformSupportApple {}) + Ok(PlatformSupportApple { + default_route_interfaces: BTreeSet::new(), + }) + } + + async fn refresh_default_route_interfaces(&mut self) -> Result<(), String> { + self.default_route_interfaces.clear(); + + let mut mib = [CTL_NET, PF_ROUTE, 0, 0, NET_RT_FLAGS, RTF_GATEWAY]; + let mut sa_tab: [*const sockaddr; RTAX_MAX as usize] = + [std::ptr::null(); RTAX_MAX as usize]; + let mut rt_buf_len = 0usize; + + // Get memory size for mib result + if unsafe { + sysctl( + mib.as_mut_ptr(), + mib.len() as u32, + std::ptr::null_mut(), + &mut rt_buf_len as *mut usize, + std::ptr::null_mut(), + 0, + ) + } < 0 + { + return Err("Unable to get memory size for routing table".to_owned()); + } + + // Allocate a buffer + let mut rt_buf = vec![0u8; rt_buf_len]; + + // Get mib result + if unsafe { + sysctl( + mib.as_mut_ptr(), + mib.len() as u32, + rt_buf.as_mut_ptr() as *mut c_void, + &mut rt_buf_len as *mut usize, + std::ptr::null_mut(), + 0, + ) + } < 0 + { + return Err("Unable to get memory size for routing table".to_owned()); + } + + // Process each routing message + let mut mib_ptr = rt_buf.as_ptr(); + let mib_end = unsafe { mib_ptr.add(rt_buf_len) }; + while mib_ptr < mib_end { + let rt = mib_ptr as *const rt_msghdr; + let mut sa = unsafe { rt.add(1) } as *const sockaddr; + let rtm_addrs = unsafe { (*rt).rtm_addrs }; + let intf_index = unsafe { (*rt).rtm_index } as u32; + + // Fill in sockaddr table + for i in 0..(RTAX_MAX as usize) { + if rtm_addrs & (1 << i) != 0 { + sa_tab[i] = sa; + sa = unsafe { + let sa_len = (*sa).sa_len; + sa = ((sa as *const u8).add(round_up!(sa_len as usize))) as *const sockaddr; + sa + }; + } + } + + // Look for gateways + if rtm_addrs & (RTA_DST | RTA_GATEWAY) == (RTA_DST | RTA_GATEWAY) { + // Only interested in AF_INET and AF_INET6 address families + // SockAddr::new() takes care of this for us + let saddr_dst = match SockAddr::new(sa_tab[RTAX_DST as usize]) { + Some(a) => a, + None => continue, + }; + let _saddr_gateway = match SockAddr::new(sa_tab[RTAX_GATEWAY as usize]) { + Some(a) => a, + None => continue, + }; + + // Look for default gateways + let dst_ipaddr = match saddr_dst.as_ipaddr() { + Some(a) => a, + None => continue, + }; + if dst_ipaddr.is_unspecified() { + self.default_route_interfaces.insert(intf_index); + } + } + + mib_ptr = unsafe { mib_ptr.add((*rt).rtm_msglen.into()) }; + } + + Ok(()) + } + + fn get_interface_flags(&self, index: u32, flags: c_int) -> Result { + Ok(InterfaceFlags { + is_loopback: (flags & IFF_LOOPBACK) != 0, + is_running: (flags & IFF_RUNNING) != 0, + has_default_route: self.default_route_interfaces.contains(&index), + }) + } + + fn get_address_flags(ifname: &str, addr: sockaddr_in6) -> Result { + 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 { + return Err(format!("Socket error {:?}", io::Error::last_os_error())); + } + + let res = unsafe { ioctl(sock, SIOCGIFAFLAG_IN6, &mut req) }; + unsafe { close(sock) }; + if res < 0 { + return Err(format!( + "SIOCGIFAFLAG_IN6 failed with error on device '{}': {:?}", + ifname, + io::Error::last_os_error() + )); + } + + let flags = req.get_flags6(); + + Ok(AddressFlags { + is_temporary: (flags & IN6_IFF_TEMPORARY) != 0, + is_dynamic: (flags & IN6_IFF_DYNAMIC) != 0, + is_deprecated: (flags & IN6_IFF_DEPRECATED) != 0, + }) } pub async fn get_interfaces( &mut self, interfaces: &mut BTreeMap, ) -> Result<(), String> { - // + 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()); + } + + // Ask for all the addresses we have + let ifaddrs = IfAddrs::new().map_err(map_to_string)?; + for ifaddr in ifaddrs.iter() { + // Get the interface name + let ifname = unsafe { CStr::from_ptr(ifaddr.ifa_name) } + .to_string_lossy() + .into_owned(); + + // Get the interface index + let ifindex = unsafe { if_nametoindex(ifaddr.ifa_name) }; + + // 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)?; + interfaces.insert(ifname.clone(), NetworkInterface::new(ifname.clone(), flags)); + } + let intf = interfaces.get_mut(&ifname).unwrap(); + + let mut address_flags = AddressFlags::default(); + + let intf_addr = match sockaddr_tools::to_ipaddr(ifaddr.ifa_addr) { + None => continue, + Some(IpAddr::V4(ipv4_addr)) => { + let netmask = match sockaddr_tools::to_ipaddr(ifaddr.ifa_netmask) { + Some(IpAddr::V4(netmask)) => netmask, + _ => Ipv4Addr::new(0, 0, 0, 0), + }; + let broadcast = if (ifaddr.ifa_flags & (IFF_BROADCAST as u32)) != 0 { + match do_broadcast(&ifaddr) { + Some(IpAddr::V4(broadcast)) => Some(broadcast), + _ => None, + } + } else { + None + }; + + IfAddr::V4(Ifv4Addr { + ip: ipv4_addr, + netmask, + broadcast, + }) + } + Some(IpAddr::V6(ipv6_addr)) => { + let netmask = match sockaddr_tools::to_ipaddr(ifaddr.ifa_netmask) { + Some(IpAddr::V6(netmask)) => netmask, + _ => Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), + }; + + // Get address flags for ipv6 + address_flags = match Self::get_address_flags( + &ifname, + SockAddr::new(ifaddr.ifa_addr).unwrap().sa_in6(), + ) { + Ok(v) => v, + Err(e) => { + log_net!(error "{}", e); + continue; + } + }; + + IfAddr::V6(Ifv6Addr { + ip: ipv6_addr, + netmask, + broadcast: None, + }) + } + }; + + // Add to the list + intf.addrs + .push(InterfaceAddress::new(intf_addr, address_flags)); + } + Ok(()) } } diff --git a/veilid-core/src/intf/native/utils/network_interfaces/deprecated/old_android_get_if_addrs.rs b/veilid-core/src/intf/native/utils/network_interfaces/deprecated/old_android_get_if_addrs.rs deleted file mode 100644 index 099eaf0f..00000000 --- a/veilid-core/src/intf/native/utils/network_interfaces/deprecated/old_android_get_if_addrs.rs +++ /dev/null @@ -1,146 +0,0 @@ -use super::*; -use crate::xx::*; -use jni::objects::JValue; -use std::io; - -macro_rules! call_method_checked { - ($env:expr, $obj:expr, $name:expr, $sig:expr, $args:expr, $kind:ident) => { - $env.call_method($obj, $name, $sig, $args) - .map_err(|e| { - io::Error::new( - io::ErrorKind::Other, - format!("call_method {} {} failed: {}", $name, $sig, e), - ) - })? - .$kind() - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))? - }; -} - -pub fn get_if_addrs() -> io::Result> { - let aglock = ANDROID_GLOBALS.lock(); - let ag = aglock.as_ref().unwrap(); - let env = ag.vm.attach_current_thread().unwrap(); - - let niclass = env.find_class("java/net/NetworkInterface").unwrap(); - let intfenum = env - .call_static_method( - niclass, - "getNetworkInterfaces", - "()Ljava/util/Enumeration;", - &[], - ) - .unwrap() - .l() - .unwrap(); - - let mut out: Vec = Vec::new(); - while call_method_checked!(env, intfenum, "hasMoreElements", "()Z", &[], z) { - let intf = - call_method_checked!(env, intfenum, "nextElement", "()Ljava/lang/Object;", &[], l); - - let nameobj = call_method_checked!(env, intf, "getName", "()Ljava/lang/String;", &[], l); - let name_jstrval = env.get_string(JString::from(nameobj)).unwrap(); - let name = String::from(name_jstrval.to_string_lossy()); - - let intfaddrs = call_method_checked!( - env, - intf, - "getInterfaceAddresses", - "()Ljava/util/List;", - &[], - l - ); - let size = call_method_checked!(env, intfaddrs, "size", "()I", &[], i); - for i in 0..size { - let intfaddr = call_method_checked!( - env, - intfaddrs, - "get", - "(I)Ljava/lang/Object;", - &[JValue::Int(i)], - l - ); - - let ia_addr = call_method_checked!( - env, - intfaddr, - "getAddress", - "()Ljava/net/InetAddress;", - &[], - l - ); - let ia_bcst = call_method_checked!( - env, - intfaddr, - "getBroadcast", - "()Ljava/net/InetAddress;", - &[], - l - ); - let ia_plen = - call_method_checked!(env, intfaddr, "getNetworkPrefixLength", "()S", &[], s); - - let ia_addr_bytearray = - call_method_checked!(env, ia_addr, "getAddress", "()[B", &[], l); - let ia_addr_bytearray_len = env.get_array_length(*ia_addr_bytearray).unwrap(); - let addr: IfAddr; - if ia_addr_bytearray_len == 4 { - let mut ia_addr_bytes_v4 = [0i8; 4]; - env.get_byte_array_region(*ia_addr_bytearray, 0, &mut ia_addr_bytes_v4) - .unwrap(); - - let broadcast = if !env.is_same_object(ia_bcst, JObject::null()).unwrap() { - let ia_bcst_bytearray = - call_method_checked!(env, ia_bcst, "getAddress", "()[B", &[], l); - let ia_bcst_bytearray_len = env.get_array_length(*ia_bcst_bytearray).unwrap(); - if ia_bcst_bytearray_len != 4 { - warn!( - "mismatched inet4 broadcast address length: {}", - ia_bcst_bytearray_len - ); - continue; - } - - let mut ia_bsct_bytes_v4 = [0i8; 4]; - env.get_byte_array_region(*ia_bcst_bytearray, 0, &mut ia_bsct_bytes_v4) - .unwrap(); - - Some(Ipv4Addr::from(convert_to_unsigned_4(ia_bsct_bytes_v4))) - } else { - None - }; - - let mut ia_netmask_bytes_v4 = [0u8; 4]; - get_netmask_from_prefix_length_v4(&mut ia_netmask_bytes_v4, ia_plen); - addr = IfAddr::V4(Ifv4Addr { - ip: Ipv4Addr::from(convert_to_unsigned_4(ia_addr_bytes_v4)), - netmask: Ipv4Addr::from(ia_netmask_bytes_v4), - broadcast: broadcast, - }); - } else if ia_addr_bytearray_len == 16 { - let mut ia_addr_bytes_v6 = [0i8; 16]; - env.get_byte_array_region(*ia_addr_bytearray, 0, &mut ia_addr_bytes_v6) - .unwrap(); - - let mut ia_netmask_bytes_v6 = [0u8; 16]; - get_netmask_from_prefix_length_v6(&mut ia_netmask_bytes_v6, ia_plen); - addr = IfAddr::V6(Ifv6Addr { - ip: Ipv6Addr::from(convert_to_unsigned_16(ia_addr_bytes_v6)), - netmask: Ipv6Addr::from(ia_netmask_bytes_v6), - broadcast: None, - }); - } else { - warn!("weird inet address length: {}", ia_addr_bytearray_len); - continue; - } - let elem = Interface { - name: name.clone(), - addr: addr, - }; - - out.push(elem); - } - } - Ok(out) -} diff --git a/veilid-core/src/intf/native/utils/network_interfaces/deprecated/old_linux.rs b/veilid-core/src/intf/native/utils/network_interfaces/deprecated/old_linux.rs deleted file mode 100644 index d6e152d2..00000000 --- a/veilid-core/src/intf/native/utils/network_interfaces/deprecated/old_linux.rs +++ /dev/null @@ -1,232 +0,0 @@ -#![allow(dead_code)] -use super::*; -use crate::xx::*; -use hex::FromHex; -use if_addrs::{IfAddr, Ifv4Addr, Ifv6Addr}; -use std::fs::File; -use std::io::{BufRead, BufReader, Error, ErrorKind}; -use std::net::{Ipv4Addr, Ipv6Addr}; - -#[derive(Debug)] -struct ProcNetIpv6RouteEntry { - dest_network: Ipv6Addr, - dest_prefix: u8, - src_network: Ipv6Addr, - src_prefix: u8, - next_hop: Ipv6Addr, - metric: u32, - ref_count: u32, - use_count: u32, - flags: u32, - intf_name: String, -} - -#[derive(Debug)] -struct ProcNetRouteEntry { - iface: String, - destination: Ipv4Addr, - gateway: Ipv4Addr, - flags: u16, - ref_count: u32, - use_count: u32, - metric: u32, - mask: Ipv4Addr, - mtu: u32, - window: u32, - irtt: u32, -} - -#[derive(Debug)] -pub struct PlatformSupport { - proc_net_ipv6_route: Vec, - proc_net_route: Vec, -} - -impl PlatformSupport { - fn parse_proc_net_ipv6_route() -> Result, Error> { - let file = File::open("/proc/net/ipv6_route")?; - let reader = BufReader::new(file); - let mut ipv6_route: Vec = Vec::new(); - for line in reader.lines() { - let line = line?; - let line: Vec<&str> = line.split_ascii_whitespace().collect(); - if line.len() != 10 { - return Err(Error::new( - ErrorKind::InvalidData, - "Unexpected number of columns in /proc/net/ipv6_route", - )); - } - - let entry = - ProcNetIpv6RouteEntry { - dest_network: Ipv6Addr::from(<[u8; 16]>::from_hex(line[0]).map_err(|_| { - Error::new(ErrorKind::InvalidData, "Unable to parse dest_network") - })?), - dest_prefix: <[u8; 1]>::from_hex(line[1]).map_err(|_| { - Error::new(ErrorKind::InvalidData, "Unable to parse dest_prefix") - })?[0], - src_network: Ipv6Addr::from(<[u8; 16]>::from_hex(line[2]).map_err(|_| { - Error::new(ErrorKind::InvalidData, "Unable to parse src_network") - })?), - src_prefix: <[u8; 1]>::from_hex(line[3]).map_err(|_| { - Error::new(ErrorKind::InvalidData, "Unable to parse src_prefix") - })?[0], - next_hop: Ipv6Addr::from(<[u8; 16]>::from_hex(line[4]).map_err(|_| { - Error::new(ErrorKind::InvalidData, "Unable to parse next_hop") - })?), - metric: u32::from_be_bytes(<[u8; 4]>::from_hex(line[5]).map_err(|_| { - Error::new(ErrorKind::InvalidData, "Unable to parse metric") - })?), - ref_count: u32::from_be_bytes(<[u8; 4]>::from_hex(line[6]).map_err(|_| { - Error::new(ErrorKind::InvalidData, "Unable to parse ref_count") - })?), - use_count: u32::from_be_bytes(<[u8; 4]>::from_hex(line[7]).map_err(|_| { - Error::new(ErrorKind::InvalidData, "Unable to parse use_count") - })?), - flags: u32::from_be_bytes(<[u8; 4]>::from_hex(line[8]).map_err(|_| { - Error::new(ErrorKind::InvalidData, "Unable to parse flags") - })?), - intf_name: String::from(line[9]), - }; - - ipv6_route.push(entry) - } - - Ok(ipv6_route) - } - - fn parse_proc_net_route() -> Result, Error> { - let file = File::open("/proc/net/route")?; - let reader = BufReader::new(file); - let mut route: Vec = Vec::new(); - let mut first = false; - for line in reader.lines() { - let line = line?; - let line: Vec<&str> = line.split_ascii_whitespace().collect(); - if line.len() != 11 { - return Err(Error::new( - ErrorKind::InvalidData, - "Unexpected number of columns in /proc/net/route", - )); - } - if first { - if line - != [ - "Iface", - "Destination", - "Gateway", - "Flags", - "RefCnt", - "Use", - "Metric", - "Mask", - "MTU", - "Window", - "IRTT", - ] - { - return Err(Error::new( - ErrorKind::InvalidData, - format!("Unexpected columns in /proc/net/route: {:?}", line), - )); - } - first = false; - continue; - } - - let entry = - ProcNetRouteEntry { - iface: String::from(line[0]), - - destination: Ipv4Addr::from(u32::from_le_bytes( - <[u8; 4]>::from_hex(line[0]).map_err(|_| { - Error::new(ErrorKind::InvalidData, "Unable to parse destination") - })?, - )), - gateway: Ipv4Addr::from(u32::from_le_bytes( - <[u8; 4]>::from_hex(line[0]).map_err(|_| { - Error::new(ErrorKind::InvalidData, "Unable to parse gateway") - })?, - )), - flags: u16::from_be_bytes(<[u8; 2]>::from_hex(line[8]).map_err(|_| { - Error::new(ErrorKind::InvalidData, "Unable to parse flags") - })?), - ref_count: u32::from_be_bytes(<[u8; 4]>::from_hex(line[6]).map_err(|_| { - Error::new(ErrorKind::InvalidData, "Unable to parse ref_count") - })?), - use_count: u32::from_be_bytes(<[u8; 4]>::from_hex(line[7]).map_err(|_| { - Error::new(ErrorKind::InvalidData, "Unable to parse use_count") - })?), - metric: u32::from_be_bytes(<[u8; 4]>::from_hex(line[5]).map_err(|_| { - Error::new(ErrorKind::InvalidData, "Unable to parse metric") - })?), - mask: Ipv4Addr::from(u32::from_le_bytes( - <[u8; 4]>::from_hex(line[0]).map_err(|_| { - Error::new(ErrorKind::InvalidData, "Unable to parse mask") - })?, - )), - mtu: u32::from_be_bytes( - <[u8; 4]>::from_hex(line[5]).map_err(|_| { - Error::new(ErrorKind::InvalidData, "Unable to parse mtu") - })?, - ), - window: u32::from_be_bytes(<[u8; 4]>::from_hex(line[5]).map_err(|_| { - Error::new(ErrorKind::InvalidData, "Unable to parse window") - })?), - irtt: u32::from_be_bytes( - <[u8; 4]>::from_hex(line[5]).map_err(|_| { - Error::new(ErrorKind::InvalidData, "Unable to parse irtt") - })?, - ), - }; - - route.push(entry) - } - - Ok(route) - } - - pub fn new() -> Result { - // Read /proc/net/ipv6_route - let proc_net_ipv6_route = Self::parse_proc_net_ipv6_route().unwrap_or_default(); - // Read /proc/net/route - let proc_net_route = Self::parse_proc_net_route().unwrap_or_default(); - - trace!("proc_net_ipv6_route: {:#?}", proc_net_ipv6_route); - trace!("proc_net_route: {:#?}", proc_net_route); - - // At least one routing table must be available - if proc_net_ipv6_route.is_empty() && proc_net_route.is_empty() { - return Err(Error::new( - ErrorKind::InvalidData, - "No routing tables available", - )); - } - Ok(Self { - proc_net_ipv6_route, - proc_net_route, - }) - } - - pub fn has_default_route(&self, name: &str) -> bool { - for e in &self.proc_net_ipv6_route { - if e.intf_name == name && e.dest_prefix == 0u8 { - return true; - } - } - for e in &self.proc_net_route { - if e.iface == name && e.mask == Ipv4Addr::new(0, 0, 0, 0) { - return true; - } - } - false - } - - pub fn get_address_flags(&self, _addr: &IfAddr) -> AddressFlags { - AddressFlags { - is_temporary: false, - is_dynamic: false, - is_deprecated: false, - } - } -} diff --git a/veilid-core/src/intf/native/utils/network_interfaces/mod.rs b/veilid-core/src/intf/native/utils/network_interfaces/mod.rs index c1ce3541..29dc2256 100644 --- a/veilid-core/src/intf/native/utils/network_interfaces/mod.rs +++ b/veilid-core/src/intf/native/utils/network_interfaces/mod.rs @@ -1,7 +1,6 @@ use crate::xx::*; use crate::*; use core::fmt; -mod tools; cfg_if::cfg_if! { if #[cfg(any(target_os = "linux", target_os = "android"))] { @@ -12,6 +11,7 @@ cfg_if::cfg_if! { use windows::PlatformSupportWindows as PlatformSupport; } else if #[cfg(any(target_os = "macos", target_os = "ios"))] { mod apple; + mod sockaddr_tools; use apple::PlatformSupportApple as PlatformSupport; } else { compile_error!("No network interfaces support for this platform!"); diff --git a/veilid-core/src/intf/native/utils/network_interfaces/sockaddr_tools.rs b/veilid-core/src/intf/native/utils/network_interfaces/sockaddr_tools.rs new file mode 100644 index 00000000..8f3a5ef7 --- /dev/null +++ b/veilid-core/src/intf/native/utils/network_interfaces/sockaddr_tools.rs @@ -0,0 +1,117 @@ +// Copyright 2018 MaidSafe.net limited. +// +// This SAFE Network Software is licensed to you under the MIT license or the Modified BSD license , at your option. This file may not be copied, +// modified, or distributed except according to those terms. Please review the Licences for the +// specific language governing permissions and limitations relating to use of the SAFE Network +// Software. + +#[cfg(not(windows))] +use libc::{sockaddr, sockaddr_in, sockaddr_in6, AF_INET, AF_INET6}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::ptr::NonNull; +#[cfg(windows)] +use winapi::{ + shared::ws2def::{AF_INET, AF_INET6, SOCKADDR as sockaddr, SOCKADDR_IN as sockaddr_in}, + shared::ws2ipdef::SOCKADDR_IN6 as sockaddr_in6, +}; + +pub fn to_ipaddr(sockaddr: *const sockaddr) -> Option { + if sockaddr.is_null() { + return None; + } + SockAddr::new(sockaddr)?.as_ipaddr() +} +pub enum SockAddrIn { + In(sockaddr_in), + In6(sockaddr_in6), +} + +// Wrapper around a sockaddr pointer. Guaranteed to not be null. +pub struct SockAddr { + inner: NonNull, +} + +impl SockAddr { + #[allow(clippy::new_ret_no_self)] + pub fn new(sockaddr: *const sockaddr) -> Option { + NonNull::new(sockaddr as *mut _).map(|inner| Self { inner }) + } + + #[cfg(not(windows))] + pub fn as_ipaddr(&self) -> Option { + match self.sockaddr_in() { + Some(SockAddrIn::In(sa)) => Some(IpAddr::V4(Ipv4Addr::new( + ((sa.sin_addr.s_addr) & 255) as u8, + ((sa.sin_addr.s_addr >> 8) & 255) as u8, + ((sa.sin_addr.s_addr >> 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))) + } + None => None, + } + } + + #[cfg(windows)] + pub fn as_ipaddr(&self) -> Option { + 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 >> 0) & 255u32) as u8, + ((s_addr >> 8) & 255u32) as u8, + ((s_addr >> 16) & 255u32) as u8, + ((s_addr >> 24) & 255u32) as u8, + ))) + } + 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.clone()))) + } + None => None, + } + } + + pub fn sockaddr_in(&self) -> Option { + const AF_INET_U32: u32 = AF_INET as u32; + const AF_INET6_U32: u32 = AF_INET6 as u32; + + match self.sa_family() { + AF_INET_U32 => Some(SockAddrIn::In(self.sa_in())), + AF_INET6_U32 => Some(SockAddrIn::In6(self.sa_in6())), + _ => None, + } + } + + #[allow(unsafe_code)] + pub fn sa_family(&self) -> u32 { + unsafe { u32::from(self.inner.as_ref().sa_family) } + } + + #[allow(unsafe_code)] + #[allow(clippy::cast_ptr_alignment)] + pub fn sa_in(&self) -> sockaddr_in { + unsafe { *(self.inner.as_ptr() as *const sockaddr_in) } + } + + #[allow(unsafe_code)] + #[allow(clippy::cast_ptr_alignment)] + pub fn sa_in6(&self) -> sockaddr_in6 { + unsafe { *(self.inner.as_ptr() as *const sockaddr_in6) } + } +} diff --git a/veilid-core/src/tests/ios/veilidcore-tests/veilidcore-tests.xcodeproj/project.pbxproj b/veilid-core/src/tests/ios/veilidcore-tests/veilidcore-tests.xcodeproj/project.pbxproj index 1fdb7816..9467bfd8 100644 --- a/veilid-core/src/tests/ios/veilidcore-tests/veilidcore-tests.xcodeproj/project.pbxproj +++ b/veilid-core/src/tests/ios/veilidcore-tests/veilidcore-tests.xcodeproj/project.pbxproj @@ -18,8 +18,8 @@ /* Begin PBXFileReference section */ 4317C6BA2694A675009C717F /* veilidcore-tests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "veilidcore-tests-Bridging-Header.h"; sourceTree = ""; }; - 4317C6BB2694A676009C717F /* veilid-core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = veilid-core.h; sourceTree = ""; }; - 4317C6BC2694A676009C717F /* veilid-core.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = veilid-core.c; sourceTree = ""; }; + 4317C6BB2694A676009C717F /* veilid-core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "veilid-core.h"; sourceTree = ""; }; + 4317C6BC2694A676009C717F /* veilid-core.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = "veilid-core.c"; sourceTree = ""; }; 43C436AC268904AC002D11C5 /* veilidcore-tests.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "veilidcore-tests.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 43C436AF268904AC002D11C5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 43C436B1268904AC002D11C5 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -339,11 +339,11 @@ ); OTHER_LDFLAGS = ""; "OTHER_LDFLAGS[sdk=iphoneos*]" = ( - "-L../../../../target/aarch64-apple-ios/debug", + "-L../../../../../target/aarch64-apple-ios/debug", "-lveilid_core", ); "OTHER_LDFLAGS[sdk=iphonesimulator*]" = ( - "-L../../../../target/x86_64-apple-ios/debug", + "-L../../../../../target/x86_64-apple-ios/debug", "-lveilid_core", ); PRODUCT_BUNDLE_IDENTIFIER = "com.veilid.veilidcore-tests"; @@ -371,11 +371,11 @@ ); OTHER_LDFLAGS = ""; "OTHER_LDFLAGS[sdk=iphoneos*]" = ( - "-L../../../../target/aarch64-apple-ios/release", + "-L../../../../../target/aarch64-apple-ios/release", "-lveilid_core", ); "OTHER_LDFLAGS[sdk=iphonesimulator*]" = ( - "-L../../../../target/x86_64-apple-ios/release", + "-L../../../../../target/x86_64-apple-ios/release", "-lveilid_core", ); PRODUCT_BUNDLE_IDENTIFIER = "com.veilid.veilidcore-tests";