diff options
| author | David Lönnhager <david.l@mullvad.net> | 2020-11-09 14:55:55 +0100 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2020-11-09 14:55:55 +0100 |
| commit | 55169f326ce43da93649f3bc8d1eb78ec8593bed (patch) | |
| tree | 7d53715b91e3590b3681d1d31b9febcf5b547d44 | |
| parent | fc26f6223269889cf1d23579ac4ab07a2d603d4e (diff) | |
| parent | e429e5caa1cf783b05fc4db3880b41dea02d509c (diff) | |
| download | mullvadvpn-55169f326ce43da93649f3bc8d1eb78ec8593bed.tar.xz mullvadvpn-55169f326ce43da93649f3bc8d1eb78ec8593bed.zip | |
Merge branch 'update-winnet'
| -rw-r--r-- | talpid-core/src/tunnel/wireguard/wireguard_go.rs | 4 | ||||
| -rw-r--r-- | talpid-core/src/winnet.rs | 116 | ||||
| -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 | 59 | ||||
| -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 | 131 | ||||
| -rw-r--r-- | windows/winnet/src/winnet/winnet.def | 2 | ||||
| -rw-r--r-- | windows/winnet/src/winnet/winnet.h | 39 |
10 files changed, 381 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..9fed90ac21 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,14 @@ 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 obtain an IP address given a LUID. + #[error(display = "Failed to obtain IP address for the given interface")] + GetIpAddressFromLuid, + /// 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 +150,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 +166,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 +351,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 +394,48 @@ 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(), + ) + } { + WinNetStatus::Success => Ok(Some(default_route)), + WinNetStatus::NotFound => Ok(None), + WinNetStatus::Failure => Err(Error::GetDefaultRoute), + } +} + +// TODO: Remove attribute once this is in use. +#[allow(dead_code)] +pub fn interface_luid_to_ip( + family: WinNetAddrFamily, + luid: u64, +) -> Result<Option<WinNetIp>, Error> { + let mut ip = WinNetIp::default(); + match unsafe { + WinNet_InterfaceLuidToIpAddress( + family, + luid, + &mut ip as *mut _, + Some(log_sink), + logging_context(), + ) + } { + WinNetStatus::Success => Ok(Some(ip)), + WinNetStatus::NotFound => Ok(None), + WinNetStatus::Failure => Err(Error::GetIpAddressFromLuid), + } +} + 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 +456,14 @@ mod api { pub type ConnectivityCallback = unsafe extern "system" fn(is_connected: bool, ctx: *mut c_void); + #[allow(dead_code)] + #[repr(u32)] + pub enum WinNetStatus { + Success = 0, + NotFound = 1, + Failure = 2, + } + extern "system" { #[link_name = "WinNet_ActivateRouteManager"] pub fn WinNet_ActivateRouteManager(sink: Option<LogSink>, sink_context: *const u8) -> bool; @@ -415,6 +500,27 @@ 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, + ) -> WinNetStatus; + + // TODO: Remove "allow(dead_code)" this is in use. + #[allow(dead_code)] + #[link_name = "WinNet_InterfaceLuidToIpAddress"] + pub fn WinNet_InterfaceLuidToIpAddress( + family: super::WinNetAddrFamily, + luid: u64, + ip: *mut super::WinNetIp, + sink: Option<LogSink>, + sink_context: *const u8, + ) -> WinNetStatus; + #[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..20b1f942c4 100644 --- a/windows/winnet/src/winnet/routing/helpers.cpp +++ b/windows/winnet/src/winnet/routing/helpers.cpp @@ -6,6 +6,51 @@ #include <libcommon/error.h> #include <libcommon/memory.h> +namespace +{ + +// Interface description substrings found for virtual adapters. +const wchar_t *TUNNEL_INTERFACE_DESCS[] = { + L"WireGuard", + L"TAP Adapter" +}; + +bool IsRouteOnPhysicalInterface(const MIB_IPFORWARD_ROW2 &route) +{ + switch (route.InterfaceLuid.Info.IfType) + { + case IF_TYPE_SOFTWARE_LOOPBACK: + case IF_TYPE_TUNNEL: + { + return false; + } + } + + // OpenVPN uses interface type IF_TYPE_PROP_VIRTUAL, + // but tethering etc. may rely on virtual adapters too, + // so we have to filter out the TAP adapter specifically. + + MIB_IF_ROW2 row = { 0 }; + row.InterfaceLuid = route.InterfaceLuid; + + if (NO_ERROR != GetIfEntry2(&row)) + { + THROW_ERROR("Cannot obtain interface information for the given route"); + } + + for (size_t i = 0; i < ARRAYSIZE(TUNNEL_INTERFACE_DESCS); i++) + { + if (nullptr != wcsstr(row.Description, TUNNEL_INTERFACE_DESCS[i])) + { + return false; + } + } + + return true; +} + +} // anonymous namespace + namespace winnet::routing { @@ -124,7 +169,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 +191,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 +200,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 +211,7 @@ InterfaceAndGateway GetBestDefaultRoute(ADDRESS_FAMILY family) if (annotated.empty()) { - THROW_ERROR("Unable to determine details of default route"); + return std::nullopt; } // @@ -187,10 +234,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..50332a85e6 100644 --- a/windows/winnet/src/winnet/winnet.cpp +++ b/windows/winnet/src/winnet/winnet.cpp @@ -9,6 +9,7 @@ #include <libshared/logging/unwind.h>
#include <libshared/network/interfaceutils.h>
#include <libcommon/error.h>
+#include <libcommon/memory.h>
#include <libcommon/valuemapper.h>
#include <libcommon/network.h>
#include <cstdint>
@@ -94,6 +95,124 @@ WinNet_EnableIpv6ForAdapter( extern "C"
WINNET_LINKAGE
+WINNET_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_STATUS_NOT_FOUND;
+ }
+
+ route->interfaceLuid = ifaceAndGateway->iface.Value;
+ const auto ips = winnet::ConvertNativeAddresses(&ifaceAndGateway->gateway, 1);
+ route->gateway = ips[0];
+
+ return WINNET_STATUS_SUCCESS;
+ }
+ catch (const std::exception & err)
+ {
+ shared::logging::UnwindAndLog(logSink, logSinkContext, err);
+ return WINNET_STATUS_FAILURE;
+ }
+ catch (...)
+ {
+ return WINNET_STATUS_FAILURE;
+ }
+}
+
+extern "C"
+WINNET_LINKAGE
+WINNET_STATUS
+WINNET_API
+WinNet_InterfaceLuidToIpAddress(
+ WINNET_ADDR_FAMILY family,
+ uint64_t interfaceLuid,
+ WINNET_IP *ip,
+ MullvadLogSink logSink,
+ void *logSinkContext
+)
+{
+ try
+ {
+ if (nullptr == ip)
+ {
+ THROW_ERROR("Invalid argument: ip");
+ }
+
+ 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);
+
+ MIB_UNICASTIPADDRESS_TABLE *table = nullptr;
+ const auto status = GetUnicastIpAddressTable(win_family, &table);
+
+ if (NO_ERROR != status)
+ {
+ THROW_WINDOWS_ERROR(status, "GetUnicastIpAddressTable");
+ }
+
+ common::memory::ScopeDestructor destructor;
+
+ destructor += [table]() {
+ FreeMibTable(table);
+ };
+
+ for (ULONG i = 0; i < table->NumEntries; i++)
+ {
+ const auto entry = table->Table[i];
+
+ if (interfaceLuid != entry.InterfaceLuid.Value)
+ {
+ continue;
+ }
+
+ // Found IP address
+ const auto ips = winnet::ConvertNativeAddresses(&entry.Address, 1);
+ *ip = ips[0];
+
+ return WINNET_STATUS_SUCCESS;
+ }
+
+ return WINNET_STATUS_NOT_FOUND;
+ }
+ catch (const std::exception & err)
+ {
+ shared::logging::UnwindAndLog(logSink, logSinkContext, err);
+ return WINNET_STATUS_FAILURE;
+ }
+ catch (...)
+ {
+ return WINNET_STATUS_FAILURE;
+ }
+}
+
+extern "C"
+WINNET_LINKAGE
bool
WINNET_API
WinNet_GetTapInterfaceAlias(
@@ -480,22 +599,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..21bb2725c7 100644 --- a/windows/winnet/src/winnet/winnet.def +++ b/windows/winnet/src/winnet/winnet.def @@ -10,3 +10,5 @@ EXPORTS WinNet_ActivateRouteManager WinNet_DeactivateRouteManager WinNet_AddDeviceIpAddresses + WinNet_GetBestDefaultRoute + WinNet_InterfaceLuidToIpAddress diff --git a/windows/winnet/src/winnet/winnet.h b/windows/winnet/src/winnet/winnet.h index 3597b01a26..0da271274e 100644 --- a/windows/winnet/src/winnet/winnet.h +++ b/windows/winnet/src/winnet/winnet.h @@ -180,6 +180,43 @@ WINNET_API WinNet_DeleteAppliedRoutes( ); +typedef struct tag_WINNET_DEFAULT_ROUTE +{ + uint64_t interfaceLuid; + WINNET_IP gateway; +} +WINNET_DEFAULT_ROUTE; + +enum WINNET_STATUS +{ + WINNET_STATUS_SUCCESS = 0, + WINNET_STATUS_NOT_FOUND = 1, + WINNET_STATUS_FAILURE = 2, +}; + +extern "C" +WINNET_LINKAGE +WINNET_STATUS +WINNET_API +WinNet_GetBestDefaultRoute( + WINNET_ADDR_FAMILY family, + WINNET_DEFAULT_ROUTE *route, + MullvadLogSink logSink, + void *logSinkContext +); + +extern "C" +WINNET_LINKAGE +WINNET_STATUS +WINNET_API +WinNet_InterfaceLuidToIpAddress( + WINNET_ADDR_FAMILY family, + uint64_t interfaceLuid, + WINNET_IP *ip, + MullvadLogSink logSink, + void *logSinkContext +); + enum WINNET_DEFAULT_ROUTE_CHANGED_EVENT_TYPE { // Best default route changed. @@ -197,7 +234,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 ); |
