summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2020-04-24 16:52:30 +0200
committerDavid Lönnhager <david.l@mullvad.net>2020-04-24 16:52:30 +0200
commitb41a0b2225411698955b6f0243c0a47eaeea0a02 (patch)
tree1b871fc85ad18cf9786509bfa392447bfaf3815e
parent874c98fa9e8103696c3f74068441bd0f71b7997e (diff)
parent6419301639d5a87cea0c50aa13c9eff4e522e862 (diff)
downloadmullvadvpn-b41a0b2225411698955b6f0243c0a47eaeea0a02.tar.xz
mullvadvpn-b41a0b2225411698955b6f0243c0a47eaeea0a02.zip
Merge branch 'fix-ipv6-issues'
-rw-r--r--CHANGELOG.md4
-rw-r--r--talpid-core/src/tunnel/mod.rs69
-rw-r--r--talpid-core/src/winnet.rs91
-rw-r--r--windows/winnet/src/winnet/netconfig.cpp269
-rw-r--r--windows/winnet/src/winnet/netconfig.h5
-rw-r--r--windows/winnet/src/winnet/winnet.cpp88
-rw-r--r--windows/winnet/src/winnet/winnet.def3
-rw-r--r--windows/winnet/src/winnet/winnet.h23
-rw-r--r--windows/winnet/src/winnet/winnet.vcxproj2
-rw-r--r--windows/winnet/src/winnet/winnet.vcxproj.filters2
10 files changed, 476 insertions, 80 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f5160a390f..b66b2e3bbf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -29,12 +29,16 @@ Line wrap the file at 100 chars. Th
### Changed
- Downgrade to Electron 7 due to issues with tray icon in Electron 8.
+#### Windows
+- When required, attempt to enable IPv6 for network adapters instead of failing.
+
### Fixed
- Enable IPv6 in WireGuard regardless of the specified MTU value, previously IPv6 was disabled if
the MTU was below 1380.
#### Windows
- Improve offline detection logic.
+- Enable missing IPv6 interface on the WireGuard TUN adapter when it has been disabled.
#### Android
- Change button colors on problem report no email confirmation dialog to match the desktop version.
diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs
index 22da520ce0..6881b02311 100644
--- a/talpid-core/src/tunnel/mod.rs
+++ b/talpid-core/src/tunnel/mod.rs
@@ -10,6 +10,8 @@ use std::{
#[cfg(not(target_os = "android"))]
use talpid_types::net::openvpn as openvpn_types;
use talpid_types::net::{wireguard as wireguard_types, TunnelParameters};
+#[cfg(target_os = "windows")]
+use talpid_types::ErrorExt;
/// A module for all OpenVPN related tunnel management.
#[cfg(not(target_os = "android"))]
@@ -207,21 +209,38 @@ impl TunnelMonitor {
fn ensure_ipv6_can_be_used_if_enabled(tunnel_parameters: &TunnelParameters) -> Result<()> {
let options = tunnel_parameters.get_generic_options();
- if options.enable_ipv6 {
- #[cfg(target_os = "windows")]
- let enabled = match tunnel_parameters {
- TunnelParameters::OpenVpn(_) => is_ipv6_enabled_in_os(true),
- _ => is_ipv6_enabled_in_os(false),
- }?;
- #[cfg(not(target_os = "windows"))]
- let enabled = is_ipv6_enabled_in_os()?;
- if enabled {
+
+ #[cfg(target_os = "windows")]
+ match tunnel_parameters {
+ TunnelParameters::OpenVpn(..) => {
+ if options.enable_ipv6 {
+ try_enabling_ipv6(tunnel_parameters)
+ } else {
+ Ok(())
+ }
+ }
+ TunnelParameters::Wireguard(..) => {
+ // WireGuard always waits on an IPv6 interface,
+ // even if it's not in use
+ if let Err(e) = try_enabling_ipv6(tunnel_parameters) {
+ log::error!("{}", e.display_chain_with_msg("Failed to enable IPv6"));
+ }
Ok(())
+ }
+ }
+
+ #[cfg(not(target_os = "windows"))]
+ {
+ if options.enable_ipv6 {
+ let enabled = is_ipv6_enabled_in_os()?;
+ if enabled {
+ Ok(())
+ } else {
+ Err(Error::EnableIpv6Error)
+ }
} else {
- Err(Error::EnableIpv6Error)
+ Ok(())
}
- } else {
- Ok(())
}
}
@@ -327,7 +346,7 @@ impl InternalTunnelMonitor {
#[cfg(target_os = "windows")]
-fn is_ipv6_enabled_in_os(check_tap: bool) -> Result<bool> {
+fn try_enabling_ipv6(tunnel_parameters: &TunnelParameters) -> Result<()> {
use winreg::{enums::*, RegKey};
const IPV6_DISABLED_ON_TUNNELS_MASK: u32 = 0x01;
@@ -341,16 +360,24 @@ fn is_ipv6_enabled_in_os(check_tap: bool) -> Result<bool> {
.unwrap_or(true);
if !globally_enabled {
- log::debug!("IPv6 disabled in tunnel interfaces");
+ // TODO: Try to globally enable IPv6
+ log::debug!("IPv6 disabled in all tunnel interfaces");
+ return Err(Error::EnableIpv6Error);
}
- if check_tap {
- let enabled_on_tap =
- crate::winnet::get_tap_interface_ipv6_status().map_err(Error::WinnetError)?;
- Ok(globally_enabled && enabled_on_tap)
- } else {
- Ok(globally_enabled)
- }
+ let guid_string: String;
+
+ let guid = match tunnel_parameters {
+ TunnelParameters::OpenVpn(..) => {
+ let alias = crate::winnet::get_tap_interface_alias().map_err(Error::WinnetError)?;
+ guid_string =
+ crate::winnet::interface_alias_to_guid(&alias).map_err(Error::WinnetError)?;
+ &guid_string
+ }
+ TunnelParameters::Wireguard(..) => "{AFE43773-E1F8-4EBB-8536-576AB86AFE9A}",
+ };
+
+ crate::winnet::enable_ipv6_for_adapter(&guid).map_err(Error::WinnetError)
}
#[cfg(not(target_os = "windows"))]
diff --git a/talpid-core/src/winnet.rs b/talpid-core/src/winnet.rs
index 21b5bb3ae7..a0b183d779 100644
--- a/talpid-core/src/winnet.rs
+++ b/talpid-core/src/winnet.rs
@@ -3,7 +3,11 @@ pub use self::api::{WinNet_ActivateConnectivityMonitor, WinNet_DeactivateConnect
use crate::{logging::windows::log_sink, routing::Node};
use ipnetwork::IpNetwork;
use libc::{c_void, wchar_t};
-use std::{ffi::OsString, net::IpAddr, ptr};
+use std::{
+ ffi::{OsStr, OsString},
+ net::IpAddr,
+ ptr,
+};
use widestring::WideCString;
/// Errors that this module may produce.
@@ -17,6 +21,14 @@ pub enum Error {
#[error(display = "Supplied interface alias is invalid")]
InvalidInterfaceAlias(#[error(source)] widestring::NulError<u16>),
+ /// Failed to enable IPv6 on the network interface.
+ #[error(display = "Failed to enable IPv6 on the network interface")]
+ EnableIpv6,
+
+ /// Failed to enable IPv6 on the network interface.
+ #[error(display = "Failed to obtain GUID for the network interface")]
+ GetInterfaceGuid,
+
/// Failed to read IPv6 status on the TAP network interface.
#[error(display = "Failed to read IPv6 status on the TAP network interface")]
GetIpv6Status,
@@ -62,29 +74,23 @@ pub fn ensure_best_metric_for_interface(interface_alias: &str) -> Result<bool, E
}
}
-/// Checks if IPv6 is enabled for the TAP interface
-pub fn get_tap_interface_ipv6_status() -> Result<bool, Error> {
- // WinNet_GetTapInterfaceIpv6Status() will fail if the alias cannot be retrieved.
- // Try to retrieve it first so that we may return a more specific error.
- let _ = get_tap_interface_alias()?;
- let tap_ipv6_status =
- unsafe { WinNet_GetTapInterfaceIpv6Status(Some(log_sink), logging_context()) };
+/// Enables IPv6 for a given interface.
+pub fn enable_ipv6_for_adapter(interface_guid: &str) -> Result<(), Error> {
+ let interface_guid_ws =
+ WideCString::from_str(interface_guid).map_err(Error::InvalidInterfaceAlias)?;
- match tap_ipv6_status {
- // Enabled
- 0 => Ok(true),
- // Disabled
- 1 => Ok(false),
- // Failure
- 2 => Err(Error::GetIpv6Status),
- // Unexpected value
- i => {
- log::error!(
- "Unexpected return code from WinNet_GetTapInterfaceIpv6Status: {}",
- i
- );
- Err(Error::GetIpv6Status)
- }
+ let result = unsafe {
+ WinNet_EnableIpv6ForAdapter(
+ interface_guid_ws.as_ptr(),
+ Some(log_sink),
+ logging_context(),
+ )
+ };
+
+ if result {
+ Ok(())
+ } else {
+ Err(Error::EnableIpv6)
}
}
@@ -105,6 +111,30 @@ pub fn get_tap_interface_alias() -> Result<OsString, Error> {
Ok(alias.to_os_string())
}
+/// Determines the interface guid for a given adapter alias.
+pub fn interface_alias_to_guid(interface_alias: &OsStr) -> Result<String, Error> {
+ let interface_alias =
+ WideCString::from_os_str(interface_alias).map_err(Error::InvalidInterfaceAlias)?;
+ let mut guid_ptr: *mut wchar_t = ptr::null_mut();
+ let status = unsafe {
+ WinNet_InterfaceAliasToGuid(
+ interface_alias.as_ptr(),
+ &mut guid_ptr as *mut _,
+ Some(log_sink),
+ logging_context(),
+ )
+ };
+
+ if !status {
+ return Err(Error::GetInterfaceGuid);
+ }
+
+ let guid = unsafe { WideCString::from_ptr_str(guid_ptr) };
+ unsafe { WinNet_ReleaseString(guid_ptr) };
+
+ Ok(guid.to_string_lossy())
+}
+
#[allow(dead_code)]
#[repr(u32)]
pub enum WinNetAddrFamily {
@@ -380,11 +410,12 @@ mod api {
sink_context: *const u8,
) -> u32;
- #[link_name = "WinNet_GetTapInterfaceIpv6Status"]
- pub fn WinNet_GetTapInterfaceIpv6Status(
+ #[link_name = "WinNet_EnableIpv6ForAdapter"]
+ pub fn WinNet_EnableIpv6ForAdapter(
+ tunnel_interface_alias: *const wchar_t,
sink: Option<LogSink>,
sink_context: *const u8,
- ) -> u32;
+ ) -> bool;
#[link_name = "WinNet_GetTapInterfaceAlias"]
pub fn WinNet_GetTapInterfaceAlias(
@@ -393,6 +424,14 @@ mod api {
sink_context: *const u8,
) -> bool;
+ #[link_name = "WinNet_InterfaceAliasToGuid"]
+ pub fn WinNet_InterfaceAliasToGuid(
+ interface_alias: *const wchar_t,
+ interface_guid: *mut *mut wchar_t,
+ sink: Option<LogSink>,
+ sink_context: *const u8,
+ ) -> bool;
+
#[link_name = "WinNet_ReleaseString"]
pub fn WinNet_ReleaseString(string: *mut wchar_t);
diff --git a/windows/winnet/src/winnet/netconfig.cpp b/windows/winnet/src/winnet/netconfig.cpp
new file mode 100644
index 0000000000..7fd7b38869
--- /dev/null
+++ b/windows/winnet/src/winnet/netconfig.cpp
@@ -0,0 +1,269 @@
+#include "stdafx.h"
+#include "netconfig.h"
+#include <stdexcept>
+#include <sstream>
+#include <windows.h>
+#include <netcfgx.h>
+#include <devguid.h>
+#include <libcommon/registry/registry.h>
+#include <libcommon/error.h>
+#include <libcommon/string.h>
+#include <libcommon/memory.h>
+#include <libshared/network/interfaceutils.h>
+
+
+namespace
+{
+
+const wchar_t NETCFG_LOCK_CLIENT_NAME[] = L"MULLVAD";
+constexpr uint16_t NETCFG_LOCK_TIMEOUT = 5000; // milliseconds
+const wchar_t NETCFG_IPV6_COMPONENT_NAME[] = L"MS_TCPIP6";
+
+void SetIpv6BindingForBindName(INetCfg *netCfg, const std::wstring &bindName, bool enable)
+{
+ INetCfgComponent *transactionComponent = nullptr;
+ HRESULT result = netCfg->FindComponent(NETCFG_IPV6_COMPONENT_NAME, &transactionComponent);
+
+ if (S_OK != result)
+ {
+ THROW_ERROR("Failed to obtain transaction component");
+ }
+
+ INetCfgComponentBindings *bindings = nullptr;
+ result = transactionComponent->QueryInterface(
+ IID_INetCfgComponentBindings,
+ reinterpret_cast<void**>(&bindings)
+ );
+
+ transactionComponent->Release();
+ transactionComponent = nullptr;
+
+ if (S_OK != result)
+ {
+ std::wstringstream ss;
+ ss << L"Failed to obtain component bindings for ";
+ ss << NETCFG_IPV6_COMPONENT_NAME;
+ THROW_ERROR(common::string::ToAnsi(ss.str()).c_str());
+ }
+
+ IEnumNetCfgBindingPath *pathsEnum = NULL;
+ result = bindings->EnumBindingPaths(EBP_BELOW, &pathsEnum);
+
+ bindings->Release();
+ bindings = nullptr;
+
+ if (S_OK != result)
+ {
+ THROW_ERROR("Failed to acquire binding path enumerator");
+ }
+
+ common::memory::ScopeDestructor pathsEnumDestructor;
+ pathsEnumDestructor += [&pathsEnum]() {
+ pathsEnum->Release();
+ pathsEnum = nullptr;
+ };
+
+ INetCfgBindingPath *bindingPath = NULL;
+
+ result = pathsEnum->Next(1, &bindingPath, nullptr);
+
+ for (; S_OK == result; result = pathsEnum->Next(1, &bindingPath, nullptr))
+ {
+ common::memory::ScopeDestructor bindingPathDestructor;
+ bindingPathDestructor += [&bindingPath]() {
+ bindingPath->Release();
+ bindingPath = nullptr;
+ };
+
+ IEnumNetCfgBindingInterface *enumInterface = nullptr;
+ HRESULT enumResult = bindingPath->EnumBindingInterfaces(&enumInterface);
+
+ if (S_OK != enumResult)
+ {
+ THROW_ERROR("Failed to acquire binding path interfaces");
+ }
+
+ common::memory::ScopeDestructor interfaceEnumDestructor;
+ interfaceEnumDestructor += [&enumInterface]() {
+ enumInterface->Release();
+ enumInterface = nullptr;
+ };
+
+ INetCfgBindingInterface *iface = nullptr;
+
+ while (S_OK == enumInterface->Next(1, &iface, nullptr))
+ {
+ INetCfgComponent *cfgComponent = nullptr;
+
+ auto status = iface->GetLowerComponent(&cfgComponent);
+
+ iface->Release();
+ iface = nullptr;
+
+ if (S_OK != status)
+ {
+ THROW_ERROR("Failed to acquire binding interface component");
+ }
+
+ wchar_t *componentBindName = 0;
+
+ status = cfgComponent->GetBindName(&componentBindName);
+
+ cfgComponent->Release();
+ cfgComponent = nullptr;
+
+ if (S_OK != status)
+ {
+ THROW_ERROR("Failed to acquire bind name");
+ }
+
+ bool matchesBindName = (0 == _wcsicmp(bindName.c_str(), componentBindName));
+ CoTaskMemFree(componentBindName);
+
+ if (matchesBindName)
+ {
+ //
+ // Apply the changes and exit the function
+ //
+
+ result = bindingPath->Enable(enable);
+ if (S_OK != result)
+ {
+ THROW_ERROR("Failed to set IPv6 status");
+ }
+ netCfg->Apply();
+
+ return;
+ }
+ }
+ }
+}
+
+} // anonymous namespace
+
+
+void EnableIpv6ForAdapter(const std::wstring &adapterGuid)
+{
+ try
+ {
+ //
+ // Avoid using the COM objects unless necessary
+ // due to slow performance.
+ //
+
+ const auto key = common::registry::Registry::OpenKey(
+ HKEY_LOCAL_MACHINE,
+ L"SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Linkage"
+ );
+ const auto bindings = key->readStringArray(L"Bind");
+
+ std::wstring matchString = std::wstring(L"\\Device\\").append(adapterGuid);
+
+ for (auto it = bindings.begin(); it != bindings.end(); ++it)
+ {
+ if (0 == _wcsicmp(it->c_str(), matchString.c_str()))
+ {
+ // return from function
+ return;
+ }
+ }
+ }
+ catch (...)
+ {
+ }
+
+ //
+ // Initialize COM
+ //
+
+ HRESULT result = CoInitialize(nullptr);
+
+ if (S_OK != result)
+ {
+ std::stringstream ss;
+ ss << "Failed to initialize COM: " << result;
+ THROW_ERROR(ss.str().c_str());
+ }
+
+ common::memory::ScopeDestructor scopeDest;
+ scopeDest += []() {
+ CoUninitialize();
+ };
+
+ //
+ // Initialize INetCfg
+ //
+
+ INetCfg *netCfg = nullptr;
+ result = CoCreateInstance(
+ CLSID_CNetCfg,
+ nullptr,
+ CLSCTX_INPROC_SERVER,
+ IID_INetCfg,
+ reinterpret_cast<void **>(&netCfg)
+ );
+
+ if (S_OK != result)
+ {
+ std::stringstream ss;
+ ss << "Failed to create INetCfg instance: " << result;
+ THROW_ERROR(ss.str().c_str());
+
+ }
+
+ scopeDest += [&netCfg]() { netCfg->Release(); };
+
+ INetCfgLock *netCfgLock = nullptr;
+ result = netCfg->QueryInterface(IID_INetCfgLock, reinterpret_cast<void **>(&netCfgLock));
+
+ if (S_OK != result)
+ {
+ std::stringstream ss;
+ ss << "Failed to obtain INetCfg lock interface: " << result;
+ THROW_ERROR(ss.str().c_str());
+ }
+
+ scopeDest += [&netCfgLock]() {
+ netCfgLock->Release();
+ };
+
+ wchar_t *blockingApplication = nullptr;
+
+ // NOTE: This should be done before initializing INetCfg
+ result = netCfgLock->AcquireWriteLock(
+ NETCFG_LOCK_TIMEOUT,
+ NETCFG_LOCK_CLIENT_NAME,
+ &blockingApplication
+ );
+
+ if (S_OK != result)
+ {
+ std::wstringstream ss;
+ ss << L"Failed to acquire write lock";
+ if (nullptr != blockingApplication)
+ {
+ ss << L" due to application: " << blockingApplication;
+ }
+ ss << ". (" << result << ")";
+
+ THROW_ERROR(common::string::ToAnsi(ss.str()).c_str());
+ }
+
+ scopeDest += [&]() {
+ CoTaskMemFree(blockingApplication);
+ netCfgLock->ReleaseWriteLock();
+ };
+
+ result = netCfg->Initialize(nullptr);
+
+ if (S_OK != result)
+ {
+ std::stringstream ss;
+ ss << "Failed to initialize INetCfg: " << result;
+ THROW_ERROR(ss.str().c_str());
+ }
+
+ scopeDest += [&netCfg]() { netCfg->Uninitialize(); };
+
+ SetIpv6BindingForBindName(netCfg, adapterGuid, true);
+}
diff --git a/windows/winnet/src/winnet/netconfig.h b/windows/winnet/src/winnet/netconfig.h
new file mode 100644
index 0000000000..b2849857a2
--- /dev/null
+++ b/windows/winnet/src/winnet/netconfig.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#include <string>
+
+void EnableIpv6ForAdapter(const std::wstring &guid);
diff --git a/windows/winnet/src/winnet/winnet.cpp b/windows/winnet/src/winnet/winnet.cpp
index 2a96d150ca..ac56e94ff6 100644
--- a/windows/winnet/src/winnet/winnet.cpp
+++ b/windows/winnet/src/winnet/winnet.cpp
@@ -4,6 +4,7 @@
#include "offlinemonitor.h"
#include "routing/routemanager.h"
#include "converters.h"
+#include "netconfig.h"
#include <libshared/logging/logsinkadapter.h>
#include <libshared/logging/unwind.h>
#include <libshared/network/interfaceutils.h>
@@ -65,43 +66,30 @@ WinNet_EnsureBestMetric(
extern "C"
WINNET_LINKAGE
-WINNET_GTII_STATUS
+bool
WINNET_API
-WinNet_GetTapInterfaceIpv6Status(
+WinNet_EnableIpv6ForAdapter(
+ const wchar_t *deviceGuid,
MullvadLogSink logSink,
void *logSinkContext
)
{
try
{
- MIB_IPINTERFACE_ROW iface = { 0 };
-
- iface.InterfaceLuid = NetworkInterfaces::GetInterfaceLuid(InterfaceUtils::GetTapInterfaceAlias());
- iface.Family = AF_INET6;
-
- const auto status = GetIpInterfaceEntry(&iface);
-
- if (NO_ERROR == status)
- {
- return WINNET_GTII_STATUS_ENABLED;
- }
-
- if (ERROR_NOT_FOUND == status)
+ if (nullptr == deviceGuid)
{
- return WINNET_GTII_STATUS_DISABLED;
+ THROW_ERROR("Invalid argument: deviceGuid");
}
- THROW_WINDOWS_ERROR(status, "Resolve TAP IPv6 interface");
+ EnableIpv6ForAdapter(deviceGuid);
+ return true;
}
- catch (const std::exception &err)
+ catch (const std::exception & err)
{
shared::logging::UnwindAndLog(logSink, logSinkContext, err);
- return WINNET_GTII_STATUS_FAILURE;
- }
- catch (...)
- {
- return WINNET_GTII_STATUS_FAILURE;
+ return false;
}
+ return false;
}
extern "C"
@@ -143,6 +131,60 @@ WinNet_GetTapInterfaceAlias(
extern "C"
WINNET_LINKAGE
+bool
+WINNET_API
+WinNet_InterfaceAliasToGuid(
+ const wchar_t *alias,
+ wchar_t **guid,
+ MullvadLogSink logSink,
+ void *logSinkContext
+)
+{
+ try
+ {
+ if (nullptr == guid)
+ {
+ THROW_ERROR("Invalid argument: guid");
+ }
+ if (nullptr == alias)
+ {
+ THROW_ERROR("Invalid argument: alias");
+ }
+
+ GUID tempGuid = { 0 };
+ NET_LUID luid = { 0 };
+
+ if (NO_ERROR != ConvertInterfaceAliasToLuid(alias, &luid))
+ {
+ THROW_ERROR("ConvertInterfaceAliasToLuid: invalid parameter");
+ }
+
+ if (NO_ERROR != ConvertInterfaceLuidToGuid(&luid, &tempGuid))
+ {
+ THROW_ERROR("ConvertInterfaceLuidToGuid: invalid parameter");
+ }
+
+ const auto guidStr = common::string::FormatGuid(tempGuid);
+
+ auto guidBuffer = new wchar_t[guidStr.size() + 1];
+ wcscpy(guidBuffer, guidStr.c_str());
+ *guid = guidBuffer;
+
+ return true;
+ }
+ catch (const std::exception & err)
+ {
+ shared::logging::UnwindAndLog(logSink, logSinkContext, err);
+ return false;
+ }
+ catch (...)
+ {
+ return false;
+ }
+}
+
+extern "C"
+WINNET_LINKAGE
void
WINNET_API
WinNet_ReleaseString(
diff --git a/windows/winnet/src/winnet/winnet.def b/windows/winnet/src/winnet/winnet.def
index ecec97959e..5a1bbfe99f 100644
--- a/windows/winnet/src/winnet/winnet.def
+++ b/windows/winnet/src/winnet/winnet.def
@@ -1,7 +1,8 @@
LIBRARY winnet
EXPORTS
WinNet_EnsureBestMetric
- WinNet_GetTapInterfaceIpv6Status
+ WinNet_InterfaceAliasToGuid
+ WinNet_EnableIpv6ForAdapter
WinNet_GetTapInterfaceAlias
WinNet_ReleaseString
WinNet_ActivateConnectivityMonitor
diff --git a/windows/winnet/src/winnet/winnet.h b/windows/winnet/src/winnet/winnet.h
index 5e2a4154a4..98b0083f03 100644
--- a/windows/winnet/src/winnet/winnet.h
+++ b/windows/winnet/src/winnet/winnet.h
@@ -33,18 +33,12 @@ WinNet_EnsureBestMetric(
void *logSinkContext
);
-enum WINNET_GTII_STATUS
-{
- WINNET_GTII_STATUS_ENABLED = 0,
- WINNET_GTII_STATUS_DISABLED = 1,
- WINNET_GTII_STATUS_FAILURE = 2,
-};
-
extern "C"
WINNET_LINKAGE
-WINNET_GTII_STATUS
+bool
WINNET_API
-WinNet_GetTapInterfaceIpv6Status(
+WinNet_EnableIpv6ForAdapter(
+ const wchar_t *deviceGuid,
MullvadLogSink logSink,
void *logSinkContext
);
@@ -59,6 +53,17 @@ WinNet_GetTapInterfaceAlias(
void *logSinkContext
);
+extern "C"
+WINNET_LINKAGE
+bool
+WINNET_API
+WinNet_InterfaceAliasToGuid(
+ const wchar_t *alias,
+ wchar_t **guid,
+ MullvadLogSink logSink,
+ void *logSinkContext
+);
+
//
// This is a companion function to the above function.
// Generically named in case we need other functions here that return strings.
diff --git a/windows/winnet/src/winnet/winnet.vcxproj b/windows/winnet/src/winnet/winnet.vcxproj
index 7b4578d4b1..6876035160 100644
--- a/windows/winnet/src/winnet/winnet.vcxproj
+++ b/windows/winnet/src/winnet/winnet.vcxproj
@@ -28,6 +28,7 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="converters.cpp" />
+ <ClCompile Include="netconfig.cpp" />
<ClCompile Include="networkadaptermonitor.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="InterfacePair.cpp" />
@@ -42,6 +43,7 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="converters.h" />
+ <ClInclude Include="netconfig.h" />
<ClInclude Include="networkadaptermonitor.h" />
<ClInclude Include="InterfacePair.h" />
<ClInclude Include="offlinemonitor.h" />
diff --git a/windows/winnet/src/winnet/winnet.vcxproj.filters b/windows/winnet/src/winnet/winnet.vcxproj.filters
index 2d5a039c6b..27f0051bc6 100644
--- a/windows/winnet/src/winnet/winnet.vcxproj.filters
+++ b/windows/winnet/src/winnet/winnet.vcxproj.filters
@@ -21,6 +21,7 @@
<Filter>routing</Filter>
</ClCompile>
<ClCompile Include="converters.cpp" />
+ <ClCompile Include="netconfig.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h" />
@@ -43,6 +44,7 @@
<Filter>routing</Filter>
</ClInclude>
<ClInclude Include="converters.h" />
+ <ClInclude Include="netconfig.h" />
</ItemGroup>
<ItemGroup>
<None Include="winnet.def" />