diff options
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/openvpn/mod.rs | 69 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/openvpn/windows.rs | 25 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/windows.rs | 102 |
4 files changed, 137 insertions, 60 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index ae614d1461..8d35cdebe1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ Line wrap the file at 100 chars. Th #### Windows - Upgrade Wintun from 0.10.4 to 0.13. +- Reduce tunnel setup time for OpenVPN by disabling DAD. ### Fixed - Fix link to download page not always using the beta URL when it should. diff --git a/talpid-core/src/tunnel/openvpn/mod.rs b/talpid-core/src/tunnel/openvpn/mod.rs index 8ae967975a..9eaa25875f 100644 --- a/talpid-core/src/tunnel/openvpn/mod.rs +++ b/talpid-core/src/tunnel/openvpn/mod.rs @@ -15,11 +15,7 @@ use lazy_static::lazy_static; #[cfg(target_os = "linux")] use std::collections::{HashMap, HashSet}; #[cfg(windows)] -use std::{ - ffi::{OsStr, OsString}, - os::windows::ffi::OsStrExt, - time::Instant, -}; +use std::{ffi::OsString, time::Instant}; use std::{ fs, io::{self, Write}, @@ -42,13 +38,9 @@ use widestring::U16CString; use winapi::shared::{ guiddef::GUID, ifdef::NET_LUID, - netioapi::{ - ConvertInterfaceAliasToLuid, FreeMibTable, GetUnicastIpAddressEntry, - GetUnicastIpAddressTable, MIB_UNICASTIPADDRESS_ROW, MIB_UNICASTIPADDRESS_TABLE, - }, + netioapi::{GetUnicastIpAddressEntry, MIB_UNICASTIPADDRESS_ROW}, nldef::{IpDadStatePreferred, IpDadStateTentative, NL_DAD_STATE}, winerror::NO_ERROR, - ws2def::AF_UNSPEC, }; #[cfg(windows)] use winreg::enums::{KEY_READ, KEY_WRITE}; @@ -282,6 +274,7 @@ trait WintunContext: Send + Sync { fn luid(&self) -> NET_LUID; fn ipv6(&self) -> bool; async fn wait_for_interfaces(&self) -> io::Result<()>; + fn disable_unused_features(&self) {} } #[cfg(windows)] @@ -319,6 +312,10 @@ impl WintunContext for WintunContextImpl { let luid = self.adapter.adapter().luid(); super::windows::wait_for_interfaces(luid, true, self.wait_v6_interface).await } + + fn disable_unused_features(&self) { + self.adapter.adapter().try_disable_unused_features(); + } } @@ -587,6 +584,7 @@ impl<C: OpenVpnBuilder + Send + 'static> OpenVpnMonitor<C> { { log::debug!("Wait for IP interfaces"); wintun.wait_for_interfaces().await?; + wintun.disable_unused_features(); } cmd.start() } @@ -1241,44 +1239,17 @@ mod event_server { #[cfg(windows)] fn wait_for_ready_device(alias: &str) -> Result<()> { // Obtain luid for alias - let alias_wide: Vec<u16> = OsStr::new(alias) - .encode_wide() - .chain(std::iter::once(0u16)) - .collect(); - - let mut luid: NET_LUID = unsafe { std::mem::zeroed() }; - let status = unsafe { ConvertInterfaceAliasToLuid(alias_wide.as_ptr(), &mut luid) }; - if status != NO_ERROR { - return Err(Error::NoDeviceLuid(io::Error::last_os_error())); - } + let luid = crate::tunnel::windows::luid_from_alias(alias).map_err(Error::NoDeviceLuid)?; // Obtain unicast IP addresses - let mut unicast_rows = vec![]; - - unsafe { - let mut unicast_table: *mut MIB_UNICASTIPADDRESS_TABLE = std::ptr::null_mut(); - - let status = GetUnicastIpAddressTable(AF_UNSPEC as u16, &mut unicast_table); - if status != NO_ERROR { - return Err(Error::ObtainUnicastAddress(io::Error::last_os_error())); - } - - if (*unicast_table).NumEntries == 0 { - FreeMibTable(unicast_table as *mut _); - return Err(Error::NoUnicastAddress); - } - - let first_row = &(*unicast_table).Table[0] as *const MIB_UNICASTIPADDRESS_ROW; - - for i in 0..(*unicast_table).NumEntries { - let row = first_row.offset(i as isize); - if (*row).InterfaceLuid.Value != luid.Value { - continue; - } - unicast_rows.push(*row); - } - - FreeMibTable(unicast_table as *mut _); + let mut unicast_rows: Vec<MIB_UNICASTIPADDRESS_ROW> = + crate::tunnel::windows::get_unicast_table(None) + .map_err(Error::ObtainUnicastAddress)? + .into_iter() + .filter(|row| row.InterfaceLuid.Value == luid.Value) + .collect(); + if unicast_rows.is_empty() { + return Err(Error::NoUnicastAddress); } // Poll DAD status using GetUnicastIpAddressEntry @@ -1289,9 +1260,11 @@ fn wait_for_ready_device(alias: &str) -> Result<()> { let mut ready = true; for row in &mut unicast_rows { - let status = unsafe { GetUnicastIpAddressEntry(row as *mut _) }; + let status = unsafe { GetUnicastIpAddressEntry(row) }; if status != NO_ERROR { - return Err(Error::ObtainUnicastAddress(io::Error::last_os_error())); + return Err(Error::ObtainUnicastAddress(io::Error::from_raw_os_error( + status as i32, + ))); } if row.DadState == IpDadStateTentative { ready = false; diff --git a/talpid-core/src/tunnel/openvpn/windows.rs b/talpid-core/src/tunnel/openvpn/windows.rs index 9b907e101a..6e49bb1f0d 100644 --- a/talpid-core/src/tunnel/openvpn/windows.rs +++ b/talpid-core/src/tunnel/openvpn/windows.rs @@ -1,3 +1,4 @@ +use crate::tunnel::windows::{get_ip_interface_entry, set_ip_interface_entry, AddressFamily}; use std::{ ffi::CStr, fmt, io, iter, mem, @@ -14,6 +15,8 @@ use winapi::{ ifdef::NET_LUID, minwindef::{BOOL, FARPROC, HINSTANCE, HMODULE}, netioapi::ConvertInterfaceLuidToGuid, + nldef::RouterDiscoveryDisabled, + ntdef::FALSE, winerror::NO_ERROR, }, um::{ @@ -154,6 +157,28 @@ impl WintunAdapter { Ok((Self { dll_handle, handle }, restart_required)) } + pub fn try_disable_unused_features(&self) { + // Disable DAD, DHCP, and router discovery + let luid = self.luid(); + for family in &[AddressFamily::Ipv4, AddressFamily::Ipv6] { + if let Ok(mut row) = get_ip_interface_entry(*family, &luid) { + row.SitePrefixLength = 0; + row.RouterDiscoveryBehavior = RouterDiscoveryDisabled; + row.DadTransmits = 0; + row.ManagedAddressConfigurationSupported = FALSE; + row.OtherStatefulConfigurationSupported = FALSE; + + if let Err(error) = set_ip_interface_entry(&row) { + log::error!( + "{} (family: {})", + error.display_chain_with_msg("Failed to update Wintun interface"), + family, + ); + } + } + } + } + pub fn delete(self, force_close_sessions: bool) -> io::Result<RebootRequired> { unsafe { self.dll_handle diff --git a/talpid-core/src/tunnel/windows.rs b/talpid-core/src/tunnel/windows.rs index 2d4fcf85e5..ea7cb646b7 100644 --- a/talpid-core/src/tunnel/windows.rs +++ b/talpid-core/src/tunnel/windows.rs @@ -1,15 +1,37 @@ -use std::{io, mem, os::windows::io::RawHandle, sync::Mutex}; +use std::{ + ffi::OsStr, + fmt, io, mem, + os::windows::{ffi::OsStrExt, io::RawHandle}, + sync::Mutex, +}; use winapi::shared::{ ifdef::NET_LUID, netioapi::{ - CancelMibChangeNotify2, GetIpInterfaceEntry, MibAddInstance, NotifyIpInterfaceChange, - MIB_IPINTERFACE_ROW, + CancelMibChangeNotify2, ConvertInterfaceAliasToLuid, FreeMibTable, GetIpInterfaceEntry, + GetUnicastIpAddressTable, MibAddInstance, NotifyIpInterfaceChange, SetIpInterfaceEntry, + MIB_IPINTERFACE_ROW, MIB_UNICASTIPADDRESS_ROW, MIB_UNICASTIPADDRESS_TABLE, }, ntdef::FALSE, winerror::{ERROR_NOT_FOUND, NO_ERROR}, ws2def::{AF_INET, AF_INET6, AF_UNSPEC}, }; +/// Address family. These correspond to the `AF_*` constants. +#[derive(Debug, Clone, Copy)] +pub enum AddressFamily { + Ipv4 = AF_INET as isize, + Ipv6 = AF_INET6 as isize, +} + +impl fmt::Display for AddressFamily { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + AddressFamily::Ipv4 => write!(f, "IPv4 (AF_INET)"), + AddressFamily::Ipv6 => write!(f, "IPv6 (AF_INET6)"), + } + } +} + /// Context for [`notify_ip_interface_change`]. When it is dropped, /// the callback is unregistered. pub struct IpNotifierHandle<'a> { @@ -41,7 +63,7 @@ unsafe extern "system" fn inner_callback( /// or changed. pub fn notify_ip_interface_change<'a, T: FnMut(&MIB_IPINTERFACE_ROW, u32) + Send + 'a>( callback: T, - family: u16, + family: Option<AddressFamily>, ) -> io::Result<Box<IpNotifierHandle<'a>>> { let mut context = Box::new(IpNotifierHandle { callback: Mutex::new(Box::new(callback)), @@ -50,7 +72,7 @@ pub fn notify_ip_interface_change<'a, T: FnMut(&MIB_IPINTERFACE_ROW, u32) + Send let status = unsafe { NotifyIpInterfaceChange( - family, + af_family_from_family(family), Some(inner_callback), &mut *context as *mut _ as *mut _, FALSE, @@ -66,12 +88,15 @@ pub fn notify_ip_interface_change<'a, T: FnMut(&MIB_IPINTERFACE_ROW, u32) + Send } /// Returns information about a network IP interface. -pub fn get_ip_interface_entry(family: u16, luid: &NET_LUID) -> io::Result<MIB_IPINTERFACE_ROW> { +pub fn get_ip_interface_entry( + family: AddressFamily, + luid: &NET_LUID, +) -> io::Result<MIB_IPINTERFACE_ROW> { let mut row: MIB_IPINTERFACE_ROW = unsafe { mem::zeroed() }; - row.Family = family; + row.Family = family as u16; row.InterfaceLuid = *luid; - let result = unsafe { GetIpInterfaceEntry(&mut row as *mut _) }; + let result = unsafe { GetIpInterfaceEntry(&mut row) }; if result == NO_ERROR { Ok(row) } else { @@ -79,7 +104,17 @@ pub fn get_ip_interface_entry(family: u16, luid: &NET_LUID) -> io::Result<MIB_IP } } -fn ip_interface_entry_exists(family: u16, luid: &NET_LUID) -> io::Result<bool> { +/// Set the properties of an IP interface. +pub fn set_ip_interface_entry(row: &MIB_IPINTERFACE_ROW) -> io::Result<()> { + let result = unsafe { SetIpInterfaceEntry(row as *const _ as *mut _) }; + if result == NO_ERROR { + Ok(()) + } else { + Err(io::Error::from_raw_os_error(result as i32)) + } +} + +fn ip_interface_entry_exists(family: AddressFamily, luid: &NET_LUID) -> io::Result<bool> { match get_ip_interface_entry(family, luid) { Ok(_) => Ok(true), Err(error) if error.raw_os_error() == Some(ERROR_NOT_FOUND as i32) => Ok(false), @@ -118,12 +153,12 @@ pub async fn wait_for_interfaces(luid: NET_LUID, ipv4: bool, ipv6: bool) -> io:: } } }, - AF_UNSPEC as u16, + None, )?; // Make sure they don't already exist - if (!ipv4 || ip_interface_entry_exists(AF_INET as u16, &luid)?) - && (!ipv6 || ip_interface_entry_exists(AF_INET6 as u16, &luid)?) + if (!ipv4 || ip_interface_entry_exists(AddressFamily::Ipv4, &luid)?) + && (!ipv6 || ip_interface_entry_exists(AddressFamily::Ipv6, &luid)?) { return Ok(()); } @@ -131,3 +166,46 @@ pub async fn wait_for_interfaces(luid: NET_LUID, ipv4: bool, ipv6: bool) -> io:: let _ = rx.await; Ok(()) } + +/// Returns the unicast IP address table. If `family` is `None`, then addresses for all families are +/// returned. +pub fn get_unicast_table( + family: Option<AddressFamily>, +) -> io::Result<Vec<MIB_UNICASTIPADDRESS_ROW>> { + let mut unicast_rows = vec![]; + let mut unicast_table: *mut MIB_UNICASTIPADDRESS_TABLE = std::ptr::null_mut(); + + let status = + unsafe { GetUnicastIpAddressTable(af_family_from_family(family), &mut unicast_table) }; + if status != NO_ERROR { + return Err(io::Error::from_raw_os_error(status as i32)); + } + let first_row = unsafe { &(*unicast_table).Table[0] } as *const MIB_UNICASTIPADDRESS_ROW; + for i in 0..unsafe { *unicast_table }.NumEntries { + unicast_rows.push(unsafe { *(first_row.offset(i as isize)) }); + } + unsafe { FreeMibTable(unicast_table as *mut _) }; + + Ok(unicast_rows) +} + +/// Returns the LUID of an interface given its alias. +pub fn luid_from_alias<T: AsRef<OsStr>>(alias: T) -> io::Result<NET_LUID> { + let alias_wide: Vec<u16> = alias + .as_ref() + .encode_wide() + .chain(std::iter::once(0u16)) + .collect(); + let mut luid: NET_LUID = unsafe { std::mem::zeroed() }; + let status = unsafe { ConvertInterfaceAliasToLuid(alias_wide.as_ptr(), &mut luid) }; + if status != NO_ERROR { + return Err(io::Error::from_raw_os_error(status as i32)); + } + Ok(luid) +} + +fn af_family_from_family(family: Option<AddressFamily>) -> u16 { + family + .map(|family| family as u16) + .unwrap_or(AF_UNSPEC as u16) +} |
