summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--talpid-core/src/tunnel/openvpn/mod.rs69
-rw-r--r--talpid-core/src/tunnel/openvpn/windows.rs25
-rw-r--r--talpid-core/src/tunnel/windows.rs102
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)
+}