From ebd36d82ef6aa3cf6eddb062ec78cd17c275748b Mon Sep 17 00:00:00 2001 From: Christien Rioux Date: Thu, 12 Oct 2023 21:17:47 -0400 Subject: [PATCH] network interface changes cleanup --- .../intf/native/network_interfaces/apple.rs | 59 +++++++++++++------ .../src/intf/native/network_interfaces/mod.rs | 17 ++++-- .../intf/native/network_interfaces/netlink.rs | 10 +--- .../network_interfaces/sockaddr_tools.rs | 16 +---- .../intf/native/network_interfaces/windows.rs | 13 ++-- .../native/discovery_context.rs | 16 +++-- veilid-core/src/network_manager/native/mod.rs | 14 ++--- .../native/network_class_discovery.rs | 8 +-- 8 files changed, 84 insertions(+), 69 deletions(-) diff --git a/veilid-core/src/intf/native/network_interfaces/apple.rs b/veilid-core/src/intf/native/network_interfaces/apple.rs index 88422f60..17e7bb6d 100644 --- a/veilid-core/src/intf/native/network_interfaces/apple.rs +++ b/veilid-core/src/intf/native/network_interfaces/apple.rs @@ -3,9 +3,9 @@ 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 +13,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 +201,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 { @@ -368,37 +374,59 @@ impl PlatformSupportApple { 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), }) } fn get_address_flags(ifname: &str, addr: sockaddr_in6) -> EyreResult { - 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 mut req = in6_ifreq::from_name(ifname).unwrap(); + req.set_addr(addr); + let res = unsafe { ioctl(sock, SIOCGIFAFLAG_IN6, &mut req) }; - unsafe { close(sock) }; if res < 0 { + unsafe { close(sock) }; bail!( "SIOCGIFAFLAG_IN6 failed with error on device '{}': {:?}", ifname, 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 { + bail!( + "SIOCGIFALIFETIME_IN6 failed with error on device '{}': {:?}", + ifname, + 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, }) } @@ -408,11 +436,6 @@ impl PlatformSupportApple { ) -> 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"); - } - // Ask for all the addresses we have let ifaddrs = IfAddrs::new().wrap_err("failed to get interface addresses")?; for ifaddr in ifaddrs.iter() { diff --git a/veilid-core/src/intf/native/network_interfaces/mod.rs b/veilid-core/src/intf/native/network_interfaces/mod.rs index 4c53cab9..5f4581cb 100644 --- a/veilid-core/src/intf/native/network_interfaces/mod.rs +++ b/veilid-core/src/intf/native/network_interfaces/mod.rs @@ -74,6 +74,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 +262,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 } @@ -363,9 +368,9 @@ impl NetworkInterfaces { // See if our best addresses have changed if old_best_addresses != inner.interface_address_cache { - trace!( - "Network interface addresses changed: {:?}", - inner.interface_address_cache + debug!( + "Network interface addresses changed: \nFrom: {:?}\n To: {:?}\n", + old_best_addresses, inner.interface_address_cache ); return Ok(true); } @@ -391,7 +396,11 @@ impl NetworkInterfaces { // 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-core/src/intf/native/network_interfaces/netlink.rs index e1f0c750..eb0eb188 100644 --- a/veilid-core/src/intf/native/network_interfaces/netlink.rs +++ b/veilid-core/src/intf/native/network_interfaces/netlink.rs @@ -4,8 +4,8 @@ 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, @@ -138,6 +138,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), }) } @@ -248,11 +249,6 @@ impl PlatformSupportNetlink { // 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"); - } - // Ask for all the addresses we have let mut names = BTreeMap::::new(); let mut addresses = self.handle.as_ref().unwrap().address().get().execute(); diff --git a/veilid-core/src/intf/native/network_interfaces/sockaddr_tools.rs b/veilid-core/src/intf/native/network_interfaces/sockaddr_tools.rs index 605c6c32..91451714 100644 --- a/veilid-core/src/intf/native/network_interfaces/sockaddr_tools.rs +++ b/veilid-core/src/intf/native/network_interfaces/sockaddr_tools.rs @@ -48,13 +48,7 @@ impl SockAddr { ((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))) - } + Some(SockAddrIn::In6(sa)) => Some(IpAddr::V6(Ipv6Addr::from(sa.sin6_addr.s6_addr))), None => None, } } @@ -64,10 +58,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 +67,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/windows.rs b/veilid-core/src/intf/native/network_interfaces/windows.rs index 53b74ba2..0e6f20c5 100644 --- a/veilid-core/src/intf/native/network_interfaces/windows.rs +++ b/veilid-core/src/intf/native/network_interfaces/windows.rs @@ -13,7 +13,7 @@ use libc::{self, c_ulong, c_void, size_t}; use std::ffi::CStr; use std::{io, ptr}; use winapi::shared::ifdef::IfOperStatusUp; -use winapi::shared::ipifcons::IF_TYPE_SOFTWARE_LOOPBACK; +use winapi::shared::ipifcons::{IF_TYPE_SOFTWARE_LOOPBACK, IF_TYPE_TUNNEL}; use winapi::shared::nldef::{ IpDadStatePreferred, IpPrefixOriginDhcp, IpSuffixOriginDhcp, IpSuffixOriginRandom, }; @@ -36,6 +36,7 @@ impl PlatformSupportWindows { 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(), } } @@ -55,13 +56,6 @@ impl PlatformSupportWindows { &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()); - // } - // Iterate all the interfaces let windows_interfaces = WindowsInterfaces::new().wrap_err("failed to get windows interfaces")?; @@ -224,6 +218,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-core/src/network_manager/native/discovery_context.rs b/veilid-core/src/network_manager/native/discovery_context.rs index d7b2376e..e92ef48e 100644 --- a/veilid-core/src/network_manager/native/discovery_context.rs +++ b/veilid-core/src/network_manager/native/discovery_context.rs @@ -658,12 +658,20 @@ impl DiscoveryContext { return; } - // Did external addresses change from the last time we made dialinfo? + // 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; - let ext_2 = inner.external_2.as_ref().unwrap().address; - if (ext_1 != ext_2) || Some(ext_1) != self.unlocked_inner.existing_external_address { + 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 { diff --git a/veilid-core/src/network_manager/native/mod.rs b/veilid-core/src/network_manager/native/mod.rs index 2cca19ea..618a5b91 100644 --- a/veilid-core/src/network_manager/native/mod.rs +++ b/veilid-core/src/network_manager/native/mod.rs @@ -375,15 +375,14 @@ 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? { return Ok(false); } - self.inner.lock().network_needs_restart = true; + self.inner.lock().needs_public_dial_info_check = true; + Ok(true) } @@ -700,7 +699,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 @@ -978,9 +977,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 44fe04b5..e0bdf9ec 100644 --- a/veilid-core/src/network_manager/native/network_class_discovery.rs +++ b/veilid-core/src/network_manager/native/network_class_discovery.rs @@ -50,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 {