diff options
| author | David Lönnhager <david.l@mullvad.net> | 2023-09-11 20:27:09 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2023-09-19 17:37:25 +0200 |
| commit | 02764a83ab2c266bd0bca3c78e433d5f4d1d7f38 (patch) | |
| tree | e8bc91e8cbd0d28bac75f9e2eda0509d1c72e667 | |
| parent | c4af21a6c08a9816b98400e5cf11f0c6f9b4adbd (diff) | |
| download | mullvadvpn-02764a83ab2c266bd0bca3c78e433d5f4d1d7f38.tar.xz mullvadvpn-02764a83ab2c266bd0bca3c78e433d5f4d1d7f38.zip | |
Ignore default routes whose interface are inactive. Stale scoped routes fooled the offline detection into thinking that there were working routes
| -rw-r--r-- | talpid-routing/src/unix/macos/interface.rs | 47 |
1 files changed, 43 insertions, 4 deletions
diff --git a/talpid-routing/src/unix/macos/interface.rs b/talpid-routing/src/unix/macos/interface.rs index 5f08fa929f..bf08317e74 100644 --- a/talpid-routing/src/unix/macos/interface.rs +++ b/talpid-routing/src/unix/macos/interface.rs @@ -1,5 +1,9 @@ -use nix::net::if_::if_nametoindex; -use std::ffi::CString; +use nix::net::if_::{if_nametoindex, InterfaceFlags}; +use std::{ + ffi::CString, + io, + net::{Ipv4Addr, Ipv6Addr}, +}; use system_configuration::{ core_foundation::string::CFString, network_configuration::{SCNetworkService, SCNetworkSet}, @@ -48,10 +52,12 @@ pub async fn get_best_default_route( } }; - // Request ifscoped route for this interface + // Request ifscoped default route for this interface let route_msg = msg.clone().set_ifscope(u16::try_from(index).unwrap()); if let Ok(Some(route)) = routing_table.get_route(&route_msg).await { - return Some(route); + if is_active_interface(&iface, family).unwrap_or(true) { + return Some(route); + } } } @@ -75,3 +81,36 @@ fn network_service_order() -> Vec<String> { }) .collect::<Vec<_>>() } + +/// Return whether the given interface has an assigned (unicast) IP address. +fn is_active_interface(interface_name: &str, family: Family) -> io::Result<bool> { + let required_link_flags: InterfaceFlags = InterfaceFlags::IFF_UP | InterfaceFlags::IFF_RUNNING; + let has_ip_addr = nix::ifaddrs::getifaddrs()? + .filter(|addr| (addr.flags & required_link_flags) == required_link_flags) + .filter(|addr| addr.interface_name == interface_name) + .any(|addr| { + if let Some(addr) = addr.address { + // Check if family matches; ignore if link-local address + match family { + Family::V4 => matches!(addr.as_sockaddr_in(), Some(addr_in) if is_routable_v4(&Ipv4Addr::from(addr_in.ip()))), + Family::V6 => { + matches!(addr.as_sockaddr_in6(), Some(addr_in) if is_routable_v6(&addr_in.ip())) + } + } + } else { + false + } + }); + Ok(has_ip_addr) +} + +fn is_routable_v4(addr: &Ipv4Addr) -> bool { + !addr.is_unspecified() && !addr.is_loopback() && !addr.is_link_local() +} + +fn is_routable_v6(addr: &Ipv6Addr) -> bool { + !addr.is_unspecified() + && !addr.is_loopback() + // !(link local) + && (addr.segments()[0] & 0xffc0) != 0xfe80 +} |
