diff options
| author | David Lönnhager <david.l@mullvad.net> | 2020-09-11 19:20:25 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2020-11-09 14:54:58 +0100 |
| commit | c3921b60fd92e114368e1df07b977ec7178a5e65 (patch) | |
| tree | 286ed72b083e4993110686a5219aaff1cfc5be8b | |
| parent | fc26f6223269889cf1d23579ac4ab07a2d603d4e (diff) | |
| download | mullvadvpn-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.rs | 4 | ||||
| -rw-r--r-- | talpid-core/src/winnet.rs | 80 | ||||
| -rw-r--r-- | windows/winnet/src/winnet/converters.cpp | 40 | ||||
| -rw-r--r-- | windows/winnet/src/winnet/converters.h | 1 | ||||
| -rw-r--r-- | windows/winnet/src/winnet/routing/helpers.cpp | 33 | ||||
| -rw-r--r-- | windows/winnet/src/winnet/routing/helpers.h | 3 | ||||
| -rw-r--r-- | windows/winnet/src/winnet/routing/routemanager.cpp | 7 | ||||
| -rw-r--r-- | windows/winnet/src/winnet/winnet.cpp | 61 | ||||
| -rw-r--r-- | windows/winnet/src/winnet/winnet.def | 1 | ||||
| -rw-r--r-- | windows/winnet/src/winnet/winnet.h | 27 |
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 ); |
