summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2020-09-11 19:20:25 +0200
committerDavid Lönnhager <david.l@mullvad.net>2020-11-09 14:54:58 +0100
commitc3921b60fd92e114368e1df07b977ec7178a5e65 (patch)
tree286ed72b083e4993110686a5219aaff1cfc5be8b
parentfc26f6223269889cf1d23579ac4ab07a2d603d4e (diff)
downloadmullvadvpn-c3921b60fd92e114368e1df07b977ec7178a5e65.tar.xz
mullvadvpn-c3921b60fd92e114368e1df07b977ec7178a5e65.zip
Add winnet function for obtaining the best default route
-rw-r--r--talpid-core/src/tunnel/wireguard/wireguard_go.rs4
-rw-r--r--talpid-core/src/winnet.rs80
-rw-r--r--windows/winnet/src/winnet/converters.cpp40
-rw-r--r--windows/winnet/src/winnet/converters.h1
-rw-r--r--windows/winnet/src/winnet/routing/helpers.cpp33
-rw-r--r--windows/winnet/src/winnet/routing/helpers.h3
-rw-r--r--windows/winnet/src/winnet/routing/routemanager.cpp7
-rw-r--r--windows/winnet/src/winnet/winnet.cpp61
-rw-r--r--windows/winnet/src/winnet/winnet.def1
-rw-r--r--windows/winnet/src/winnet/winnet.h27
10 files changed, 236 insertions, 21 deletions
diff --git a/talpid-core/src/tunnel/wireguard/wireguard_go.rs b/talpid-core/src/tunnel/wireguard/wireguard_go.rs
index b4d437f046..e1ded8b3a0 100644
--- a/talpid-core/src/tunnel/wireguard/wireguard_go.rs
+++ b/talpid-core/src/tunnel/wireguard/wireguard_go.rs
@@ -165,7 +165,7 @@ impl WgGoTunnel {
pub unsafe extern "system" fn default_route_changed_callback(
event_type: winnet::WinNetDefaultRouteChangeEventType,
address_family: winnet::WinNetAddrFamily,
- interface_luid: u64,
+ default_route: winnet::WinNetDefaultRoute,
_ctx: *mut libc::c_void,
) {
use winapi::shared::{ifdef::NET_LUID, netioapi::ConvertInterfaceLuidToIndex};
@@ -173,7 +173,7 @@ impl WgGoTunnel {
winnet::WinNetDefaultRouteChangeEventType::DefaultRouteChanged => {
let mut iface_idx = 0u32;
let iface_luid = NET_LUID {
- Value: interface_luid,
+ Value: default_route.interface_luid,
};
let status =
ConvertInterfaceLuidToIndex(&iface_luid as *const _, &mut iface_idx as *mut _);
diff --git a/talpid-core/src/winnet.rs b/talpid-core/src/winnet.rs
index ab9dff5d06..a648aad982 100644
--- a/talpid-core/src/winnet.rs
+++ b/talpid-core/src/winnet.rs
@@ -5,7 +5,7 @@ use ipnetwork::IpNetwork;
use libc::{c_void, wchar_t};
use std::{
ffi::{OsStr, OsString},
- net::IpAddr,
+ net::{IpAddr, Ipv4Addr, Ipv6Addr},
ptr,
};
use widestring::WideCString;
@@ -29,6 +29,10 @@ pub enum Error {
#[error(display = "Failed to obtain GUID for the network interface")]
GetInterfaceGuid,
+ /// Failed to get the current default route.
+ #[error(display = "Failed to obtain default route")]
+ GetDefaultRoute,
+
/// Failed to read IPv6 status on the TAP network interface.
#[error(display = "Failed to read IPv6 status on the TAP network interface")]
GetIpv6Status,
@@ -142,6 +146,12 @@ pub enum WinNetAddrFamily {
IPV6 = 1,
}
+impl Default for WinNetAddrFamily {
+ fn default() -> Self {
+ WinNetAddrFamily::IPV4
+ }
+}
+
impl WinNetAddrFamily {
pub fn to_windows_proto_enum(&self) -> u16 {
match self {
@@ -152,9 +162,30 @@ impl WinNetAddrFamily {
}
#[repr(C)]
+#[derive(Default)]
pub struct WinNetIp {
- addr_family: WinNetAddrFamily,
- ip_bytes: [u8; 16],
+ pub addr_family: WinNetAddrFamily,
+ pub ip_bytes: [u8; 16],
+}
+
+#[repr(C)]
+#[derive(Default)]
+pub struct WinNetDefaultRoute {
+ pub interface_luid: u64,
+ pub gateway: WinNetIp,
+}
+
+impl From<WinNetIp> for IpAddr {
+ fn from(addr: WinNetIp) -> IpAddr {
+ match addr.addr_family {
+ WinNetAddrFamily::IPV4 => {
+ let mut bytes: [u8; 4] = Default::default();
+ bytes.clone_from_slice(&addr.ip_bytes[..4]);
+ IpAddr::V4(Ipv4Addr::from(bytes))
+ }
+ WinNetAddrFamily::IPV6 => IpAddr::V6(Ipv6Addr::from(addr.ip_bytes)),
+ }
+ }
}
impl From<IpAddr> for WinNetIp {
@@ -316,8 +347,8 @@ pub enum WinNetDefaultRouteChangeEventType {
pub type DefaultRouteChangedCallback = unsafe extern "system" fn(
event_type: WinNetDefaultRouteChangeEventType,
- addr_family: WinNetAddrFamily,
- interface_luid: u64,
+ family: WinNetAddrFamily,
+ default_route: WinNetDefaultRoute,
ctx: *mut c_void,
);
@@ -359,6 +390,26 @@ pub fn deactivate_routing_manager() {
unsafe { WinNet_DeactivateRouteManager() }
}
+// TODO: Remove attribute once this is in use.
+#[allow(dead_code)]
+pub fn get_best_default_route(
+ family: WinNetAddrFamily,
+) -> Result<Option<WinNetDefaultRoute>, Error> {
+ let mut default_route = WinNetDefaultRoute::default();
+ match unsafe {
+ WinNet_GetBestDefaultRoute(
+ family,
+ &mut default_route as *mut _,
+ Some(log_sink),
+ logging_context(),
+ )
+ } {
+ GetBestDefaultRouteStatus::Success => Ok(Some(default_route)),
+ GetBestDefaultRouteStatus::NotFound => Ok(None),
+ GetBestDefaultRouteStatus::Failure => Err(Error::GetDefaultRoute),
+ }
+}
+
pub fn add_device_ip_addresses(iface: &String, addresses: &Vec<IpAddr>) -> bool {
let raw_iface = WideCString::from_str(iface)
.expect("Failed to convert UTF-8 string to null terminated UCS string")
@@ -379,6 +430,15 @@ mod api {
pub type ConnectivityCallback = unsafe extern "system" fn(is_connected: bool, ctx: *mut c_void);
+ #[allow(dead_code)]
+ #[repr(u32)]
+ pub enum FailableOptionalStatus {
+ Success = 0,
+ NotFound = 1,
+ Failure = 2,
+ }
+ pub type GetBestDefaultRouteStatus = FailableOptionalStatus;
+
extern "system" {
#[link_name = "WinNet_ActivateRouteManager"]
pub fn WinNet_ActivateRouteManager(sink: Option<LogSink>, sink_context: *const u8) -> bool;
@@ -415,6 +475,16 @@ mod api {
sink_context: *const u8,
) -> bool;
+ // TODO: Remove "allow(dead_code)" this is in use.
+ #[allow(dead_code)]
+ #[link_name = "WinNet_GetBestDefaultRoute"]
+ pub fn WinNet_GetBestDefaultRoute(
+ family: super::WinNetAddrFamily,
+ default_route: *mut super::WinNetDefaultRoute,
+ sink: Option<LogSink>,
+ sink_context: *const u8,
+ ) -> GetBestDefaultRouteStatus;
+
#[link_name = "WinNet_GetTapInterfaceAlias"]
pub fn WinNet_GetTapInterfaceAlias(
tunnel_interface_alias: *mut *mut wchar_t,
diff --git a/windows/winnet/src/winnet/converters.cpp b/windows/winnet/src/winnet/converters.cpp
index 3584f8e34f..dc3f333c6c 100644
--- a/windows/winnet/src/winnet/converters.cpp
+++ b/windows/winnet/src/winnet/converters.cpp
@@ -37,6 +37,33 @@ SOCKADDR_INET IpToNative(const WINNET_IP &from)
return to;
}
+WINNET_IP IpFromNative(const SOCKADDR_INET &from)
+{
+ WINNET_IP to = { 0 };
+
+ switch (from.si_family)
+ {
+ case AF_INET:
+ {
+ *reinterpret_cast<uint32_t*>(to.bytes) = static_cast<uint32_t>(from.Ipv4.sin_addr.s_addr);
+ to.family = WINNET_ADDR_FAMILY_IPV4;
+ break;
+ }
+ case AF_INET6:
+ {
+ memcpy(to.bytes, from.Ipv6.sin6_addr.u.Byte, 16);
+ to.family = WINNET_ADDR_FAMILY_IPV6;
+ break;
+ }
+ default:
+ {
+ THROW_ERROR("Invalid network address family");
+ }
+ }
+
+ return to;
+}
+
} // anonymous namespace
namespace winnet
@@ -115,4 +142,17 @@ std::vector<SOCKADDR_INET> ConvertAddresses(const WINNET_IP *addresses, uint32_t
return out;
}
+std::vector<WINNET_IP> ConvertNativeAddresses(const SOCKADDR_INET *addresses, uint32_t numAddresses)
+{
+ std::vector<WINNET_IP> out;
+ out.reserve(numAddresses);
+
+ for (uint32_t i = 0; i < numAddresses; ++i)
+ {
+ out.emplace_back(IpFromNative(addresses[i]));
+ }
+
+ return out;
+}
+
}
diff --git a/windows/winnet/src/winnet/converters.h b/windows/winnet/src/winnet/converters.h
index 8a1c59e4da..e094728902 100644
--- a/windows/winnet/src/winnet/converters.h
+++ b/windows/winnet/src/winnet/converters.h
@@ -12,5 +12,6 @@ routing::Network ConvertNetwork(const WINNET_IP_NETWORK &in);
std::optional<routing::Node> ConvertNode(const WINNET_NODE *in);
std::vector<routing::Route> ConvertRoutes(const WINNET_ROUTE *routes, uint32_t numRoutes);
std::vector<SOCKADDR_INET> ConvertAddresses(const WINNET_IP *addresses, uint32_t numAddresses);
+std::vector<WINNET_IP> ConvertNativeAddresses(const SOCKADDR_INET *addresses, uint32_t numAddresses);
}
diff --git a/windows/winnet/src/winnet/routing/helpers.cpp b/windows/winnet/src/winnet/routing/helpers.cpp
index 2f171c08a1..9e46b203d4 100644
--- a/windows/winnet/src/winnet/routing/helpers.cpp
+++ b/windows/winnet/src/winnet/routing/helpers.cpp
@@ -6,6 +6,25 @@
#include <libcommon/error.h>
#include <libcommon/memory.h>
+namespace
+{
+
+bool IsRouteOnPhysicalInterface(const MIB_IPFORWARD_ROW2 &route)
+{
+ switch (route.InterfaceLuid.Info.IfType)
+ {
+ case IF_TYPE_SOFTWARE_LOOPBACK:
+ case IF_TYPE_TUNNEL:
+ case IF_TYPE_PROP_VIRTUAL:
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // anonymous namespace
+
namespace winnet::routing
{
@@ -124,7 +143,7 @@ bool RouteHasGateway(const MIB_IPFORWARD_ROW2 &route)
};
}
-InterfaceAndGateway GetBestDefaultRoute(ADDRESS_FAMILY family)
+std::optional<InterfaceAndGateway> GetBestDefaultRoute(ADDRESS_FAMILY family)
{
PMIB_IPFORWARD_TABLE2 table;
@@ -146,7 +165,8 @@ InterfaceAndGateway GetBestDefaultRoute(ADDRESS_FAMILY family)
candidates.reserve(table->NumEntries);
//
- // Enumerate routes looking for: route 0/0 && gateway specified.
+ // Enumerate routes looking for: route 0/0
+ // The WireGuard interface route has no gateway.
//
for (ULONG i = 0; i < table->NumEntries; ++i)
@@ -154,7 +174,8 @@ InterfaceAndGateway GetBestDefaultRoute(ADDRESS_FAMILY family)
const MIB_IPFORWARD_ROW2 &candidate = table->Table[i];
if (0 == candidate.DestinationPrefix.PrefixLength
- && RouteHasGateway(candidate))
+ && RouteHasGateway(candidate)
+ && IsRouteOnPhysicalInterface(candidate))
{
candidates.emplace_back(&candidate);
}
@@ -164,7 +185,7 @@ InterfaceAndGateway GetBestDefaultRoute(ADDRESS_FAMILY family)
if (annotated.empty())
{
- THROW_ERROR("Unable to determine details of default route");
+ return std::nullopt;
}
//
@@ -187,10 +208,10 @@ InterfaceAndGateway GetBestDefaultRoute(ADDRESS_FAMILY family)
if (false == annotated[0].active)
{
- THROW_ERROR("Unable to identify active default route");
+ return std::nullopt;
}
- return InterfaceAndGateway { annotated[0].route->InterfaceLuid, annotated[0].route->NextHop };
+ return std::make_optional(InterfaceAndGateway { annotated[0].route->InterfaceLuid, annotated[0].route->NextHop });
}
bool AdapterInterfaceEnabled(const IP_ADAPTER_ADDRESSES *adapter, ADDRESS_FAMILY family)
diff --git a/windows/winnet/src/winnet/routing/helpers.h b/windows/winnet/src/winnet/routing/helpers.h
index 37a0acc611..9d73585da9 100644
--- a/windows/winnet/src/winnet/routing/helpers.h
+++ b/windows/winnet/src/winnet/routing/helpers.h
@@ -2,6 +2,7 @@
#include "types.h"
#include <vector>
+#include <optional>
namespace winnet::routing
{
@@ -29,7 +30,7 @@ std::vector<AnnotatedRoute> AnnotateRoutes(const std::vector<const MIB_IPFORWARD
bool RouteHasGateway(const MIB_IPFORWARD_ROW2 &route);
-InterfaceAndGateway GetBestDefaultRoute(ADDRESS_FAMILY family);
+std::optional<InterfaceAndGateway> GetBestDefaultRoute(ADDRESS_FAMILY family);
bool AdapterInterfaceEnabled(const IP_ADAPTER_ADDRESSES *adapter, ADDRESS_FAMILY family);
diff --git a/windows/winnet/src/winnet/routing/routemanager.cpp b/windows/winnet/src/winnet/routing/routemanager.cpp
index 9d5b36ff98..dc38466440 100644
--- a/windows/winnet/src/winnet/routing/routemanager.cpp
+++ b/windows/winnet/src/winnet/routing/routemanager.cpp
@@ -125,7 +125,12 @@ InterfaceAndGateway ResolveNode(ADDRESS_FAMILY family, const std::optional<Node>
if (false == optionalNode.has_value())
{
- return GetBestDefaultRoute(family);
+ const auto default_route = GetBestDefaultRoute(family);
+ if (!default_route.has_value())
+ {
+ THROW_ERROR("Unable to determine details of default route");
+ }
+ return default_route.value();
}
const auto &node = optionalNode.value();
diff --git a/windows/winnet/src/winnet/winnet.cpp b/windows/winnet/src/winnet/winnet.cpp
index c52fc57d60..4532e43d14 100644
--- a/windows/winnet/src/winnet/winnet.cpp
+++ b/windows/winnet/src/winnet/winnet.cpp
@@ -94,6 +94,55 @@ WinNet_EnableIpv6ForAdapter(
extern "C"
WINNET_LINKAGE
+WINNET_GBDR_STATUS
+WINNET_API
+WinNet_GetBestDefaultRoute(
+ WINNET_ADDR_FAMILY family,
+ WINNET_DEFAULT_ROUTE *route,
+ MullvadLogSink logSink,
+ void *logSinkContext
+)
+{
+ try
+ {
+ if (nullptr == route)
+ {
+ THROW_ERROR("Invalid argument: route");
+ }
+
+ static const std::pair<WINNET_ADDR_FAMILY, ADDRESS_FAMILY> familyMap[] =
+ {
+ { WINNET_ADDR_FAMILY_IPV4, static_cast<ADDRESS_FAMILY>(AF_INET) },
+ { WINNET_ADDR_FAMILY_IPV6, static_cast<ADDRESS_FAMILY>(AF_INET6) }
+ };
+ const auto win_family = common::ValueMapper::Map<>(family, familyMap);
+
+ const auto ifaceAndGateway = GetBestDefaultRoute(win_family);
+
+ if (!ifaceAndGateway.has_value())
+ {
+ return WINNET_GBDR_STATUS_NOT_FOUND;
+ }
+
+ route->interfaceLuid = ifaceAndGateway->iface.Value;
+ const auto ips = winnet::ConvertNativeAddresses(&ifaceAndGateway->gateway, 1);
+ route->gateway = ips[0];
+
+ return WINNET_GBDR_STATUS_SUCCESS;
+ }
+ catch (const std::exception & err)
+ {
+ shared::logging::UnwindAndLog(logSink, logSinkContext, err);
+ return WINNET_GBDR_STATUS_FAILURE;
+ }
+ catch (...)
+ {
+ return WINNET_GBDR_STATUS_FAILURE;
+ }
+}
+
+extern "C"
+WINNET_LINKAGE
bool
WINNET_API
WinNet_GetTapInterfaceAlias(
@@ -480,22 +529,24 @@ WinNet_RegisterDefaultRouteChangedCallback(
const auto translatedFamily = common::ValueMapper::Map<>(family, familyMap);
+ WINNET_DEFAULT_ROUTE defaultRoute = { 0 };
+
//
- // Determine which LUID to forward.
+ // Determine which LUID and gateway to forward.
//
- uint64_t translatedLuid = 0;
-
if (RouteManager::DefaultRouteChangedEventType::Updated == eventType)
{
- translatedLuid = route.value().iface.Value;
+ const auto ips = winnet::ConvertNativeAddresses(&route.value().gateway, 1);
+ defaultRoute.gateway = ips[0];
+ defaultRoute.interfaceLuid = route.value().iface.Value;
}
//
// Forward to client.
//
- callback(translatedEventType, translatedFamily, translatedLuid, context);
+ callback(translatedEventType, translatedFamily, defaultRoute, context);
};
*registrationHandle = g_RouteManager->registerDefaultRouteChangedCallback(forwarder);
diff --git a/windows/winnet/src/winnet/winnet.def b/windows/winnet/src/winnet/winnet.def
index 5a1bbfe99f..187ce253d9 100644
--- a/windows/winnet/src/winnet/winnet.def
+++ b/windows/winnet/src/winnet/winnet.def
@@ -10,3 +10,4 @@ EXPORTS
WinNet_ActivateRouteManager
WinNet_DeactivateRouteManager
WinNet_AddDeviceIpAddresses
+ WinNet_GetBestDefaultRoute
diff --git a/windows/winnet/src/winnet/winnet.h b/windows/winnet/src/winnet/winnet.h
index 3597b01a26..c102ebdba3 100644
--- a/windows/winnet/src/winnet/winnet.h
+++ b/windows/winnet/src/winnet/winnet.h
@@ -180,6 +180,31 @@ WINNET_API
WinNet_DeleteAppliedRoutes(
);
+typedef struct tag_WINNET_DEFAULT_ROUTE
+{
+ uint64_t interfaceLuid;
+ WINNET_IP gateway;
+}
+WINNET_DEFAULT_ROUTE;
+
+enum WINNET_GBDR_STATUS
+{
+ WINNET_GBDR_STATUS_SUCCESS = 0,
+ WINNET_GBDR_STATUS_NOT_FOUND = 1,
+ WINNET_GBDR_STATUS_FAILURE = 2,
+};
+
+extern "C"
+WINNET_LINKAGE
+WINNET_GBDR_STATUS
+WINNET_API
+WinNet_GetBestDefaultRoute(
+ WINNET_ADDR_FAMILY family,
+ WINNET_DEFAULT_ROUTE *route,
+ MullvadLogSink logSink,
+ void *logSinkContext
+);
+
enum WINNET_DEFAULT_ROUTE_CHANGED_EVENT_TYPE
{
// Best default route changed.
@@ -197,7 +222,7 @@ typedef void (WINNET_API *WinNetDefaultRouteChangedCallback)
WINNET_ADDR_FAMILY family,
// For update events, indicates the interface associated with the new best default route.
- uint64_t interfaceLuid,
+ WINNET_DEFAULT_ROUTE route,
void *context
);