mirror of https://github.com/microsoft/WSL
2726 lines
127 KiB
C++
2726 lines
127 KiB
C++
/*++
|
|
|
|
Copyright (c) Microsoft. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
WslMirroredNetworking.cpp
|
|
|
|
Abstract:
|
|
|
|
This file contains WSL mirrored networking function definitions.
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include "WslMirroredNetworking.h"
|
|
#include "WslCoreMessageQueue.h"
|
|
#include "Stringify.h"
|
|
#include "WslCoreNetworkingSupport.h"
|
|
#include "WslCoreNetworkEndpointSettings.h"
|
|
#include "hcs.hpp"
|
|
#include "hns_schema.h"
|
|
|
|
static constexpr auto c_loopbackDeviceName = TEXT(LX_INIT_LOOPBACK_DEVICE_NAME);
|
|
static constexpr auto c_initialMirroredGoalStateWaitTimeoutMs = 5 * 1000;
|
|
|
|
using namespace wsl::windows::common;
|
|
using namespace wsl::shared;
|
|
using wsl::core::networking::EndpointIpAddress;
|
|
using wsl::core::networking::EndpointRoute;
|
|
|
|
namespace {
|
|
inline const auto HnsModifyRequestTypeToString(const hns::ModifyRequestType requestType)
|
|
{
|
|
return JsonEnumToString<hns::ModifyRequestType>(requestType);
|
|
}
|
|
} // namespace
|
|
|
|
_Requires_lock_held_(m_networkLock)
|
|
void wsl::core::networking::WslMirroredNetworkManager::ProcessConnectivityChange()
|
|
{
|
|
const std::set<GUID, wsl::windows::common::helpers::GuidLess> initialConnectedInterfaces{std::move(m_hostConnectedInterfaces)};
|
|
m_hostConnectedInterfaces.clear();
|
|
|
|
const auto coInit = wil::CoInitializeEx();
|
|
const wil::com_ptr<INetworkListManager> networkListManager = wil::CoCreateInstance<NetworkListManager, INetworkListManager>();
|
|
|
|
wil::com_ptr<IEnumNetworks> networksEnumerator;
|
|
THROW_IF_FAILED(networkListManager->GetNetworks(NLM_ENUM_NETWORK_CONNECTED, &networksEnumerator));
|
|
|
|
for (;;)
|
|
{
|
|
ULONG fetched{};
|
|
wil::com_ptr<INetwork> networkInstance;
|
|
auto hr = networksEnumerator->Next(1, &networkInstance, &fetched);
|
|
THROW_IF_FAILED(hr);
|
|
if (hr == S_FALSE || fetched == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// each NLM network could have multiple interfaces - walk through each
|
|
// if we fail trying to access an individual interface, continue the loop for the other interfaces
|
|
|
|
wil::com_ptr<IEnumNetworkConnections> enumNetworkConnections;
|
|
hr = networkInstance->GetNetworkConnections(&enumNetworkConnections);
|
|
if (FAILED(hr))
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::ProcessConnectivityChange - ignoring interface after processing "
|
|
"INetworkConnection::GetAdapterId",
|
|
TraceLoggingValue(hr, "hr"));
|
|
continue;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
ULONG fetchedNetworkConnections{};
|
|
wil::com_ptr<INetworkConnection> networkConnection;
|
|
hr = enumNetworkConnections->Next(1, &networkConnection, &fetchedNetworkConnections);
|
|
if (FAILED(hr) || hr == S_FALSE || fetchedNetworkConnections == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
GUID interfaceGuid{};
|
|
hr = networkConnection->GetAdapterId(&interfaceGuid);
|
|
if (FAILED(hr))
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::ProcessConnectivityChange - ignoring interface INetworkConnection::GetAdapterId "
|
|
"failed",
|
|
TraceLoggingValue(hr, "hr"));
|
|
continue;
|
|
}
|
|
|
|
NLM_CONNECTIVITY connectivity{};
|
|
hr = networkConnection->GetConnectivity(&connectivity);
|
|
if (FAILED(hr) || connectivity == NLM_CONNECTIVITY_DISCONNECTED)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::ProcessConnectivityChange - ignoring interface after processing "
|
|
"INetworkConnection::GetConnectivity",
|
|
TraceLoggingValue(wsl::shared::string::GuidToString<wchar_t>(interfaceGuid).c_str(), "interfaceGuid"),
|
|
TraceLoggingValue(connectivity == NLM_CONNECTIVITY_DISCONNECTED, "is_NLM_CONNECTIVITY_DISCONNECTED"),
|
|
TraceLoggingValue(hr, "hr"));
|
|
continue;
|
|
}
|
|
|
|
m_hostConnectedInterfaces.insert(interfaceGuid);
|
|
}
|
|
}
|
|
|
|
if (initialConnectedInterfaces != m_hostConnectedInterfaces)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::ProcessConnectivityChange - reset goal state",
|
|
TraceLoggingValue(initialConnectedInterfaces.size(), "previous_interfaces_size"),
|
|
TraceLoggingValue(m_hostConnectedInterfaces.size(), "updated_interfaces_size"));
|
|
|
|
m_inMirroredGoalState.ResetEvent();
|
|
m_connectivityTelemetry.UpdateTimer();
|
|
|
|
std::wstring guids;
|
|
for (const auto& connectedInterface : initialConnectedInterfaces)
|
|
{
|
|
guids.append(wsl::shared::string::GuidToString<wchar_t>(connectedInterface) + L",");
|
|
}
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::ProcessConnectivityChange [previous]",
|
|
TraceLoggingValue(guids.c_str(), "connectedInterfaces"));
|
|
|
|
guids.clear();
|
|
for (const auto& connectedInterface : m_hostConnectedInterfaces)
|
|
{
|
|
guids.append(wsl::shared::string::GuidToString<wchar_t>(connectedInterface) + L",");
|
|
}
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::ProcessConnectivityChange [updated]",
|
|
TraceLoggingValue(guids.c_str(), "connectedInterfaces"));
|
|
}
|
|
}
|
|
|
|
_Requires_lock_held_(m_networkLock)
|
|
void wsl::core::networking::WslMirroredNetworkManager::ProcessIpAddressChange()
|
|
{
|
|
wsl::core::networking::unique_address_table addressTable{};
|
|
THROW_IF_WIN32_ERROR(GetUnicastIpAddressTable(AF_UNSPEC, &addressTable));
|
|
|
|
for (auto& endpoint : m_networkEndpoints)
|
|
{
|
|
const auto initialAddresses{std::move(endpoint.Network->IpAddresses)};
|
|
endpoint.Network->IpAddresses.clear();
|
|
|
|
// if the interface isn't connected, ensure we always track zero addresses
|
|
if (!endpoint.Network->IsConnected)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (const auto& address : wil::make_range(addressTable.get()->Table, addressTable.get()->NumEntries))
|
|
{
|
|
if (address.InterfaceIndex != endpoint.Network->InterfaceIndex)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const auto endpointAddress = EndpointIpAddress(address);
|
|
if (endpointAddress.IsPreferred())
|
|
{
|
|
endpoint.Network->IpAddresses.insert(endpointAddress);
|
|
}
|
|
}
|
|
|
|
if (initialAddresses != endpoint.Network->IpAddresses)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::ProcessIpAddressChange - reset goal state",
|
|
TraceLoggingValue(endpoint.EndpointId, "endpointId"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
|
|
TraceLoggingValue(initialAddresses.size(), "previous_addresses_size"),
|
|
TraceLoggingValue(endpoint.Network->IpAddresses.size(), "updated_addresses_size"));
|
|
|
|
m_inMirroredGoalState.ResetEvent();
|
|
m_connectivityTelemetry.UpdateTimer();
|
|
|
|
for (const auto& address : initialAddresses)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::ProcessIpAddressChange [previous]",
|
|
TraceLoggingValue(endpoint.EndpointId, "endpointId"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
|
|
TraceLoggingValue(address.AddressString.c_str(), "address"),
|
|
TraceLoggingValue(address.PrefixLength, "prefixLength"));
|
|
}
|
|
for (const auto& address : endpoint.Network->IpAddresses)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::ProcessIpAddressChange [updated]",
|
|
TraceLoggingValue(endpoint.EndpointId, "endpointId"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
|
|
TraceLoggingValue(address.AddressString.c_str(), "address"),
|
|
TraceLoggingValue(address.PrefixLength, "prefixLength"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_Requires_lock_held_(m_networkLock)
|
|
void wsl::core::networking::WslMirroredNetworkManager::ProcessRouteChange()
|
|
{
|
|
wsl::core::networking::unique_address_table addressTable{};
|
|
wsl::core::networking::unique_forward_table routeTable{};
|
|
THROW_IF_WIN32_ERROR(GetIpForwardTable2(AF_UNSPEC, &routeTable));
|
|
|
|
for (auto& endpoint : m_networkEndpoints)
|
|
{
|
|
const auto initialRoutes = endpoint.Network->Routes;
|
|
endpoint.Network->Routes.clear();
|
|
|
|
// if the interface isn't connected, ensure we always track zero routes
|
|
// Windows can have routes assigned on disconnected interfaces, Linux cannot
|
|
if (!endpoint.Network->IsConnected)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Gather endpoint address prefixes and raw address
|
|
std::unordered_set<std::wstring> addressPrefixes{};
|
|
std::unordered_set<std::wstring> addresses{};
|
|
std::unordered_set<std::wstring> ipv4broadcastAddresses{};
|
|
|
|
for (const auto& endpointAddress : endpoint.Network->IpAddresses)
|
|
{
|
|
addresses.insert(endpointAddress.AddressString);
|
|
|
|
auto addressPrefix = endpointAddress.GetPrefix();
|
|
WI_ASSERT(!addressPrefix.empty());
|
|
if (!addressPrefix.empty())
|
|
{
|
|
addressPrefixes.insert(std::move(addressPrefix));
|
|
}
|
|
|
|
if (endpointAddress.Address.si_family == AF_INET)
|
|
{
|
|
auto v4BroadcastMaskAddress = endpointAddress.GetIpv4BroadcastMask();
|
|
WI_ASSERT(!v4BroadcastMaskAddress.empty());
|
|
if (!v4BroadcastMaskAddress.empty())
|
|
{
|
|
ipv4broadcastAddresses.emplace(std::move(v4BroadcastMaskAddress));
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const auto& route : wil::make_range(routeTable.get()->Table, routeTable.get()->NumEntries))
|
|
{
|
|
if (route.InterfaceIndex == endpoint.Network->InterfaceIndex)
|
|
{
|
|
auto endpointRoute = EndpointRoute(route);
|
|
|
|
endpointRoute.IsAutoGeneratedPrefixRoute =
|
|
endpointRoute.IsNextHopOnlink() && addressPrefixes.contains(endpointRoute.GetFullDestinationPrefix());
|
|
|
|
// Ignore host IPv4 routes, e.g. 192.168.5.2/32 -> 0.0.0.0
|
|
if (addresses.contains(endpointRoute.DestinationPrefixString))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// ignore host routes for deprecated addresses
|
|
// the address will not be in the 'addresses' variable above since it's deprecated
|
|
// e.g. a route 2001:0:d5b:9458:1ceb:518b:7c94:609e/128, but the matching local IP address is deprecated
|
|
bool shouldIgnoreUnicastAddressRoute = false;
|
|
if (endpointRoute.IsUnicastAddressRoute())
|
|
{
|
|
if (!addressTable)
|
|
{
|
|
THROW_IF_WIN32_ERROR(GetUnicastIpAddressTable(AF_UNSPEC, &addressTable));
|
|
}
|
|
// find the address matching this destination prefix
|
|
for (const auto& address : wil::make_range(addressTable.get()->Table, addressTable.get()->NumEntries))
|
|
{
|
|
if (address.InterfaceIndex != endpoint.Network->InterfaceIndex)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const auto endpointAddress = EndpointIpAddress(address);
|
|
if (endpointAddress.Address == route.DestinationPrefix.Prefix)
|
|
{
|
|
if (!endpointAddress.IsPreferred())
|
|
{
|
|
shouldIgnoreUnicastAddressRoute = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (shouldIgnoreUnicastAddressRoute)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (endpointRoute.DestinationPrefix.Prefix.si_family == AF_INET)
|
|
{
|
|
if (endpoint.Network->DisableIpv4DefaultRoutes && endpointRoute.IsDefault())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const auto addressType =
|
|
Ipv4AddressType(reinterpret_cast<const UCHAR*>(&endpointRoute.DestinationPrefix.Prefix.Ipv4.sin_addr));
|
|
if (addressType != NlatUnspecified && addressType != NlatUnicast)
|
|
{
|
|
// ignore broadcast and multicast routes - Linux doesn't seem to create those like Windows
|
|
continue;
|
|
}
|
|
|
|
if (ipv4broadcastAddresses.contains(endpointRoute.DestinationPrefixString))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else if (endpointRoute.DestinationPrefix.Prefix.si_family == AF_INET6)
|
|
{
|
|
if (endpoint.Network->DisableIpv6DefaultRoutes && endpointRoute.IsDefault())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const auto addressType =
|
|
Ipv6AddressType(reinterpret_cast<const UCHAR*>(&endpointRoute.DestinationPrefix.Prefix.Ipv6.sin6_addr));
|
|
if (addressType != NlatUnspecified && addressType != NlatUnicast)
|
|
{
|
|
// ignore broadcast and multicast routes - Linux doesn't seem to create those like Windows
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// update the route metric for Linux - which to be equivalent to Windows must be the sum of the interface metric and route metric
|
|
endpointRoute.Metric += (endpointRoute.Family == AF_INET) ? endpoint.Network->IPv4InterfaceMetric.value_or(0)
|
|
: endpoint.Network->IPv6InterfaceMetric.value_or(0);
|
|
if (endpointRoute.Metric > UINT16_MAX)
|
|
{
|
|
endpointRoute.Metric = UINT16_MAX;
|
|
}
|
|
endpoint.Network->Routes.insert(endpointRoute);
|
|
}
|
|
}
|
|
|
|
// Linux requires that there's an onlink route for any route with a NextHop address that's not all-zeros (on-link)
|
|
// "normal" network deployments with Windows creates an address prefix route that includes that next hop
|
|
// but some deployments, like some VPNs, do not include a prefix route that includes the nexthop
|
|
// While that works in Windows (all nexthop addresses in a route *must* be on-link), it won't work in Linux
|
|
// thus we must guarantee an onlink route for all routes with a non-zero nexthop
|
|
std::vector<EndpointRoute> newRoutes;
|
|
for (const auto& route : endpoint.Network->Routes)
|
|
{
|
|
if (!route.IsNextHopOnlink())
|
|
{
|
|
EndpointRoute newRoute;
|
|
newRoute.Family = route.Family;
|
|
newRoute.Metric = route.Metric;
|
|
newRoute.SitePrefixLength = route.GetMaxPrefixLength();
|
|
|
|
// update the destination prefix to the nexthop address /32 (for ipv4) or /128 (for ipv6)
|
|
newRoute.DestinationPrefix.Prefix = route.NextHop;
|
|
newRoute.DestinationPrefix.PrefixLength = route.GetMaxPrefixLength();
|
|
newRoute.DestinationPrefixString = windows::common::string::SockAddrInetToWstring(newRoute.DestinationPrefix.Prefix);
|
|
|
|
// update the destination prefix to be all zeros (on-link)
|
|
ZeroMemory(&newRoute.NextHop, sizeof newRoute.NextHop);
|
|
newRoute.NextHop.si_family = route.NextHop.si_family;
|
|
newRoute.NextHopString = windows::common::string::SockAddrInetToWstring(newRoute.NextHop);
|
|
|
|
// force a copy so the route strings are re-calculated in the new EndpointRoute object
|
|
newRoutes.emplace_back(std::move(newRoute));
|
|
}
|
|
}
|
|
for (const auto& route : newRoutes)
|
|
{
|
|
endpoint.Network->Routes.insert(route);
|
|
}
|
|
|
|
if (initialRoutes != endpoint.Network->Routes)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::ProcessRouteChange - reset goal state",
|
|
TraceLoggingValue(endpoint.EndpointId, "endpointId"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
|
|
TraceLoggingValue(initialRoutes.size(), "previous_routes_size"),
|
|
TraceLoggingValue(endpoint.Network->Routes.size(), "updated_routes_size"));
|
|
|
|
m_inMirroredGoalState.ResetEvent();
|
|
m_connectivityTelemetry.UpdateTimer();
|
|
|
|
for (const auto& route : initialRoutes)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::ProcessRouteChange [previous]",
|
|
TraceLoggingValue(endpoint.EndpointId, "endpointId"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
|
|
TraceLoggingValue(route.Metric, "metric"),
|
|
TraceLoggingValue(route.NextHopString.c_str(), "nextHop"),
|
|
TraceLoggingValue(route.DestinationPrefixString.c_str(), "destinationPrefix"),
|
|
TraceLoggingValue(route.DestinationPrefix.PrefixLength, "destinationPrefixLength"));
|
|
}
|
|
for (const auto& route : endpoint.Network->Routes)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::ProcessRouteChange [updated]",
|
|
TraceLoggingValue(endpoint.EndpointId, "endpointId"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
|
|
TraceLoggingValue(route.Metric, "metric"),
|
|
TraceLoggingValue(route.NextHopString.c_str(), "nextHop"),
|
|
TraceLoggingValue(route.DestinationPrefixString.c_str(), "destinationPrefix"),
|
|
TraceLoggingValue(route.DestinationPrefix.PrefixLength, "destinationPrefixLength"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_Requires_lock_held_(m_networkLock)
|
|
void wsl::core::networking::WslMirroredNetworkManager::ProcessDNSChange()
|
|
{
|
|
const auto initialDnsInfo = m_dnsInfo;
|
|
|
|
if (m_vmConfig.EnableDnsTunneling)
|
|
{
|
|
m_dnsInfo = wsl::core::networking::HostDnsInfo::GetDnsTunnelingSettings(m_dnsTunnelingIpAddress);
|
|
}
|
|
else
|
|
{
|
|
m_hostDnsInfo.UpdateNetworkInformation();
|
|
m_dnsInfo = m_hostDnsInfo.GetDnsSettings(
|
|
wsl::core::networking::DnsSettingsFlags::IncludeVpn | wsl::core::networking::DnsSettingsFlags::IncludeIpv6Servers |
|
|
wsl::core::networking::DnsSettingsFlags::IncludeAllSuffixes);
|
|
}
|
|
|
|
if (initialDnsInfo != m_dnsInfo)
|
|
{
|
|
WSL_LOG("WslMirroredNetworkManager::ProcessDNSChange - reset goal state");
|
|
m_inMirroredGoalState.ResetEvent();
|
|
m_connectivityTelemetry.UpdateTimer();
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::ProcessDNSChange [previous]",
|
|
TraceLoggingValue(wsl::shared::string::Join(initialDnsInfo.Domains, ',').c_str(), "domainList"),
|
|
TraceLoggingValue(wsl::shared::string::Join(initialDnsInfo.Servers, ',').c_str(), "dnsServerList"));
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::ProcessDNSChange [updated]",
|
|
TraceLoggingValue(wsl::shared::string::Join(m_dnsInfo.Domains, ',').c_str(), "domainList"),
|
|
TraceLoggingValue(wsl::shared::string::Join(m_dnsInfo.Servers, ',').c_str(), "dnsServerList"));
|
|
}
|
|
}
|
|
|
|
_Requires_lock_held_(m_networkLock)
|
|
void wsl::core::networking::WslMirroredNetworkManager::ProcessInterfaceChange()
|
|
{
|
|
wsl::core::networking::unique_interface_table interfaceTable{};
|
|
THROW_IF_WIN32_ERROR(::GetIpInterfaceTable(AF_UNSPEC, &interfaceTable));
|
|
|
|
for (auto& endpoint : m_networkEndpoints)
|
|
{
|
|
const auto originalIPv4DisableDefaultRoutes = endpoint.Network->DisableIpv4DefaultRoutes;
|
|
const auto originalIPv6DisableDefaultRoutes = endpoint.Network->DisableIpv6DefaultRoutes;
|
|
const auto originallyConnected = endpoint.Network->IsConnected;
|
|
const auto originalMinimumMtu = endpoint.Network->GetEffectiveMtu();
|
|
const auto originalMinimumMetric = endpoint.Network->GetMinimumMetric();
|
|
|
|
endpoint.Network->IsConnected = false;
|
|
|
|
auto interfaceFoundCount = 0;
|
|
for (const auto& ipInterface : wil::make_range(interfaceTable.get()->Table, interfaceTable.get()->NumEntries))
|
|
{
|
|
if (ipInterface.InterfaceIndex != endpoint.Network->InterfaceIndex ||
|
|
(ipInterface.Family != AF_INET && ipInterface.Family != AF_INET6))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Endpoint is marked as connected if either IPv4 or IPv6 interface is connected
|
|
endpoint.Network->IsConnected = endpoint.Network->IsConnected || !!ipInterface.Connected;
|
|
|
|
if (ipInterface.Family == AF_INET)
|
|
{
|
|
endpoint.Network->IPv4InterfaceMtu = ipInterface.NlMtu;
|
|
endpoint.Network->IPv4InterfaceMetric = ipInterface.Metric;
|
|
endpoint.Network->DisableIpv4DefaultRoutes = ipInterface.DisableDefaultRoutes;
|
|
}
|
|
else
|
|
{
|
|
endpoint.Network->IPv6InterfaceMtu = ipInterface.NlMtu;
|
|
endpoint.Network->IPv6InterfaceMetric = ipInterface.Metric;
|
|
endpoint.Network->DisableIpv6DefaultRoutes = ipInterface.DisableDefaultRoutes;
|
|
}
|
|
|
|
++interfaceFoundCount;
|
|
if (interfaceFoundCount > 1)
|
|
{
|
|
// we already found both v4 and v6
|
|
break;
|
|
}
|
|
}
|
|
|
|
const auto disableDefaultRoutesUpdated = originalIPv4DisableDefaultRoutes != endpoint.Network->DisableIpv4DefaultRoutes ||
|
|
originalIPv6DisableDefaultRoutes != endpoint.Network->DisableIpv6DefaultRoutes;
|
|
const auto connectedStateUpdated = originallyConnected != endpoint.Network->IsConnected;
|
|
const auto minimumMtu = endpoint.Network->GetEffectiveMtu();
|
|
const auto mtuUpdated = originalMinimumMtu != minimumMtu;
|
|
const auto minimumMetric = endpoint.Network->GetMinimumMetric();
|
|
const auto metricUpdate = originalMinimumMetric != minimumMetric;
|
|
|
|
endpoint.Network->PendingIPInterfaceUpdate |= connectedStateUpdated || mtuUpdated || metricUpdate;
|
|
|
|
if (disableDefaultRoutesUpdated || connectedStateUpdated || mtuUpdated || metricUpdate)
|
|
{
|
|
// we want to trace when disableDefaultRoutesUpdated, but that won't trigger resetting the goal-state
|
|
// if disableDefaultRoutesUpdated affects routes, then ProcessRouteChange will reset the goal-state accordingly
|
|
// but we do want to trace when disableDefaultRoutes get updated - to greatly help debugging
|
|
if (connectedStateUpdated || mtuUpdated || metricUpdate)
|
|
{
|
|
WSL_LOG("WslMirroredNetworkManager::ProcessInterfaceChange - reset goal state");
|
|
m_inMirroredGoalState.ResetEvent();
|
|
m_connectivityTelemetry.UpdateTimer();
|
|
}
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::ProcessInterfaceChange [previous]",
|
|
TraceLoggingValue(endpoint.EndpointId, "endpointId"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
|
|
TraceLoggingValue(originallyConnected, "isConnected"),
|
|
TraceLoggingValue(originalMinimumMtu, "EffectiveMtu"),
|
|
TraceLoggingValue(originalMinimumMetric, "MinimumMetric"),
|
|
TraceLoggingValue(originalIPv4DisableDefaultRoutes, "disableIpv4DefaultRoutes"),
|
|
TraceLoggingValue(originalIPv6DisableDefaultRoutes, "disableIpv6DefaultRoutes"));
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::ProcessInterfaceChange [updated]",
|
|
TraceLoggingValue(endpoint.EndpointId, "endpointId"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
|
|
TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
|
|
TraceLoggingValue(endpoint.Network->IPv4InterfaceMtu, "ipv4InterfaceMtu"),
|
|
TraceLoggingValue(endpoint.Network->IPv6InterfaceMtu, "ipv6InterfaceMtu"),
|
|
TraceLoggingValue(endpoint.Network->IPv4InterfaceMetric.value_or(0xffffffff), "IPv4InterfaceMetric"),
|
|
TraceLoggingValue(endpoint.Network->IPv6InterfaceMetric.value_or(0xffffffff), "IPv6InterfaceMetric"),
|
|
TraceLoggingValue(endpoint.Network->DisableIpv4DefaultRoutes, "disableIpv4DefaultRoutes"),
|
|
TraceLoggingValue(endpoint.Network->DisableIpv6DefaultRoutes, "disableIpv6DefaultRoutes"));
|
|
}
|
|
}
|
|
}
|
|
|
|
wsl::core::networking::WslMirroredNetworkManager::WslMirroredNetworkManager(
|
|
HCS_SYSTEM hcsSystem,
|
|
const Config& config,
|
|
GnsMessageCallbackWithCallbackResult&& GnsMessageCallbackWithCallbackResult,
|
|
AddNetworkEndpointCallback&& addNetworkEndpointCallback,
|
|
const std::pair<uint16_t, uint16_t>& ephemeralPortRange) :
|
|
m_callbackForGnsMessage(std::move(GnsMessageCallbackWithCallbackResult)),
|
|
m_addNetworkEndpointCallback(std::move(addNetworkEndpointCallback)),
|
|
m_hcsSystem{hcsSystem},
|
|
m_vmConfig{config},
|
|
m_ephemeralPortRange(ephemeralPortRange),
|
|
m_state(State::Starting)
|
|
{
|
|
// ensure the MTA apartment stays alive for the lifetime of this object in this process
|
|
// we do not want to risk COM unloading / reloading when we need to make our WinRT API calls
|
|
// which by default will be in the MTA
|
|
LOG_IF_FAILED(CoIncrementMTAUsage(&m_mtaCookie));
|
|
|
|
// locking in the c'tor in case any of the below callbacks fire before this object is fully constructed
|
|
const auto lock = m_networkLock.lock_exclusive();
|
|
|
|
// keep the WinRT DLL loaded for the lifetime of this instance. we instantiate it repeatedly,
|
|
// and today we are loading and unloading 7 dll's over and over again - each time we call it.
|
|
// this also circumvents many performance optimizations we made with our WinRT API
|
|
const auto roInit = wil::RoInitialize();
|
|
m_networkInformationStatics = wil::GetActivationFactory<ABI::Windows::Networking::Connectivity::INetworkInformationStatics>(
|
|
RuntimeClass_Windows_Networking_Connectivity_NetworkInformation);
|
|
|
|
m_netListManager = wil::CoCreateInstance<NetworkListManager, INetworkListManager>();
|
|
// create an event sink for NLM Network change notifications, then register (Advise) with NLM
|
|
m_netListManagerEventSink = wil::com_ptr<INetworkEvents>(Microsoft::WRL::Make<PublicNLMSink>(this));
|
|
// INetworkListManager is actually an inproc COM API - it just calls private COM APIs which are hosted in a service
|
|
m_netListManagerAdviseHandler.AdviseInProcObject<INetworkEvents>(m_netListManager, m_netListManagerEventSink.get());
|
|
|
|
// Subscribe for network change notifications. This is done before
|
|
// obtaining the initial list of networks to connect to, in order to
|
|
// avoid a race condition between the initial enumeration and any network
|
|
// changes that may be occurring at the same time. The subscription will
|
|
// receive network change events, but will not be able to react to them
|
|
// the lock is released.
|
|
m_hcnCallback = windows::common::hcs::RegisterServiceCallback(HcnCallback, this);
|
|
|
|
// Create the timer used to retry the HNS service connection.
|
|
m_retryHcnServiceConnectionTimer.reset(CreateThreadpoolTimer(HcnServiceConnectionTimerCallback, this, nullptr));
|
|
THROW_IF_NULL_ALLOC(m_retryHcnServiceConnectionTimer);
|
|
|
|
// Create the timer used to retry syncing pending IP state with Linux.
|
|
m_retryLinuxIpStateSyncTimer.reset(CreateThreadpoolTimer(RetryLinuxIpStateSyncTimerCallback, this, nullptr));
|
|
THROW_IF_NULL_ALLOC(m_retryLinuxIpStateSyncTimer);
|
|
|
|
m_debounceUpdateAllEndpointsDefaultTimer.reset(CreateThreadpoolTimer(DebounceUpdateAllEndpointsDefaultTimerFired, this, nullptr));
|
|
THROW_IF_NULL_ALLOC(m_debounceUpdateAllEndpointsDefaultTimer);
|
|
|
|
m_debounceCreateEndpointFailureTimer.reset(CreateThreadpoolTimer(DebounceCreateEndpointFailureTimerFired, this, nullptr));
|
|
THROW_IF_NULL_ALLOC(m_debounceCreateEndpointFailureTimer);
|
|
|
|
// Populate the initial list of networks. The list will then be kept
|
|
// up to date by the above subscription notifications.
|
|
for (const auto& networkId : EnumerateMirroredNetworks())
|
|
{
|
|
// Must call back through MirroredNetworking to create a new Endpoint
|
|
// note that the callback will not block - it just queues the work in MirroredNetworking
|
|
LOG_IF_FAILED(AddNetwork(networkId));
|
|
}
|
|
|
|
// once HNS has started creating networks, start our telemetry timer
|
|
if (config.EnableTelemetry && !WslTraceLoggingShouldDisableTelemetry())
|
|
{
|
|
m_connectivityTelemetry.StartTimer([&](NLM_CONNECTIVITY hostConnectivity, uint32_t telemetryCounter) {
|
|
TelemetryConnectionCallback(hostConnectivity, telemetryCounter);
|
|
});
|
|
}
|
|
|
|
if (config.DnsTunnelingIpAddress.has_value())
|
|
{
|
|
m_dnsTunnelingIpAddress = wsl::windows::common::string::IntegerIpv4ToWstring(config.DnsTunnelingIpAddress.value());
|
|
}
|
|
|
|
m_state = State::Started;
|
|
}
|
|
|
|
wsl::core::networking::WslMirroredNetworkManager::~WslMirroredNetworkManager() noexcept
|
|
{
|
|
Stop();
|
|
}
|
|
|
|
wsl::core::networking::WslMirroredNetworkManager::HnsStatus wsl::core::networking::WslMirroredNetworkManager::Stop() noexcept
|
|
{
|
|
HnsStatus returnStatus{};
|
|
try
|
|
{
|
|
// scope to the lock to flip the bit that we are stopping
|
|
{
|
|
const auto lock = m_networkLock.lock_exclusive();
|
|
m_state = State::Stopped;
|
|
returnStatus = m_latestHnsStatus;
|
|
}
|
|
|
|
// must set state first so all other threads won't make forward progress
|
|
// since we are about to stop all timers and callbacks
|
|
// which must be stopped not holding our lock
|
|
|
|
// Next stop the telemetry timer which could queue work to linux (through m_gnsCallbackQueue)
|
|
m_connectivityTelemetry.Reset();
|
|
|
|
// Next stop the timer which could reset the hcnCallback
|
|
m_retryHcnServiceConnectionTimer.reset();
|
|
|
|
// Next stop the Hcn callback, which could add/remove networks
|
|
m_hcnCallback.reset();
|
|
|
|
m_debounceUpdateAllEndpointsDefaultTimer.reset();
|
|
|
|
m_debounceCreateEndpointFailureTimer.reset();
|
|
|
|
// Stop the linux ip state sync timer
|
|
m_retryLinuxIpStateSyncTimer.reset();
|
|
|
|
// canceling the callback queue only after stopping all sources that could queue a callback
|
|
m_gnsCallbackQueue.cancel();
|
|
m_hnsQueue.cancel();
|
|
|
|
// all of the above must be done outside holding a lock to avoid deadlocks
|
|
const auto lock = m_networkLock.lock_exclusive();
|
|
m_networkEndpoints.clear();
|
|
}
|
|
CATCH_LOG()
|
|
|
|
return returnStatus;
|
|
}
|
|
|
|
void wsl::core::networking::WslMirroredNetworkManager::DebounceUpdateAllEndpointsDefaultTimerFired(
|
|
_Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_TIMER)
|
|
try
|
|
{
|
|
auto* const instance = static_cast<WslMirroredNetworkManager*>(Context);
|
|
|
|
const auto lock = instance->m_networkLock.lock_exclusive();
|
|
instance->m_IsDebounceUpdateAllEndpointsDefaultTimerSet = false;
|
|
if (instance->m_state == State::Stopped)
|
|
{
|
|
return;
|
|
}
|
|
|
|
instance->UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, "DebounceUpdateAllEndpointsDefaultTimerFired");
|
|
}
|
|
CATCH_LOG()
|
|
|
|
void wsl::core::networking::WslMirroredNetworkManager::DebounceCreateEndpointFailureTimerFired(
|
|
_Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_TIMER)
|
|
try
|
|
{
|
|
auto* const instance = static_cast<WslMirroredNetworkManager*>(Context);
|
|
|
|
const auto lock = instance->m_networkLock.lock_exclusive();
|
|
if (instance->m_state == State::Stopped)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!instance->m_failedEndpointProperties.empty())
|
|
{
|
|
// AddEndpointImpl will update m_failedEndpointProperties if any re-attempts to add the endpoint fail
|
|
// thus we must first move everything out
|
|
auto failedEndpointProperties = std::move(instance->m_failedEndpointProperties);
|
|
instance->m_failedEndpointProperties.clear();
|
|
for (auto& endpointProperties : failedEndpointProperties)
|
|
{
|
|
instance->AddEndpointImpl(std::move(endpointProperties));
|
|
}
|
|
}
|
|
}
|
|
CATCH_LOG()
|
|
|
|
_Requires_lock_held_(m_networkLock)
|
|
std::vector<GUID> wsl::core::networking::WslMirroredNetworkManager::EnumerateMirroredNetworks() const noexcept
|
|
try
|
|
{
|
|
WI_ASSERT(m_state == State::Started || m_state == State::Starting);
|
|
|
|
return EnumerateMirroredNetworksAndHyperVFirewall(m_vmConfig.FirewallConfig.Enabled());
|
|
}
|
|
catch (...)
|
|
{
|
|
LOG_CAUGHT_EXCEPTION();
|
|
return {};
|
|
}
|
|
|
|
_Requires_lock_held_(m_networkLock)
|
|
_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::AddNetwork(const GUID& networkId) noexcept
|
|
try
|
|
{
|
|
WSL_LOG("WslMirroredNetworkManager::AddNetwork", TraceLoggingValue(networkId, "networkId"));
|
|
|
|
// Inform the parent class to create a new endpoint object which we can then connect into the container
|
|
m_hnsQueue.submit([this, networkId] { m_addNetworkEndpointCallback(networkId); });
|
|
|
|
return S_OK;
|
|
}
|
|
CATCH_RETURN()
|
|
|
|
_Requires_lock_held_(m_networkLock)
|
|
_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::RemoveNetwork(const GUID& networkId) noexcept
|
|
try
|
|
{
|
|
WSL_LOG("WslMirroredNetworkManager::RemoveNetwork", TraceLoggingValue(networkId, "networkId"));
|
|
|
|
const auto foundEndpoint =
|
|
std::ranges::find_if(m_networkEndpoints, [&](const auto& endpoint) { return endpoint.NetworkId == networkId; });
|
|
if (foundEndpoint == std::end(m_networkEndpoints))
|
|
{
|
|
WSL_LOG("WslMirroredNetworkManager::RemoveNetwork - Network not found", TraceLoggingValue(networkId, "networkId"));
|
|
return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
|
|
}
|
|
|
|
// RemoveEndpoint will remove this endpoint from m_networkEndpoints
|
|
return RemoveEndpoint(foundEndpoint->EndpointId);
|
|
}
|
|
CATCH_RETURN()
|
|
|
|
void __stdcall wsl::core::networking::WslMirroredNetworkManager::RetryLinuxIpStateSyncTimerCallback(
|
|
_Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_TIMER) noexcept
|
|
{
|
|
auto* const manager = static_cast<WslMirroredNetworkManager*>(Context);
|
|
const auto lock = manager->m_networkLock.lock_exclusive();
|
|
if (manager->m_state == State::Stopped)
|
|
{
|
|
return;
|
|
}
|
|
|
|
manager->UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, "RetryLinuxIpStateSyncTimerCallback");
|
|
}
|
|
|
|
_Requires_lock_held_(m_networkLock)
|
|
_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::SendAddressRequestToGns(
|
|
const NetworkEndpoint& endpoint, const TrackedIpAddress& address, hns::ModifyRequestType requestType) noexcept
|
|
try
|
|
{
|
|
hns::ModifyGuestEndpointSettingRequest<hns::IPAddress> modifyRequest;
|
|
modifyRequest.ResourceType = hns::GuestEndpointResourceType::IPAddress;
|
|
modifyRequest.RequestType = requestType;
|
|
modifyRequest.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(endpoint.InterfaceGuid);
|
|
modifyRequest.Settings = address.ConvertToHnsSettingsMsg();
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SendAddressRequestToGns",
|
|
TraceLoggingValue("ModifyGuestDeviceSettingRequest - set address [queued]", "GnsMessage"),
|
|
TraceLoggingValue(HnsModifyRequestTypeToString(requestType).c_str(), "requestType"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(address.Address.AddressString.c_str(), "ipAddress"),
|
|
TraceLoggingValue(address.Address.PrefixLength, "prefixLength"),
|
|
TraceLoggingValue(address.Address.IsPreferred(), "isPreferred"));
|
|
|
|
int linuxResultCode{};
|
|
// can safely capture by ref since we are waiting
|
|
const auto hr = m_gnsCallbackQueue.submit_and_wait([&] {
|
|
return m_callbackForGnsMessage(LxGnsMessageDeviceSettingRequest, ToJsonW(modifyRequest), GnsCallbackFlags::Wait, &linuxResultCode);
|
|
});
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SendAddressRequestToGns",
|
|
TraceLoggingValue("ModifyGuestDeviceSettingRequest - set address [completed]", "GnsMessage"),
|
|
TraceLoggingHResult(hr, "hr"),
|
|
TraceLoggingValue(linuxResultCode, "linuxResultCode"));
|
|
|
|
address.SyncRetryCount = (address.SyncRetryCount > 0) ? address.SyncRetryCount - 1 : 0;
|
|
return hr;
|
|
}
|
|
CATCH_RETURN()
|
|
|
|
_Requires_lock_held_(m_networkLock)
|
|
_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::SendRouteRequestToGns(
|
|
const NetworkEndpoint& endpoint, const TrackedRoute& route, hns::ModifyRequestType requestType) noexcept
|
|
try
|
|
{
|
|
hns::ModifyGuestEndpointSettingRequest<hns::Route> modifyRequest;
|
|
modifyRequest.ResourceType = hns::GuestEndpointResourceType::Route;
|
|
modifyRequest.RequestType = requestType;
|
|
modifyRequest.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(endpoint.InterfaceGuid);
|
|
modifyRequest.Settings = route.ConvertToHnsSettingsMsg();
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SendRouteRequestToGns",
|
|
TraceLoggingValue("ModifyGuestDeviceSettingRequest : set route [queued]", "GnsMessage"),
|
|
TraceLoggingValue(HnsModifyRequestTypeToString(requestType).c_str(), "requestType"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(route.Route.DestinationPrefixString.c_str(), "destinationPrefix"),
|
|
TraceLoggingValue(route.Route.DestinationPrefix.PrefixLength, "prefixLength"),
|
|
TraceLoggingValue(route.Route.NextHopString.c_str(), "nextHop"),
|
|
TraceLoggingValue(route.Route.Metric, "metric"));
|
|
|
|
int linuxResultCode{};
|
|
// can safely capture by ref since we are waiting
|
|
const auto hr = m_gnsCallbackQueue.submit_and_wait([&] {
|
|
return m_callbackForGnsMessage(LxGnsMessageDeviceSettingRequest, ToJsonW(modifyRequest), GnsCallbackFlags::Wait, &linuxResultCode);
|
|
});
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SendRouteRequestToGns",
|
|
TraceLoggingValue("ModifyGuestDeviceSettingRequest : set route [completed]", "GnsMessage"),
|
|
TraceLoggingHResult(hr, "hr"),
|
|
TraceLoggingValue(linuxResultCode, "linuxResultCode"));
|
|
|
|
route.SyncRetryCount = (route.SyncRetryCount > 0) ? route.SyncRetryCount - 1 : 0;
|
|
return hr;
|
|
}
|
|
CATCH_RETURN()
|
|
|
|
_Requires_lock_held_(m_networkLock)
|
|
_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::SendLoopbackRequestToGns(
|
|
const NetworkEndpoint& endpoint, const TrackedIpAddress& address, hns::OperationType operation) noexcept
|
|
try
|
|
{
|
|
hns::LoopbackRoutesRequest loopbackRequest;
|
|
loopbackRequest.operation = operation;
|
|
loopbackRequest.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(endpoint.InterfaceGuid);
|
|
loopbackRequest.family = address.Address.Address.si_family;
|
|
loopbackRequest.ipAddress = address.Address.AddressString;
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SendLoopbackRequestToGns",
|
|
TraceLoggingValue("LoopbackRoutesRequest [queued]", "GnsMessage"),
|
|
TraceLoggingValue(JsonEnumToString(operation).c_str(), "requestType"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(address.Address.AddressString.c_str(), "ipAddress"));
|
|
|
|
int linuxResultCode{};
|
|
// can safely capture by ref since we are waiting
|
|
const auto hr = m_gnsCallbackQueue.submit_and_wait([&]() {
|
|
return m_callbackForGnsMessage(LxGnsMessageLoopbackRoutesRequest, ToJsonW(loopbackRequest), GnsCallbackFlags::Wait, &linuxResultCode);
|
|
});
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SendLoopbackRequestToGns",
|
|
TraceLoggingValue("LoopbackRoutesRequest [completed]", "GnsMessage"),
|
|
TraceLoggingHResult(hr, "hr"),
|
|
TraceLoggingValue(linuxResultCode, "linuxResultCode"));
|
|
|
|
return hr;
|
|
}
|
|
CATCH_RETURN()
|
|
|
|
static hns::DNS ConvertDnsInfoToHnsSettingsMsg(const wsl::core::networking::DnsInfo& dnsInfo)
|
|
{
|
|
hns::DNS dnsSettings{};
|
|
dnsSettings.Options = LX_INIT_RESOLVCONF_FULL_HEADER;
|
|
|
|
dnsSettings.ServerList = wsl::shared::string::MultiByteToWide(wsl::shared::string::Join(dnsInfo.Servers, ','));
|
|
dnsSettings.Search = wsl::shared::string::MultiByteToWide(wsl::shared::string::Join(dnsInfo.Domains, ','));
|
|
|
|
return dnsSettings;
|
|
}
|
|
|
|
_Requires_lock_held_(m_networkLock)
|
|
_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::SendDnsRequestToGns(
|
|
const NetworkEndpoint& endpoint, const DnsInfo& dnsInfo, hns::ModifyRequestType requestType) noexcept
|
|
try
|
|
{
|
|
hns::ModifyGuestEndpointSettingRequest<hns::DNS> modifyRequest;
|
|
modifyRequest.ResourceType = hns::GuestEndpointResourceType::DNS;
|
|
modifyRequest.RequestType = requestType;
|
|
modifyRequest.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(endpoint.InterfaceGuid);
|
|
modifyRequest.Settings = ConvertDnsInfoToHnsSettingsMsg(dnsInfo);
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SendDnsRequestToGns",
|
|
TraceLoggingValue("ModifyGuestDeviceSettingRequest : set DNS [queued]", "GnsMessage"),
|
|
TraceLoggingValue(HnsModifyRequestTypeToString(requestType).c_str(), "requestType"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(m_vmConfig.EnableDnsTunneling, "m_vmConfig.EnableDnsTunneling"),
|
|
TraceLoggingValue(wsl::shared::string::Join(dnsInfo.Servers, ',').c_str(), "server list"),
|
|
TraceLoggingValue(wsl::shared::string::Join(dnsInfo.Domains, ',').c_str(), "suffix list"));
|
|
|
|
int linuxResultCode{};
|
|
// can safely capture by ref since we are waiting
|
|
const auto hr = m_gnsCallbackQueue.submit_and_wait([&] {
|
|
return m_callbackForGnsMessage(LxGnsMessageDeviceSettingRequest, ToJsonW(modifyRequest), GnsCallbackFlags::Wait, &linuxResultCode);
|
|
});
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SendDnsRequestToGns",
|
|
TraceLoggingValue("ModifyGuestDeviceSettingRequest : set DNS [completed]", "GnsMessage"),
|
|
TraceLoggingHResult(hr, "hr"),
|
|
TraceLoggingValue(linuxResultCode, "linuxResultCode"));
|
|
|
|
return hr;
|
|
}
|
|
CATCH_RETURN()
|
|
|
|
_Requires_lock_held_(m_networkLock)
|
|
_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::SendInterfaceRequestToGns(const NetworkEndpoint& endpoint) noexcept
|
|
try
|
|
{
|
|
const auto interfaceConnected = endpoint.Network->IsConnected;
|
|
const auto interfaceMtu = endpoint.Network->GetEffectiveMtu();
|
|
const auto interfaceMetric = endpoint.Network->GetMinimumMetric();
|
|
|
|
hns::ModifyGuestEndpointSettingRequest<hns::NetworkInterface> modifyRequest;
|
|
modifyRequest.Settings.Connected = interfaceConnected;
|
|
modifyRequest.Settings.NlMtu = interfaceMtu;
|
|
modifyRequest.Settings.Metric = interfaceMetric;
|
|
modifyRequest.ResourceType = hns::GuestEndpointResourceType::Interface;
|
|
modifyRequest.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(endpoint.InterfaceGuid);
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SendInterfaceRequestToGns",
|
|
TraceLoggingValue("ModifyGuestDeviceSettingRequest : update interface properties [queued]", "GnsMessage"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(interfaceConnected, "connected"),
|
|
TraceLoggingValue(interfaceMtu, "mtu"),
|
|
TraceLoggingValue(interfaceMetric, "metric"));
|
|
|
|
int linuxResultCode{};
|
|
// can safely capture by ref since we are waiting
|
|
const auto hr = m_gnsCallbackQueue.submit_and_wait([&] {
|
|
return m_callbackForGnsMessage(LxGnsMessageModifyGuestDeviceSettingRequest, ToJsonW(modifyRequest), GnsCallbackFlags::Wait, &linuxResultCode);
|
|
});
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SendInterfaceRequestToGns",
|
|
TraceLoggingValue("ModifyGuestDeviceSettingRequest : update interface properties [completed]", "GnsMessage"),
|
|
TraceLoggingHResult(hr, "hr"),
|
|
TraceLoggingValue(linuxResultCode, "linuxResultCode"));
|
|
|
|
return hr;
|
|
}
|
|
CATCH_RETURN()
|
|
|
|
_Requires_lock_held_(m_networkLock)
|
|
_Check_return_ bool wsl::core::networking::WslMirroredNetworkManager::SyncIpStateWithLinux(NetworkEndpoint& endpoint)
|
|
{
|
|
using hns::GuestEndpointResourceType;
|
|
using hns::IPAddress;
|
|
using hns::Route;
|
|
using TrackedIpStateSyncStatus::PendingAdd;
|
|
using TrackedIpStateSyncStatus::PendingRemoval;
|
|
using TrackedIpStateSyncStatus::PendingUpdate;
|
|
using TrackedIpStateSyncStatus::Synced;
|
|
|
|
bool syncSuccessful = true;
|
|
|
|
if (!endpoint.StateTracking->InitialSyncComplete)
|
|
{
|
|
// Tell GNS that we're ready to start pushing addresses and routes to Linux on this interface.
|
|
hns::InitialIpConfigurationNotification notification{};
|
|
notification.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(endpoint.InterfaceGuid);
|
|
WI_SetAllFlags(
|
|
notification.flags,
|
|
(hns::InitialIpConfigurationNotificationFlags::SkipPrimaryRoutingTableUpdate |
|
|
hns::InitialIpConfigurationNotificationFlags::SkipLoopbackRouteReset));
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SyncIpStateWithLinux",
|
|
TraceLoggingValue("InitialIpConfigurationNotification [queued]", "GnsMessage"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"));
|
|
|
|
int linuxResultCode{};
|
|
// can safely capture by ref since we are waiting
|
|
const auto hr = m_gnsCallbackQueue.submit_and_wait([&] {
|
|
return m_callbackForGnsMessage(
|
|
LxGnsMessageInitialIpConfigurationNotification, ToJsonW(notification), GnsCallbackFlags::Wait, &linuxResultCode);
|
|
});
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SyncIpStateWithLinux",
|
|
TraceLoggingValue("InitialIpConfigurationNotification [completed]", "GnsMessage"),
|
|
TraceLoggingHResult(hr, "hr"),
|
|
TraceLoggingValue(linuxResultCode, "linuxResultCode"));
|
|
}
|
|
|
|
const auto makingIpInterfaceUpdate = endpoint.Network->PendingIPInterfaceUpdate;
|
|
// Linux may delete routes behind us when making interface, address, and route changes
|
|
// will track when to refresh v4 and v6 routes to ensure routes are still present after changes
|
|
// a few customers have seen this when we update temporary v6 addresses, for example
|
|
auto refreshAllRoutes = false;
|
|
|
|
// First: update Linux with any interface updates
|
|
// If IsHidden is set, then also indicate to Linux that the interface should be disconnected
|
|
if (endpoint.Network->PendingIPInterfaceUpdate || endpoint.Network->IsHidden)
|
|
{
|
|
const auto originalConnectValue = endpoint.Network->IsConnected;
|
|
if (endpoint.Network->IsHidden)
|
|
{
|
|
endpoint.Network->IsConnected = false;
|
|
}
|
|
|
|
if (FAILED(SendInterfaceRequestToGns(endpoint)))
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SyncIpStateWithLinux",
|
|
TraceLoggingValue("Failed to update Interface properties", "message"),
|
|
TraceLoggingValue(endpoint.Network->IsConnected, "connected"),
|
|
TraceLoggingValue(endpoint.Network->GetEffectiveMtu(), "mtu"),
|
|
TraceLoggingValue(endpoint.Network->GetMinimumMetric(), "metric"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"));
|
|
|
|
syncSuccessful = false;
|
|
// interfaces are in an unknown state - push route updates in case Linux deleted routes behind us
|
|
refreshAllRoutes = true;
|
|
}
|
|
else
|
|
{
|
|
endpoint.Network->PendingIPInterfaceUpdate = false;
|
|
}
|
|
|
|
endpoint.Network->IsConnected = originalConnectValue;
|
|
if (originalConnectValue)
|
|
{
|
|
// interface potentially just moved from disconnected -> connected
|
|
// push route updates in case Linux deleted routes behind us
|
|
refreshAllRoutes = true;
|
|
}
|
|
}
|
|
|
|
// Second: update Linux with any addresses to remove
|
|
auto addressIt = endpoint.StateTracking->IpAddresses.begin();
|
|
while (addressIt != endpoint.StateTracking->IpAddresses.end())
|
|
{
|
|
auto address = addressIt++;
|
|
|
|
// if the interface is hidden, we need to remove addresses
|
|
// 'continue' to keep the address
|
|
if (!endpoint.Network->IsHidden)
|
|
{
|
|
if (endpoint.Network->IpAddresses.contains(address->Address))
|
|
{
|
|
if (address->SyncStatus == PendingRemoval)
|
|
{
|
|
// This address was slated for removal but still exists on the host. It should be kept around instead.
|
|
// We'll send an update just in case.
|
|
address->SyncStatus = PendingUpdate;
|
|
address->SyncRetryCount = TrackedIpAddress::MaxSyncRetryCount;
|
|
}
|
|
else if (makingIpInterfaceUpdate)
|
|
{
|
|
// if we pushed an interface update, ensure our addresses are up to date
|
|
address->SyncStatus = PendingUpdate;
|
|
address->SyncRetryCount = TrackedIpAddress::MaxSyncRetryCount;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// We found an address that should be removed from the guest.
|
|
|
|
if (address->SyncStatus != PendingRemoval)
|
|
{
|
|
// We've never attempted to remove this address before, so reset the sync retry count.
|
|
address->SyncRetryCount = TrackedIpAddress::MaxSyncRetryCount;
|
|
}
|
|
address->SyncStatus = PendingRemoval;
|
|
|
|
if (m_vmConfig.EnableHostAddressLoopback)
|
|
{
|
|
if (FAILED(SendLoopbackRequestToGns(endpoint, *address, hns::OperationType::Remove)))
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SyncIpStateWithLinux",
|
|
TraceLoggingValue("Failed to remove loopback routes for local address", "message"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
|
|
TraceLoggingValue(address->Address.AddressString.c_str(), "address"),
|
|
TraceLoggingValue(address->Address.PrefixLength, "prefixLength"));
|
|
}
|
|
}
|
|
|
|
if (FAILED(SendAddressRequestToGns(endpoint, *address, hns::ModifyRequestType::Remove)))
|
|
{
|
|
if (address->SyncRetryCount == 0)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SyncIpStateWithLinux",
|
|
TraceLoggingValue(
|
|
"Reached maximum retries to remove an address - we will no longer schedule the retry timer", "message"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
|
|
TraceLoggingValue(address->Address.AddressString.c_str(), "address"),
|
|
TraceLoggingValue(address->Address.PrefixLength, "prefixLength"));
|
|
}
|
|
else
|
|
{
|
|
syncSuccessful = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SyncIpStateWithLinux",
|
|
TraceLoggingValue("Address synced (removed)", "message"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
|
|
TraceLoggingValue(address->Address.AddressString.c_str(), "address"),
|
|
TraceLoggingValue(address->Address.PrefixLength, "prefixLength"));
|
|
endpoint.StateTracking->IpAddresses.erase(address);
|
|
}
|
|
// push route updates in case Linux deleted routes behind us after removing addresses
|
|
refreshAllRoutes = true;
|
|
}
|
|
|
|
// Third: update Linux with any routes to remove
|
|
auto routeIt = endpoint.StateTracking->Routes.begin();
|
|
while (routeIt != endpoint.StateTracking->Routes.end())
|
|
{
|
|
auto route = routeIt++;
|
|
|
|
// if the interface is hidden, we need to remove routes
|
|
// 'continue' to keep the route
|
|
if (!endpoint.Network->IsHidden)
|
|
{
|
|
if (endpoint.Network->Routes.contains(route->Route))
|
|
{
|
|
if (route->SyncStatus == PendingRemoval)
|
|
{
|
|
// This route was slated for removal but still exists on the host. It should be kept around instead.
|
|
// We'll send an update just in case.
|
|
route->SyncStatus = PendingUpdate;
|
|
route->SyncRetryCount = TrackedRoute::MaxSyncRetryCount;
|
|
}
|
|
else if (makingIpInterfaceUpdate)
|
|
{
|
|
// if we pushed an interface update, ensure our routes are up to date
|
|
route->SyncStatus = PendingUpdate;
|
|
route->SyncRetryCount = TrackedRoute::MaxSyncRetryCount;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// We found a route that should be removed from the guest.
|
|
|
|
if (route->SyncStatus != PendingRemoval)
|
|
{
|
|
// We've never attempted to remove this route before, so reset the sync retry count.
|
|
route->SyncRetryCount = TrackedRoute::MaxSyncRetryCount;
|
|
}
|
|
route->SyncStatus = PendingRemoval;
|
|
|
|
if (FAILED(SendRouteRequestToGns(endpoint, *route, hns::ModifyRequestType::Remove)))
|
|
{
|
|
if (route->SyncRetryCount == 0)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SyncIpStateWithLinux",
|
|
TraceLoggingValue(
|
|
"Reached maximum retries to remove a route - we will no longer schedule the retry timer", "message"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
|
|
TraceLoggingValue(route->Route.DestinationPrefixString.c_str(), "destinationPrefix"),
|
|
TraceLoggingValue(route->Route.DestinationPrefix.PrefixLength, "prefixLength"),
|
|
TraceLoggingValue(route->Route.NextHopString.c_str(), "nextHop"),
|
|
TraceLoggingValue(route->Route.Metric, "metric"));
|
|
}
|
|
else
|
|
{
|
|
syncSuccessful = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SyncIpStateWithLinux",
|
|
TraceLoggingValue("Route synced (removed) succeeded", "message"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
|
|
TraceLoggingValue(route->Route.DestinationPrefixString.c_str(), "destinationPrefix"),
|
|
TraceLoggingValue(route->Route.DestinationPrefix.PrefixLength, "prefixLength"),
|
|
TraceLoggingValue(route->Route.NextHopString.c_str(), "nextHop"),
|
|
TraceLoggingValue(route->Route.Metric, "metric"));
|
|
endpoint.StateTracking->Routes.erase(route);
|
|
}
|
|
// push route updates in case Linux deleted routes behind us after removing other various routes
|
|
refreshAllRoutes = true;
|
|
}
|
|
|
|
// Fourth: update Linux with any addresses to add
|
|
if (!endpoint.Network->IsHidden && endpoint.Network->IsConnected)
|
|
{
|
|
bool shouldRefreshAllAddresses = false;
|
|
for (auto& hostAddress : endpoint.Network->IpAddresses)
|
|
{
|
|
auto trackedAddress = endpoint.StateTracking->IpAddresses.emplace(TrackedIpAddress(hostAddress)).first;
|
|
// detect if previously sync'd addresses need to be updated
|
|
// this addresses issues we've seen where addresses were removed from Linux without our knowledge
|
|
shouldRefreshAllAddresses |= trackedAddress->SyncStatus == PendingAdd || trackedAddress->SyncStatus == PendingUpdate;
|
|
}
|
|
|
|
for (auto& trackedAddress : endpoint.StateTracking->IpAddresses)
|
|
{
|
|
std::optional<HRESULT> hr{};
|
|
switch (trackedAddress.SyncStatus)
|
|
{
|
|
case PendingAdd:
|
|
{
|
|
hr = SendAddressRequestToGns(endpoint, trackedAddress, hns::ModifyRequestType::Add);
|
|
if (FAILED(hr.value()))
|
|
{
|
|
// try to update it instead if it already exists
|
|
hr = SendAddressRequestToGns(endpoint, trackedAddress, hns::ModifyRequestType::Update);
|
|
}
|
|
|
|
if (SUCCEEDED(hr.value()) && m_vmConfig.EnableHostAddressLoopback)
|
|
{
|
|
// Add a special loopback route so that loopback packets flow through the host and back.
|
|
hr = SendLoopbackRequestToGns(endpoint, trackedAddress, hns::OperationType::Create);
|
|
if (FAILED(hr.value()))
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SyncIpStateWithLinux",
|
|
TraceLoggingValue("Failed to create loopback routes for local address", "message"),
|
|
TraceLoggingValue(wsl::core::networking::ToString(trackedAddress.SyncStatus), "AddressSyncStatus"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
|
|
TraceLoggingValue(trackedAddress.Address.AddressString.c_str(), "address"),
|
|
TraceLoggingValue(trackedAddress.Address.PrefixLength, "prefixLength"));
|
|
}
|
|
}
|
|
// push route updates in case Linux deleted routes behind us after refreshing addresses
|
|
refreshAllRoutes = true;
|
|
break;
|
|
}
|
|
|
|
case Synced:
|
|
{
|
|
auto fallThroughToUpdateAddress = shouldRefreshAllAddresses;
|
|
|
|
// Check if this address needs to be updated (i.e., its PreferredLifetime / DAD state needs to be updated)
|
|
auto hostAddress = endpoint.Network->IpAddresses.find(trackedAddress.Address);
|
|
if (hostAddress != endpoint.Network->IpAddresses.end())
|
|
{
|
|
if (trackedAddress.Address.IsPreferred() != hostAddress->IsPreferred())
|
|
{
|
|
trackedAddress.Address.PreferredLifetime = hostAddress->PreferredLifetime;
|
|
fallThroughToUpdateAddress = true;
|
|
}
|
|
}
|
|
|
|
if (!fallThroughToUpdateAddress)
|
|
{
|
|
break;
|
|
}
|
|
|
|
trackedAddress.SyncStatus = PendingUpdate;
|
|
trackedAddress.SyncRetryCount = TrackedIpAddress::MaxSyncRetryCount;
|
|
__fallthrough;
|
|
}
|
|
|
|
case PendingUpdate:
|
|
hr = SendAddressRequestToGns(endpoint, trackedAddress, hns::ModifyRequestType::Update);
|
|
if (FAILED(hr.value()))
|
|
{
|
|
// try to add it if it was removed in Linux
|
|
hr = SendAddressRequestToGns(endpoint, trackedAddress, hns::ModifyRequestType::Add);
|
|
}
|
|
|
|
if (SUCCEEDED(hr.value()) && m_vmConfig.EnableHostAddressLoopback)
|
|
{
|
|
// Add a special loopback route so that loopback packets flow through the host and back.
|
|
hr = SendLoopbackRequestToGns(endpoint, trackedAddress, hns::OperationType::Create);
|
|
if (FAILED(hr.value()))
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SyncIpStateWithLinux",
|
|
TraceLoggingValue("Failed to create loopback routes for local address", "message"),
|
|
TraceLoggingValue(wsl::core::networking::ToString(trackedAddress.SyncStatus), "AddressSyncStatus"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
|
|
TraceLoggingValue(trackedAddress.Address.AddressString.c_str(), "address"),
|
|
TraceLoggingValue(trackedAddress.Address.PrefixLength, "prefixLength"));
|
|
}
|
|
}
|
|
|
|
// push route updates in case Linux deleted routes behind us after refreshing addresses
|
|
refreshAllRoutes = true;
|
|
break;
|
|
|
|
case PendingRemoval:
|
|
// This address is still slated for removal, which we'll try again later.
|
|
continue;
|
|
|
|
default:
|
|
WI_ASSERT(false);
|
|
continue;
|
|
}
|
|
|
|
if (SUCCEEDED(hr.value_or(E_FAIL)))
|
|
{
|
|
trackedAddress.SyncStatus = Synced;
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SyncIpStateWithLinux",
|
|
TraceLoggingValue("Address synced", "message"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
|
|
TraceLoggingValue(trackedAddress.Address.AddressString.c_str(), "address"),
|
|
TraceLoggingValue(trackedAddress.Address.PrefixLength, "prefixLength"));
|
|
}
|
|
|
|
if (trackedAddress.SyncRetryCount == 0)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SyncIpStateWithLinux",
|
|
TraceLoggingValue(
|
|
"Reached maximum retries to sync an address - we will no longer schedule the retry timer", "message"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
|
|
TraceLoggingValue(trackedAddress.Address.AddressString.c_str(), "address"),
|
|
TraceLoggingValue(trackedAddress.Address.PrefixLength, "prefixLength"));
|
|
}
|
|
|
|
syncSuccessful &= (trackedAddress.SyncStatus == Synced || trackedAddress.SyncRetryCount == 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SyncIpStateWithLinux",
|
|
TraceLoggingValue("Not adding addresses for hidden or disconnected interface", "message"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(endpoint.Network->IsHidden, "isHidden"),
|
|
TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"));
|
|
}
|
|
|
|
// Fourth: update Linux with any routes to add
|
|
if (!endpoint.Network->IsHidden && endpoint.Network->IsConnected)
|
|
{
|
|
for (auto& hostRoute : endpoint.Network->Routes)
|
|
{
|
|
const auto trackedRoute = endpoint.StateTracking->Routes.emplace(TrackedRoute(hostRoute)).first;
|
|
// detect if previously sync'd routes need to be updated
|
|
// this addresses issues we've seen where routes were removed from Linux without our knowledge
|
|
// and routes couldn't be updated later because required routes, like the prefix route, wasn't there
|
|
refreshAllRoutes |= trackedRoute->SyncStatus == PendingAdd || trackedRoute->SyncStatus == PendingUpdate;
|
|
}
|
|
|
|
if (refreshAllRoutes)
|
|
{
|
|
WSL_LOG("WslMirroredNetworkManager::SyncIpStateWithLinux", TraceLoggingValue("Refreshing all routes", "message"));
|
|
}
|
|
|
|
for (auto& trackedRoute : endpoint.StateTracking->Routes)
|
|
{
|
|
std::optional<HRESULT> hr{};
|
|
switch (trackedRoute.SyncStatus)
|
|
{
|
|
case PendingAdd:
|
|
hr = SendRouteRequestToGns(endpoint, trackedRoute, hns::ModifyRequestType::Add);
|
|
if (FAILED(hr.value()))
|
|
{
|
|
// try to update it instead if it already exists
|
|
hr = SendRouteRequestToGns(endpoint, trackedRoute, hns::ModifyRequestType::Update);
|
|
}
|
|
break;
|
|
|
|
case Synced:
|
|
if (refreshAllRoutes)
|
|
{
|
|
hr = SendRouteRequestToGns(endpoint, trackedRoute, hns::ModifyRequestType::Update);
|
|
if (FAILED(hr.value()))
|
|
{
|
|
// try to add it
|
|
hr = SendRouteRequestToGns(endpoint, trackedRoute, hns::ModifyRequestType::Add);
|
|
}
|
|
if (FAILED(hr.value()))
|
|
{
|
|
trackedRoute.SyncStatus = PendingUpdate;
|
|
trackedRoute.SyncRetryCount = TrackedRoute::MaxSyncRetryCount;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PendingUpdate:
|
|
hr = SendRouteRequestToGns(endpoint, trackedRoute, hns::ModifyRequestType::Update);
|
|
if (FAILED(hr.value()))
|
|
{
|
|
// try to add it
|
|
hr = SendRouteRequestToGns(endpoint, trackedRoute, hns::ModifyRequestType::Add);
|
|
}
|
|
break;
|
|
|
|
case PendingRemoval:
|
|
// This route is still slated for removal, which we'll try again later.
|
|
continue;
|
|
|
|
default:
|
|
WI_ASSERT(false);
|
|
continue;
|
|
}
|
|
|
|
if (SUCCEEDED(hr.value_or(E_FAIL)))
|
|
{
|
|
trackedRoute.SyncStatus = Synced;
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SyncIpStateWithLinux",
|
|
TraceLoggingValue("Route synced", "message"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
|
|
TraceLoggingValue(trackedRoute.Route.DestinationPrefixString.c_str(), "destinationPrefix"),
|
|
TraceLoggingValue(trackedRoute.Route.DestinationPrefix.PrefixLength, "prefixLength"),
|
|
TraceLoggingValue(trackedRoute.Route.NextHopString.c_str(), "nextHop"),
|
|
TraceLoggingValue(trackedRoute.Route.Metric, "metric"));
|
|
}
|
|
|
|
if (trackedRoute.SyncRetryCount == 0)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SyncIpStateWithLinux",
|
|
TraceLoggingValue(
|
|
"Reached maximum amount of retries to sync a route. This can happen if the route's next hop is not "
|
|
"reachable, as Linux does not allow such routes to be plumbed. Failure to sync the route will no longer "
|
|
"schedule the retry timer.",
|
|
"message"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
|
|
TraceLoggingValue(trackedRoute.Route.DestinationPrefixString.c_str(), "destinationPrefix"),
|
|
TraceLoggingValue(trackedRoute.Route.DestinationPrefix.PrefixLength, "prefixLength"),
|
|
TraceLoggingValue(trackedRoute.Route.NextHopString.c_str(), "nextHop"),
|
|
TraceLoggingValue(trackedRoute.Route.Metric, "metric"));
|
|
}
|
|
|
|
syncSuccessful &= (trackedRoute.SyncStatus == Synced || trackedRoute.SyncRetryCount == 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SyncIpStateWithLinux",
|
|
TraceLoggingValue("Not adding routes for hidden or disconnected interface", "message"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(endpoint.Network->IsHidden, "isHidden"),
|
|
TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"));
|
|
}
|
|
|
|
// Fifth: update Linux with updated DNS information
|
|
if (m_dnsInfo != m_trackedDnsInfo)
|
|
{
|
|
if (FAILED(SendDnsRequestToGns(endpoint, m_dnsInfo, hns::ModifyRequestType::Update)))
|
|
{
|
|
syncSuccessful = false;
|
|
}
|
|
else
|
|
{
|
|
m_trackedDnsInfo = m_dnsInfo;
|
|
}
|
|
}
|
|
|
|
endpoint.StateTracking->InitialSyncComplete = true;
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SyncIpStateWithLinux",
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(syncSuccessful, "syncSuccessful"));
|
|
return syncSuccessful;
|
|
}
|
|
|
|
// We must determine what IP changes to push to Linux
|
|
_Requires_lock_held_(m_networkLock)
|
|
void wsl::core::networking::WslMirroredNetworkManager::UpdateAllEndpointsImpl(UpdateEndpointFlag updateFlag, _In_ PCSTR callingSource) noexcept
|
|
try
|
|
{
|
|
static long s_updateAllEndpointsCounter = 0;
|
|
const auto instanceCounter = InterlockedIncrement(&s_updateAllEndpointsCounter);
|
|
|
|
if (updateFlag == UpdateEndpointFlag::None)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::UpdateAllEndpointsImpl",
|
|
TraceLoggingValue(instanceCounter, "instanceCounter"),
|
|
TraceLoggingValue(callingSource, "callingSource"),
|
|
TraceLoggingValue("None [exiting early]", "updateFlag"));
|
|
return;
|
|
}
|
|
|
|
if (updateFlag == UpdateEndpointFlag::Default)
|
|
{
|
|
const auto currentTickCount = GetTickCount64();
|
|
const auto timeFromLastUpdate = currentTickCount - m_lastUpdateAllEndpointsDefaultTime;
|
|
|
|
if (timeFromLastUpdate >= m_debounceUpdateAllEndpointsTimerMs)
|
|
{
|
|
// It's been >= m_debounceUpdateAllEndpointsTimerMs since we last attempted an update, so go ahead and process it.
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::UpdateAllEndpointsImpl",
|
|
TraceLoggingValue(instanceCounter, "instanceCounter"),
|
|
TraceLoggingValue(callingSource, "callingSource"),
|
|
TraceLoggingValue(wsl::core::networking::ToString(updateFlag), "updateFlag"),
|
|
TraceLoggingValue("Debounce time reset - continuing update", "state"),
|
|
TraceLoggingValue(timeFromLastUpdate, "timeFromLastUpdate"),
|
|
TraceLoggingValue(m_debounceUpdateAllEndpointsTimerMs, "m_debounceUpdateAllEndpointsTimerMs"));
|
|
m_lastUpdateAllEndpointsDefaultTime = currentTickCount;
|
|
}
|
|
else if (!m_IsDebounceUpdateAllEndpointsDefaultTimerSet)
|
|
{
|
|
// The debounce timer is not already scheduled, so schedule it.
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::UpdateAllEndpointsImpl",
|
|
TraceLoggingValue(instanceCounter, "instanceCounter"),
|
|
TraceLoggingValue(callingSource, "callingSource"),
|
|
TraceLoggingValue(wsl::core::networking::ToString(updateFlag), "updateFlag"),
|
|
TraceLoggingValue("Debouncing Notification - setting timer", "state"),
|
|
TraceLoggingValue(timeFromLastUpdate, "timeFromLastUpdate"),
|
|
TraceLoggingValue(m_debounceUpdateAllEndpointsTimerMs, "m_debounceUpdateAllEndpointsTimerMs"));
|
|
|
|
// Set the due time just past the debounce timer duration, relative to the last update time.
|
|
m_IsDebounceUpdateAllEndpointsDefaultTimerSet = true;
|
|
FILETIME dueTime = wil::filetime::from_int64(static_cast<ULONGLONG>(
|
|
-1 * (wil::filetime_duration::one_millisecond * (20 + m_debounceUpdateAllEndpointsTimerMs - timeFromLastUpdate))));
|
|
SetThreadpoolTimer(m_debounceUpdateAllEndpointsDefaultTimer.get(), &dueTime, 0, 0);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// The debounce timer is already scheduled, so ignore this update.
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::UpdateAllEndpointsImpl",
|
|
TraceLoggingValue(instanceCounter, "instanceCounter"),
|
|
TraceLoggingValue(callingSource, "callingSource"),
|
|
TraceLoggingValue(wsl::core::networking::ToString(updateFlag), "updateFlag"),
|
|
TraceLoggingValue("Debouncing Notification - timer already set", "state"),
|
|
TraceLoggingValue(timeFromLastUpdate, "timeFromLastUpdate"),
|
|
TraceLoggingValue(m_debounceUpdateAllEndpointsTimerMs, "m_debounceUpdateAllEndpointsTimerMs"));
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::UpdateAllEndpointsImpl",
|
|
TraceLoggingValue(instanceCounter, "instanceCounter"),
|
|
TraceLoggingValue(callingSource, "callingSource"),
|
|
TraceLoggingValue(wsl::core::networking::ToString(updateFlag), "updateFlag"));
|
|
}
|
|
|
|
m_latestHnsStatus = HnsStatus::NetworkConnectedWithHnsNotification;
|
|
|
|
// Update IP properties on all interfaces on the host
|
|
// N.B. We must process the DisableDefaultRoutes property of each host interface before we
|
|
// process the host routes, as this property might impact the set of routes we choose to mirror.
|
|
ProcessConnectivityChange();
|
|
ProcessInterfaceChange();
|
|
ProcessIpAddressChange();
|
|
ProcessRouteChange();
|
|
ProcessDNSChange();
|
|
|
|
// Push IP state to Linux
|
|
bool syncSuccessful = true;
|
|
std::set<GUID, wsl::windows::common::helpers::GuidLess> mirroredConnectedInterfaces;
|
|
for (auto& endpoint : m_networkEndpoints)
|
|
{
|
|
if (IsInterfaceIndexOfGelnic(endpoint.Network->InterfaceIndex))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// there may be more mirrored interfaces than 'host-connected' interfaces
|
|
// e.g. network adapters which are disconnected or hidden
|
|
// track all host-connected interfaces that have been successfully mirrored
|
|
if (m_hostConnectedInterfaces.contains(endpoint.InterfaceGuid))
|
|
{
|
|
if (endpoint.Network->IsHidden)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::UpdateAllEndpointsImpl",
|
|
TraceLoggingValue(instanceCounter, "instanceCounter"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
|
|
TraceLoggingValue(
|
|
"Resetting IsHidden to false and PendingIPInterfaceUpdate to true to update the Interface", "message"));
|
|
endpoint.Network->IsHidden = false;
|
|
// setting PendingIPInterfaceUpdate to tell SyncIpStateWithLinux to update the Interface state
|
|
endpoint.Network->PendingIPInterfaceUpdate = true;
|
|
}
|
|
mirroredConnectedInterfaces.insert(endpoint.InterfaceGuid);
|
|
}
|
|
else
|
|
{
|
|
// if the host has hidden the interface that was mirrored by HNS
|
|
// ensure the interface is not connected in Linux
|
|
// we are deliberately overriding the endpoint state in this case
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::UpdateAllEndpointsImpl",
|
|
TraceLoggingValue(instanceCounter, "instanceCounter"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
|
|
TraceLoggingValue(
|
|
"Setting IsHidden to true - this interface is hidden on the host and must not be connected in the container",
|
|
"message"));
|
|
endpoint.Network->IsHidden = true;
|
|
// setting PendingIPInterfaceUpdate to tell SyncIpStateWithLinux to update the Interface state
|
|
endpoint.Network->PendingIPInterfaceUpdate = true;
|
|
}
|
|
|
|
if (!SyncIpStateWithLinux(endpoint))
|
|
{
|
|
// We failed to sync some bit of state. Let's schedule a timer to try again in a bit.
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::UpdateAllEndpointsImpl",
|
|
TraceLoggingValue(instanceCounter, "instanceCounter"),
|
|
TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
|
|
TraceLoggingValue("Some IP state did not sync with Linux - scheduling a retry attempt", "message"),
|
|
TraceLoggingValue(m_linuxIpStateRetryDebounceTimerMilliseconds, "m_linuxIpStateRetryDebounceTimerMilliseconds"));
|
|
|
|
FILETIME dueTime = wil::filetime::from_int64(
|
|
static_cast<ULONGLONG>(-1 * wil::filetime_duration::one_millisecond * m_linuxIpStateRetryDebounceTimerMilliseconds));
|
|
|
|
SetThreadpoolTimer(m_retryLinuxIpStateSyncTimer.get(), &dueTime, 0, 1000);
|
|
|
|
if (syncSuccessful)
|
|
{
|
|
// set to false the first pass through the for loop
|
|
syncSuccessful = false;
|
|
|
|
// Increase the IP state retry timer according to exponential back-off, capping at a maximum value.
|
|
m_linuxIpStateRetryDebounceTimerMilliseconds =
|
|
std::min(m_linuxIpStateRetryDebounceTimerMilliseconds * 2, m_linuxIpStateRetryDebounceTimerMaxMilliseconds);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If all of the following occurs, then we have entered the goal state.
|
|
// 1) Mirrored all usable host interfaces
|
|
// 2) Successfully sync'd all settings on those interfaces
|
|
// 3) Not currently in the goal state
|
|
if (syncSuccessful)
|
|
{
|
|
// Reset the IP state retry timer back to the minimum value.
|
|
m_linuxIpStateRetryDebounceTimerMilliseconds = m_linuxIpStateRetryDebounceTimerMinMilliseconds;
|
|
|
|
// if any host-connected interfaces are not yet mirrored, don't indicate we are in sync
|
|
bool hnsMirroredInSyncWithHost = mirroredConnectedInterfaces == m_hostConnectedInterfaces;
|
|
if (mirroredConnectedInterfaces != m_hostConnectedInterfaces)
|
|
{
|
|
// mirroredConnectedInterfaces won't equal m_hostConnectedInterfaces when:
|
|
// - there are hidden host interfaces
|
|
// i.e., interfaces are in m_networkEndpoints but not in m_hostConnectedInterfaces
|
|
// - when HNS hasn't yet mirrored a connected host interface
|
|
// i.e. interfaces are in m_hostConnectedInterfaces but not in m_networkEndpoints
|
|
//
|
|
// if HNS has not yet mirrored a host interface, we should not indicate we are in sync
|
|
// but if hidden interfaces should not block being in sync
|
|
|
|
// reset to true until we see if HNS hasn't yet mirrored a connected host interface
|
|
hnsMirroredInSyncWithHost = true;
|
|
// verify that HNS has mirrored all host-connected interfaces
|
|
for (const auto& connectedHostInterface : m_hostConnectedInterfaces)
|
|
{
|
|
bool interfaceMatched = false;
|
|
for (const auto& hnsEndpoint : m_networkEndpoints)
|
|
{
|
|
if (connectedHostInterface == hnsEndpoint.InterfaceGuid)
|
|
{
|
|
interfaceMatched = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!interfaceMatched)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::UpdateAllEndpointsImpl",
|
|
TraceLoggingValue(instanceCounter, "instanceCounter"),
|
|
TraceLoggingValue("HNS has not yet mirrored a host connected Interface", "message"),
|
|
TraceLoggingValue(connectedHostInterface, "interfaceGuid"));
|
|
hnsMirroredInSyncWithHost = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hnsMirroredInSyncWithHost && !m_inMirroredGoalState.is_signaled())
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::UpdateAllEndpointsImpl",
|
|
TraceLoggingValue(instanceCounter, "instanceCounter"),
|
|
TraceLoggingValue("Reached goal state", "message"));
|
|
m_inMirroredGoalState.SetEvent();
|
|
|
|
// Telemetry to see how long it takes to reach the mirrored goal state for the first time.
|
|
if (std::chrono::duration_cast<std::chrono::milliseconds>(m_initialMirroredGoalStateEndTime.time_since_epoch()) ==
|
|
std::chrono::milliseconds::zero())
|
|
{
|
|
m_initialMirroredGoalStateEndTime = std::chrono::steady_clock::now();
|
|
|
|
const auto waitTime = m_initialMirroredGoalStateEndTime - m_objectCreationTime;
|
|
WSL_LOG(
|
|
"WslMirroringInitialGoalStateWait",
|
|
TraceLoggingValue((std::chrono::duration_cast<std::chrono::milliseconds>(waitTime)).count(), "waitTimeMs"),
|
|
TraceLoggingValue(m_vmConfig.EnableDnsTunneling, "DnsTunnelingEnabled"),
|
|
TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), "HyperVFirewallEnabled"),
|
|
TraceLoggingValue(m_vmConfig.EnableAutoProxy, "AutoProxyFeatureEnabled")); // the feature is enabled, but we don't know if proxy settings are actually configured
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CATCH_LOG()
|
|
|
|
_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::EnumerateNetworks(_Out_ std::vector<GUID>& NetworkIds) const noexcept
|
|
try
|
|
{
|
|
auto lock = m_networkLock.lock_shared();
|
|
WI_ASSERT(m_state == State::Started);
|
|
if (m_state == State::Stopped)
|
|
{
|
|
return E_ABORT;
|
|
}
|
|
|
|
NetworkIds = EnumerateMirroredNetworks();
|
|
return S_OK;
|
|
}
|
|
CATCH_RETURN()
|
|
|
|
void wsl::core::networking::WslMirroredNetworkManager::AddEndpoint(NetworkEndpoint&& newEndpoint, hns::HNSEndpoint&& endpointProperties) noexcept
|
|
{
|
|
const auto lock = m_networkLock.lock_exclusive();
|
|
if (m_state == State::Stopped)
|
|
{
|
|
return;
|
|
}
|
|
|
|
constexpr uint32_t defaultRetryCount = 0ul;
|
|
AddEndpointImpl({std::move(newEndpoint), std::move(endpointProperties), defaultRetryCount});
|
|
}
|
|
|
|
_Requires_lock_held_(m_networkLock)
|
|
void wsl::core::networking::WslMirroredNetworkManager::AddEndpointImpl(EndpointTracking&& endpointTrackingObject) noexcept
|
|
{
|
|
PCSTR executionStep = "";
|
|
try
|
|
{
|
|
// Hot-add the network endpoint to the utility VM.
|
|
hcs::NetworkAdapter networkEndpoint{};
|
|
|
|
networkEndpoint.MacAddress = wsl::shared::string::ParseMacAddress(endpointTrackingObject.m_hnsEndpoint.MacAddress);
|
|
|
|
// Set the instance id to the mirrored interfaceGuid so HNS -> netvsc can optimally use the same vmNIC constructs when the InterfaceGuid is the same
|
|
hcs::ModifySettingRequest<hcs::NetworkAdapter> addEndpointRequest{};
|
|
addEndpointRequest.ResourcePath =
|
|
c_networkAdapterPrefix + wsl::shared::string::GuidToString<wchar_t>(endpointTrackingObject.m_networkEndpoint.InterfaceGuid);
|
|
addEndpointRequest.RequestType = hcs::ModifyRequestType::Add;
|
|
addEndpointRequest.Settings.EndpointId = endpointTrackingObject.m_hnsEndpoint.ID;
|
|
addEndpointRequest.Settings.InstanceId = endpointTrackingObject.m_networkEndpoint.InterfaceGuid;
|
|
|
|
addEndpointRequest.Settings.MacAddress = wsl::shared::string::ParseMacAddress(endpointTrackingObject.m_hnsEndpoint.MacAddress);
|
|
auto addEndpointRequestString = wsl::shared::ToJsonW(addEndpointRequest);
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::AddEndpoint [Creating HCS endpoint]",
|
|
TraceLoggingValue(addEndpointRequestString.c_str(), "networkRequestString"));
|
|
|
|
executionStep = "AddHcsEndpoint";
|
|
auto hr = m_hnsQueue.submit_and_wait([&] {
|
|
// RetryWithTimeout throws if fails every attempt - which is caught and returned by m_gnsMessageQueue
|
|
auto retryCount = 0ul;
|
|
return wsl::shared::retry::RetryWithTimeout<HRESULT>(
|
|
[&] {
|
|
const auto retryHr = wil::ResultFromException(
|
|
[&] { wsl::windows::common::hcs::ModifyComputeSystem(m_hcsSystem, addEndpointRequestString.c_str()); });
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::AddEndpoint [ModifyComputeSystem(ModifyRequestType::Add)]",
|
|
TraceLoggingValue(endpointTrackingObject.m_hnsEndpoint.ID, "endpointId"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "instanceId"),
|
|
TraceLoggingValue(retryHr, "retryHr"),
|
|
TraceLoggingValue(retryCount, "retryCount"));
|
|
|
|
++retryCount;
|
|
return THROW_IF_FAILED(retryHr);
|
|
},
|
|
wsl::core::networking::AddEndpointRetryPeriod,
|
|
wsl::core::networking::AddEndpointRetryTimeout,
|
|
wsl::core::networking::AddEndpointRetryPredicate);
|
|
});
|
|
|
|
if (hr == HCN_E_ENDPOINT_ALREADY_ATTACHED)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::AddEndpoint [Adding the endpoint returned HCN_E_ENDPOINT_ALREADY_ATTACHED - "
|
|
"continuing]",
|
|
TraceLoggingValue(endpointTrackingObject.m_hnsEndpoint.ID, "endpointId"));
|
|
|
|
hr = S_OK;
|
|
}
|
|
else if (FAILED(hr))
|
|
{
|
|
THROW_HR(hr);
|
|
}
|
|
|
|
auto removeEndpointOnError = wil::scope_exit([&] {
|
|
// try to delete the endpoint in HCS if anything failed
|
|
// Set the instance id to the mirrored interfaceGuid so HNS -> netvsc can optimally use the same vmNIC constructs when the InterfaceGuid is the same
|
|
hcs::ModifySettingRequest<hcs::NetworkAdapter> networkRequest{};
|
|
networkRequest.ResourcePath =
|
|
c_networkAdapterPrefix + wsl::shared::string::GuidToString<wchar_t>(endpointTrackingObject.m_networkEndpoint.InterfaceGuid);
|
|
networkRequest.RequestType = hcs::ModifyRequestType::Remove;
|
|
networkRequest.Settings.EndpointId = endpointTrackingObject.m_hnsEndpoint.ID;
|
|
networkRequest.Settings.InstanceId = endpointTrackingObject.m_networkEndpoint.InterfaceGuid;
|
|
|
|
const auto networkRequestString = wsl::shared::ToJsonW(std::move(networkRequest));
|
|
|
|
// capturing by ref because we wait for the workitem to complete
|
|
const auto modifyResult = m_hnsQueue.submit_and_wait([&] {
|
|
windows::common::hcs::ModifyComputeSystem(m_hcsSystem, networkRequestString.c_str());
|
|
return S_OK; // ModifyComputeSystem throws errors, caught by m_gnsMessageQueue
|
|
});
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::AddEndpoint [Removing the HCS mirrored endpoint after failure to Add]",
|
|
TraceLoggingHResult(modifyResult, "hr"),
|
|
TraceLoggingValue(endpointTrackingObject.m_hnsEndpoint.ID, "endpointId"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "instanceId"));
|
|
|
|
if (FAILED(modifyResult))
|
|
{
|
|
WSL_LOG(
|
|
"AddMirroredEndpointFailed",
|
|
TraceLoggingHResult(modifyResult, "result"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(
|
|
endpointTrackingObject.m_networkEndpoint.Network ? endpointTrackingObject.m_networkEndpoint.Network->InterfaceType : 0,
|
|
"InterfaceType"),
|
|
TraceLoggingValue("RemoveHcsEndpointOnFailure", "executionStep"),
|
|
TraceLoggingValue(m_vmConfig.EnableDnsTunneling, "DnsTunnelingEnabled"),
|
|
TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), "HyperVFirewallEnabled"),
|
|
TraceLoggingValue(m_vmConfig.EnableAutoProxy, "AutoProxyFeatureEnabled"), // the feature is enabled, but we don't know if proxy settings are actually configured
|
|
TraceLoggingValue(endpointTrackingObject.m_retryCount, "retryCount"));
|
|
}
|
|
|
|
// Inform the parent class to remove the endpoint object from GNS registration since we couldn't add the endpoint
|
|
});
|
|
|
|
// Refreshing the endpoint causes it to reach the GNSInterfaceState::Synchronized state in HNS
|
|
// which is required to receive notifications.
|
|
// When HcnModifyEndpoint returns, all GNS notifications have been processed and the interface is fully configured.
|
|
hns::ModifyGuestEndpointSettingRequest<void> refreshRequest{};
|
|
refreshRequest.RequestType = hns::ModifyRequestType::Refresh;
|
|
refreshRequest.ResourceType = hns::GuestEndpointResourceType::Port;
|
|
|
|
const auto refreshEndpointRequestString = ToJsonW(refreshRequest);
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::AddEndpoint [Synchronizing HNS state]",
|
|
TraceLoggingValue(endpointTrackingObject.m_hnsEndpoint.ID, "endpointId"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "instanceId"));
|
|
|
|
executionStep = "RefreshHcsEndpoint";
|
|
THROW_IF_FAILED(m_hnsQueue.submit_and_wait([&] {
|
|
// Don't retry if HcnModifyEndpoint fails with HCN_E_ENDPOINT_NOT_FOUND which indicates that the underlying network object was deleted.
|
|
constexpr auto retryPredicate = [] { return wil::ResultFromCaughtException() != HCN_E_ENDPOINT_NOT_FOUND; };
|
|
auto retryCount = 0ul;
|
|
// RetryWithTimeout throws if fails every attempt - which is caught and returned by m_hnsQueue
|
|
return wsl::shared::retry::RetryWithTimeout<HRESULT>(
|
|
[&] {
|
|
wil::unique_cotaskmem_string error;
|
|
const auto retryHr = HcnModifyEndpoint(
|
|
endpointTrackingObject.m_networkEndpoint.Endpoint.get(), refreshEndpointRequestString.c_str(), &error);
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::AddEndpoint [HcnModifyEndpoint(ModifyRequestType::Refresh)]",
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.EndpointId, "endpointId"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "instanceId"),
|
|
TraceLoggingValue(refreshEndpointRequestString.c_str(), "json"),
|
|
TraceLoggingHResult(retryHr, "retryHr"),
|
|
TraceLoggingValue(error.is_valid() ? error.get() : L"", "errorString"),
|
|
TraceLoggingValue(retryCount, "retryCount"));
|
|
|
|
++retryCount;
|
|
return THROW_IF_FAILED(retryHr);
|
|
},
|
|
wsl::core::networking::AddEndpointRetryPeriod,
|
|
wsl::core::networking::AddEndpointRetryTimeout,
|
|
retryPredicate);
|
|
}));
|
|
|
|
// Notify GNS of the new adapter
|
|
hns::VmNicCreatedNotification newAdapterNotification;
|
|
// Set the adapterId == instanceId of the created Endpoint == the mirrored interfaceGuid
|
|
newAdapterNotification.adapterId = endpointTrackingObject.m_networkEndpoint.InterfaceGuid;
|
|
|
|
constexpr auto type = GnsMessageType(newAdapterNotification);
|
|
const auto jsonString = ToJsonW(newAdapterNotification);
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::AddEndpoint",
|
|
TraceLoggingValue("VmNicCreatedNotification [queued]", "GnsMessage"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "adapterId"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.NetworkId, "networkId"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.EndpointId, "endpointId"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "interfaceGuid"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.Network->InterfaceIndex, "interfaceIndex"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.Network->InterfaceType, "interfaceType"),
|
|
TraceLoggingValue(jsonString.c_str(), "jsonString"));
|
|
|
|
int linuxResultCode{};
|
|
// can safely capture by ref since we are waiting
|
|
hr = m_gnsCallbackQueue.submit_and_wait(
|
|
[&] { return m_callbackForGnsMessage(type, jsonString, GnsCallbackFlags::Wait, &linuxResultCode); });
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::AddEndpoint",
|
|
TraceLoggingValue("VmNicCreatedNotification [completed]", "GnsMessage"),
|
|
TraceLoggingHResult(hr, "hr"),
|
|
TraceLoggingValue(linuxResultCode, "linuxResultCode"));
|
|
|
|
// Send the endpoint state (link status) to gns.
|
|
// Also set the loopback device name to allow configuration by name.
|
|
//
|
|
// Temporarily set endpoint ID and PortFriendlyName to what LxGnsMessageInterfaceConfiguration expects.
|
|
GUID originalEndpointId = endpointTrackingObject.m_hnsEndpoint.ID;
|
|
std::wstring originalPortFriendlyName = endpointTrackingObject.m_hnsEndpoint.PortFriendlyName;
|
|
endpointTrackingObject.m_hnsEndpoint.ID = endpointTrackingObject.m_networkEndpoint.InterfaceGuid;
|
|
if (IsInterfaceIndexOfGelnic(endpointTrackingObject.m_networkEndpoint.Network->InterfaceIndex))
|
|
{
|
|
endpointTrackingObject.m_hnsEndpoint.PortFriendlyName = c_loopbackDeviceName;
|
|
}
|
|
WI_ASSERT(endpointTrackingObject.m_hnsEndpoint.IPAddress.empty());
|
|
|
|
executionStep = "SendEndpointStateToGns";
|
|
linuxResultCode = {};
|
|
// can safely capture by ref since we are waiting
|
|
hr = m_gnsCallbackQueue.submit_and_wait([&] {
|
|
return m_callbackForGnsMessage(
|
|
LxGnsMessageInterfaceConfiguration, ToJsonW(endpointTrackingObject.m_hnsEndpoint), GnsCallbackFlags::Wait, &linuxResultCode);
|
|
});
|
|
// restore the Endpoint ID GUID and PortFriendlyName
|
|
endpointTrackingObject.m_hnsEndpoint.ID = originalEndpointId;
|
|
endpointTrackingObject.m_hnsEndpoint.PortFriendlyName = originalPortFriendlyName;
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::AddEndpoint [Update link status]",
|
|
TraceLoggingHResult(hr, "hr"),
|
|
TraceLoggingValue(linuxResultCode, "linuxResultCode"),
|
|
TraceLoggingValue(endpointTrackingObject.m_hnsEndpoint.ID, "endpointId"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "instanceId"),
|
|
TraceLoggingValue(endpointTrackingObject.m_hnsEndpoint.PortFriendlyName.c_str(), "PortFriendlyName"));
|
|
THROW_IF_FAILED(hr);
|
|
|
|
endpointTrackingObject.m_networkEndpoint.Network->MacAddress = endpointTrackingObject.m_hnsEndpoint.MacAddress;
|
|
endpointTrackingObject.m_networkEndpoint.Network->DeviceName = endpointTrackingObject.m_hnsEndpoint.PortFriendlyName;
|
|
|
|
if (IsInterfaceIndexOfGelnic(endpointTrackingObject.m_networkEndpoint.Network->InterfaceIndex))
|
|
{
|
|
// Create loopback device in the container which will also set up loopback communication with the host.
|
|
hns::CreateDeviceRequest createLoopbackDevice;
|
|
createLoopbackDevice.deviceName = c_loopbackDeviceName;
|
|
createLoopbackDevice.type = hns::DeviceType::Loopback;
|
|
// Set the lowerEdgeAdapterId == the InstanceId of the Endpoint == the mirrored interfaceGuid
|
|
createLoopbackDevice.lowerEdgeAdapterId = endpointTrackingObject.m_networkEndpoint.InterfaceGuid;
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::AddEndpoint",
|
|
TraceLoggingValue("CreateDeviceRequest - loopback [queued]", "GnsMessage"),
|
|
TraceLoggingValue(c_loopbackDeviceName, "deviceName"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "lowerEdgeAdapterId"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.EndpointId, "endpointId"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "interfaceGuid"));
|
|
constexpr auto gnsMessageType = GnsMessageType(createLoopbackDevice);
|
|
|
|
linuxResultCode = {};
|
|
// can safely capture by ref since we are waiting
|
|
hr = m_gnsCallbackQueue.submit_and_wait([&] {
|
|
return m_callbackForGnsMessage(gnsMessageType, ToJsonW(createLoopbackDevice), GnsCallbackFlags::Wait, &linuxResultCode);
|
|
});
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::AddEndpoint",
|
|
TraceLoggingValue("CreateDeviceRequest - loopback [completed]", "GnsMessage"),
|
|
TraceLoggingHResult(hr, "hr"),
|
|
TraceLoggingValue(linuxResultCode, "linuxResultCode"));
|
|
}
|
|
else
|
|
{
|
|
// Perform per-interface configuration of net filter rules.
|
|
hns::InterfaceNetFilterRequest interfaceNetFilterRequest;
|
|
interfaceNetFilterRequest.targetDeviceName =
|
|
wsl::shared::string::GuidToString<wchar_t>(endpointTrackingObject.m_networkEndpoint.InterfaceGuid);
|
|
interfaceNetFilterRequest.operation = hns::OperationType::Create;
|
|
interfaceNetFilterRequest.ephemeralPortRangeStart = m_ephemeralPortRange.first;
|
|
interfaceNetFilterRequest.ephemeralPortRangeEnd = m_ephemeralPortRange.second;
|
|
|
|
linuxResultCode = {};
|
|
// can safely capture by ref since we are waiting
|
|
hr = m_gnsCallbackQueue.submit_and_wait([&] {
|
|
return m_callbackForGnsMessage(
|
|
LxGnsMessageInterfaceNetFilter, ToJsonW(interfaceNetFilterRequest), GnsCallbackFlags::Wait, &linuxResultCode);
|
|
});
|
|
LOG_IF_FAILED(hr);
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::AddEndpoint [InterfaceNetFilterRequest]",
|
|
TraceLoggingHResult(hr, "hr"),
|
|
TraceLoggingValue(linuxResultCode, "linuxResultCode"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.EndpointId, "endpointId"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "interfaceGuid"),
|
|
TraceLoggingValue(m_ephemeralPortRange.first, "ephemeralPortRangeStart"),
|
|
TraceLoggingValue(m_ephemeralPortRange.second, "ephemeralPortRangeEnd"));
|
|
}
|
|
|
|
// WSL will track state for every endpoint (interface)
|
|
endpointTrackingObject.m_networkEndpoint.StateTracking.emplace(m_vmConfig.FirewallConfig.VmCreatorId);
|
|
endpointTrackingObject.m_networkEndpoint.StateTracking->SeedInitialState(*endpointTrackingObject.m_networkEndpoint.Network);
|
|
|
|
m_networkEndpoints.emplace_back(std::move(endpointTrackingObject.m_networkEndpoint));
|
|
|
|
// successfully tracked the added endpoint - release the scope guards
|
|
removeEndpointOnError.release();
|
|
|
|
// after added, we must determine what is the preferred interface to indicate to bond to connect
|
|
UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, "AddEndpoint");
|
|
|
|
WSL_LOG(
|
|
"CreateMirroredEndpointEnd",
|
|
TraceLoggingHResult(S_OK, "result"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(
|
|
endpointTrackingObject.m_networkEndpoint.Network ? endpointTrackingObject.m_networkEndpoint.Network->InterfaceType : 0,
|
|
"InterfaceType"),
|
|
TraceLoggingValue(m_vmConfig.EnableDnsTunneling, "DnsTunnelingEnabled"),
|
|
TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), "HyperVFirewallEnabled"),
|
|
TraceLoggingValue(m_vmConfig.EnableAutoProxy, "AutoProxyFeatureEnabled"), // the feature is enabled, but we don't know if proxy settings are actually configured
|
|
TraceLoggingValue(endpointTrackingObject.m_retryCount, "retryCount"));
|
|
}
|
|
catch (...)
|
|
{
|
|
const auto hr = wil::ResultFromCaughtException();
|
|
|
|
WSL_LOG(
|
|
"AddMirroredEndpointFailed",
|
|
TraceLoggingHResult(hr, "result"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(
|
|
endpointTrackingObject.m_networkEndpoint.Network ? endpointTrackingObject.m_networkEndpoint.Network->InterfaceType : 0,
|
|
"InterfaceType"),
|
|
TraceLoggingValue(executionStep, "executionStep"),
|
|
TraceLoggingValue(m_vmConfig.EnableDnsTunneling, "DnsTunnelingEnabled"),
|
|
TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), "HyperVFirewallEnabled"),
|
|
TraceLoggingValue(m_vmConfig.EnableAutoProxy, "AutoProxyFeatureEnabled"), // the feature is enabled, but we don't know if proxy settings are actually configured
|
|
TraceLoggingValue(endpointTrackingObject.m_retryCount, "retryCount"));
|
|
|
|
if (hr == HCN_E_ENDPOINT_NOT_FOUND)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::AddEndpoint",
|
|
TraceLoggingValue("HCN/HCS returned HCN_E_ENDPOINT_NOT_FOUND - not retrying", "GnsMessage"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.NetworkId, "networkId"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.EndpointId, "endpointId"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "interfaceGuid"),
|
|
TraceLoggingHResult(hr, "hr"));
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
++endpointTrackingObject.m_retryCount;
|
|
|
|
if (endpointTrackingObject.m_retryCount > m_maxAddEndpointRetryCount)
|
|
{
|
|
WSL_LOG(
|
|
"BlockedNetworkEndpoint",
|
|
TraceLoggingValue("WslMirroredNetworkManager::AddEndpoint", "where"),
|
|
TraceLoggingHResult(hr, "result"),
|
|
TraceLoggingValue(executionStep, "executionStep"),
|
|
TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "InterfaceGuid"),
|
|
TraceLoggingValue(
|
|
endpointTrackingObject.m_networkEndpoint.Network ? endpointTrackingObject.m_networkEndpoint.Network->InterfaceType : 0,
|
|
"InterfaceType"),
|
|
TraceLoggingValue(m_vmConfig.EnableDnsTunneling, "DnsTunnelingEnabled"),
|
|
TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), "HyperVFirewallEnabled"),
|
|
TraceLoggingValue(m_vmConfig.EnableAutoProxy, "AutoProxyFeatureEnabled")); // the feature is enabled, but we don't know if proxy settings are actually configured
|
|
|
|
// we now need to guarantee that Update* gets called again - but we can't do it from this thread
|
|
// will update our debounce-timer to fire soon to invoke Update - which will trigger the Blocked* path
|
|
// since we are now blocked on this interface
|
|
FILETIME dueTime = wil::filetime::from_int64(
|
|
static_cast<ULONGLONG>(-1 * wil::filetime_duration::one_millisecond * m_debounceUpdateAllEndpointsTimerMs));
|
|
SetThreadpoolTimer(m_debounceUpdateAllEndpointsDefaultTimer.get(), &dueTime, 0, 0);
|
|
return;
|
|
}
|
|
|
|
m_failedEndpointProperties.emplace_back(
|
|
std::move(endpointTrackingObject.m_networkEndpoint),
|
|
std::move(endpointTrackingObject.m_hnsEndpoint),
|
|
endpointTrackingObject.m_retryCount);
|
|
|
|
FILETIME dueTime = wil::filetime::from_int64(
|
|
static_cast<ULONGLONG>(-1 * wil::filetime_duration::one_millisecond * m_debounceCreateEndpointFailureTimerMs));
|
|
SetThreadpoolTimer(m_debounceCreateEndpointFailureTimer.get(), &dueTime, 0, 0);
|
|
}
|
|
CATCH_LOG()
|
|
}
|
|
}
|
|
|
|
_Requires_lock_held_(m_networkLock)
|
|
_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::RemoveEndpoint(const GUID& endpointId) noexcept
|
|
try
|
|
{
|
|
const auto removedFailedEndpointCount = std::erase_if(m_failedEndpointProperties, [&](const auto& endpointTracking) {
|
|
return endpointTracking.m_networkEndpoint.EndpointId == endpointId;
|
|
});
|
|
if (removedFailedEndpointCount > 0)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::RemoveEndpoint - Endpoint removed from m_failedEndpointProperties",
|
|
TraceLoggingValue(endpointId, "endpointId"));
|
|
}
|
|
|
|
WSL_LOG("WslMirroredNetworkManager::RemoveEndpoint", TraceLoggingValue(endpointId, "endpointId"));
|
|
|
|
std::vector<NetworkEndpoint>::const_iterator foundEndpoint;
|
|
|
|
try
|
|
{
|
|
foundEndpoint = std::find_if(m_networkEndpoints.cbegin(), m_networkEndpoints.cend(), [&](const auto& endpoint) {
|
|
return endpoint.EndpointId == endpointId;
|
|
});
|
|
|
|
if (foundEndpoint == m_networkEndpoints.cend())
|
|
{
|
|
WSL_LOG("WslMirroredNetworkManager::RemoveEndpoint - Endpoint not found", TraceLoggingValue(endpointId, "endpointId"));
|
|
return S_OK;
|
|
}
|
|
|
|
// Perform per-interface configuration of net filter rules.
|
|
hns::InterfaceNetFilterRequest interfaceNetFilterRequest;
|
|
interfaceNetFilterRequest.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(foundEndpoint->InterfaceGuid);
|
|
interfaceNetFilterRequest.operation = hns::OperationType::Remove;
|
|
|
|
int linuxResultCode{};
|
|
// can safely capture by ref since we are waiting
|
|
auto hr = m_gnsCallbackQueue.submit_and_wait([&] {
|
|
return m_callbackForGnsMessage(
|
|
LxGnsMessageInterfaceNetFilter, ToJsonW(std::move(interfaceNetFilterRequest)), GnsCallbackFlags::Wait, &linuxResultCode);
|
|
});
|
|
LOG_IF_FAILED(hr);
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::RemoveEndpoint [InterfaceNetFilterRequest]",
|
|
TraceLoggingHResult(hr, "hr"),
|
|
TraceLoggingValue(linuxResultCode, "linuxResultCode"),
|
|
TraceLoggingValue(endpointId, "endpointId"),
|
|
TraceLoggingValue(foundEndpoint->InterfaceGuid, "interfaceGuid"));
|
|
|
|
// A race exists between already queued operations for this interface on the GNS queue and HNS endpoint removal.
|
|
// In order to resolve the race, while holding the m_networkLock, flush the GNS queue then delete the endpoint in HCS.
|
|
WSL_LOG("WslMirroredNetworkManager::RemoveEndpoint", TraceLoggingValue("Flush GNS queue [queued]", "message"));
|
|
|
|
linuxResultCode = {};
|
|
// can safely capture by ref since we are waiting
|
|
hr = m_gnsCallbackQueue.submit_and_wait([&] {
|
|
return m_callbackForGnsMessage(LxGnsMessageNoOp, std::wstring(L""), GnsCallbackFlags::Wait, &linuxResultCode);
|
|
});
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::RemoveEndpoint",
|
|
TraceLoggingValue("Flush GNS queue [completed]", "message"),
|
|
TraceLoggingHResult(hr, "hr"),
|
|
TraceLoggingValue(linuxResultCode, "linuxResultCode"));
|
|
|
|
// try to delete the endpoint in HCS
|
|
// Set the instance id to the mirrored interfaceGuid so HNS -> netvsc can optimally use the same vmNIC constructs when the InterfaceGuid is the same
|
|
|
|
hcs::ModifySettingRequest<hcs::NetworkAdapter> networkRequest{};
|
|
networkRequest.ResourcePath = c_networkAdapterPrefix + wsl::shared::string::GuidToString<wchar_t>(foundEndpoint->InterfaceGuid);
|
|
networkRequest.RequestType = hcs::ModifyRequestType::Remove;
|
|
networkRequest.Settings.InstanceId = foundEndpoint->InterfaceGuid;
|
|
networkRequest.Settings.EndpointId = endpointId;
|
|
|
|
const auto networkRequestString = wsl::shared::ToJsonW(networkRequest);
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::RemoveEndpoint : Removing the HCS mirrored endpoint [queued]",
|
|
TraceLoggingValue(networkRequestString.c_str(), "networkRequest"),
|
|
TraceLoggingValue(endpointId, "endpointId"));
|
|
// capturing by ref because we wait for the workitem to complete
|
|
hr = m_hnsQueue.submit_and_wait([&] {
|
|
windows::common::hcs::ModifyComputeSystem(m_hcsSystem, networkRequestString.c_str());
|
|
return S_OK;
|
|
});
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::RemoveEndpoint : Removing the HCS mirrored endpoint [completed]",
|
|
TraceLoggingHResult(hr, "hr"));
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
WSL_LOG(
|
|
"RemoveMirroredEndpointFailed",
|
|
TraceLoggingHResult(hr, "result"),
|
|
TraceLoggingValue("RemoveHcsEndpoint", "executionStep"),
|
|
TraceLoggingValue(m_vmConfig.EnableDnsTunneling, "DnsTunnelingEnabled"),
|
|
TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), "HyperVFirewallEnabled"),
|
|
TraceLoggingValue(m_vmConfig.EnableAutoProxy, "AutoProxyFeatureEnabled")); // the feature is enabled, but we don't know if proxy settings are actually configured
|
|
}
|
|
}
|
|
CATCH_LOG()
|
|
|
|
// Remove the endpoint and its tracked state
|
|
// Linux will delete any addresses and routes associated with the interface
|
|
m_networkEndpoints.erase(foundEndpoint);
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::RemoveEndpoint - Endpoint removed from m_networkEndpoints",
|
|
TraceLoggingValue(endpointId, "endpointId"));
|
|
|
|
// Is this necessary?
|
|
UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, "RemoveEndpoint");
|
|
|
|
return S_OK;
|
|
}
|
|
CATCH_RETURN()
|
|
|
|
void wsl::core::networking::WslMirroredNetworkManager::SendCreateNotificationsForInitialEndpoints() noexcept
|
|
{
|
|
WSL_LOG("WslMirroredNetworkManager::SendCreateNotificationsForInitialEndpoints");
|
|
const auto lock = m_networkLock.lock_shared();
|
|
if (m_state == State::Stopped)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Perform global configuration of net filter rules.
|
|
int linuxResultCode{};
|
|
// can safely capture by ref since we are waiting
|
|
const auto hr = m_gnsCallbackQueue.submit_and_wait([&] {
|
|
return m_callbackForGnsMessage(LxGnsMessageGlobalNetFilter, std::wstring(L""), GnsCallbackFlags::Wait, &linuxResultCode);
|
|
});
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::SendCreateNotificationsForInitialEndpoints",
|
|
TraceLoggingValue("Sent message to perform global configuration of net filter rules", "message"),
|
|
TraceLoggingHResult(hr, "hr"),
|
|
TraceLoggingValue(linuxResultCode, "linuxResultCode"));
|
|
LOG_IF_FAILED(hr);
|
|
}
|
|
|
|
HRESULT wsl::core::networking::WslMirroredNetworkManager::WaitForMirroredGoalState() noexcept
|
|
{
|
|
WSL_LOG("WslMirroredNetworkManager::WaitForMirroredGoalState");
|
|
|
|
return (m_inMirroredGoalState.wait(c_initialMirroredGoalStateWaitTimeoutMs)) ? S_OK : HRESULT_FROM_WIN32(ERROR_TIMEOUT);
|
|
}
|
|
|
|
_Check_return_ bool wsl::core::networking::WslMirroredNetworkManager::DoesEndpointExist(GUID networkId) const noexcept
|
|
try
|
|
{
|
|
const auto lock = m_networkLock.lock_shared();
|
|
if (m_state == State::Stopped)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return std::ranges::any_of(m_networkEndpoints, [&](const NetworkEndpoint& endpoint) { return endpoint.NetworkId == networkId; });
|
|
}
|
|
catch (...)
|
|
{
|
|
LOG_CAUGHT_EXCEPTION();
|
|
return false;
|
|
}
|
|
|
|
_Requires_lock_not_held_(m_networkLock)
|
|
void wsl::core::networking::WslMirroredNetworkManager::UpdateAllEndpoints(_In_ PCSTR sourceName) noexcept
|
|
{
|
|
const auto lock = m_networkLock.lock_exclusive();
|
|
if (m_state == State::Stopped)
|
|
{
|
|
return;
|
|
}
|
|
|
|
UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, sourceName);
|
|
}
|
|
|
|
void wsl::core::networking::WslMirroredNetworkManager::OnNetworkConnectivityHintChange() noexcept
|
|
{
|
|
const auto lock = m_networkLock.lock_exclusive();
|
|
if (m_state == State::Stopped)
|
|
{
|
|
return;
|
|
}
|
|
|
|
UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, "OnNetworkConnectivityHintChange");
|
|
}
|
|
|
|
// Strategy for handling notifications from HNS:
|
|
// 1) Always consume the data immediately.
|
|
// 2) If UpdateAllEndpointsImpl hasn't run for >= m_debounceUpdateAllEndpointsTimerMs then run it.
|
|
// 3) If UpdateAllEndpointsImpl has run < m_debounceUpdateAllEndpointsTimerMs ago, schedule the timer.
|
|
void wsl::core::networking::WslMirroredNetworkManager::OnNetworkEndpointChange() noexcept
|
|
{
|
|
const auto lock = m_networkLock.lock_exclusive();
|
|
if (m_state == State::Stopped)
|
|
{
|
|
return;
|
|
}
|
|
|
|
UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, "OnNetworkEndpointChange");
|
|
}
|
|
|
|
void wsl::core::networking::WslMirroredNetworkManager::OnDnsSuffixChange() noexcept
|
|
try
|
|
{
|
|
const auto lock = m_networkLock.lock_exclusive();
|
|
if (m_state == State::Stopped)
|
|
{
|
|
return;
|
|
}
|
|
|
|
UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, "OnDnsSuffixChange");
|
|
}
|
|
CATCH_LOG();
|
|
|
|
void wsl::core::networking::WslMirroredNetworkManager::TunAdapterStateChanged(_In_ const std::string& interfaceName, _In_ bool up) noexcept
|
|
{
|
|
}
|
|
|
|
void wsl::core::networking::WslMirroredNetworkManager::ReconnectGuestNetwork()
|
|
{
|
|
auto lock = m_networkLock.lock_exclusive();
|
|
if (m_state == State::Stopped)
|
|
{
|
|
return;
|
|
}
|
|
|
|
WSL_LOG("WslMirroredNetworkManager::ReconnectGuestNetwork");
|
|
UpdateAllEndpointsImpl(UpdateEndpointFlag::ForceUpdate, "ReconnectGuestNetwork");
|
|
}
|
|
|
|
_Requires_lock_held_(m_networkLock)
|
|
wsl::core::networking::NetworkSettings wsl::core::networking::WslMirroredNetworkManager::GetNetworkSettingsOfInterface(DWORD ifIndex) const
|
|
{
|
|
const auto matchingEndpoint =
|
|
std::ranges::find_if(m_networkEndpoints, [&](const auto& endpoint) { return endpoint.Network->InterfaceIndex == ifIndex; });
|
|
if (matchingEndpoint == std::end(m_networkEndpoints))
|
|
{
|
|
WSL_LOG("GetNetworkSettingsOfInterface - Network not found", TraceLoggingValue(ifIndex, "ifIndex"));
|
|
return {};
|
|
}
|
|
else
|
|
{
|
|
WSL_LOG("GetNetworkSettingsOfInterface", TRACE_NETWORKSETTINGS_OBJECT(matchingEndpoint->Network.get()));
|
|
return *matchingEndpoint->Network;
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<wsl::core::networking::NetworkSettings> wsl::core::networking::WslMirroredNetworkManager::GetEndpointSettings(
|
|
const hns::HNSEndpoint& endpointProperties) const
|
|
{
|
|
return wsl::core::networking::GetEndpointSettings(endpointProperties);
|
|
}
|
|
|
|
_Requires_lock_held_(m_networkLock)
|
|
_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::UpdateHcnServiceTimer() noexcept
|
|
try
|
|
{
|
|
// These values are chosen so that the connection will be retried 5 times.
|
|
static constexpr DWORD INITIAL_RETRY_HCN_SERVICE_CONNECTION_TIMER_DURATION_MS = 1000; // 1 second.
|
|
static constexpr DWORD MAX_RETRY_HCN_SERVICE_CONNECTION_TIMER_DURATION_MS = 80000; // ~1.3 minute.
|
|
|
|
// Check if the maximum retry attempt count has been reached.
|
|
if (m_retryHcnServiceConnectionDurationMs <= MAX_RETRY_HCN_SERVICE_CONNECTION_TIMER_DURATION_MS)
|
|
{
|
|
// Determine how long until the timer should fire.
|
|
if (m_retryHcnServiceConnectionDurationMs == 0)
|
|
{
|
|
// Use the initial duration value as this is the first time the timer is being armed.
|
|
m_retryHcnServiceConnectionDurationMs = INITIAL_RETRY_HCN_SERVICE_CONNECTION_TIMER_DURATION_MS;
|
|
}
|
|
else
|
|
{
|
|
// Make sure that the timer duration can't overflow.
|
|
static_assert(MAX_RETRY_HCN_SERVICE_CONNECTION_TIMER_DURATION_MS < (DWORD_MAX / 2));
|
|
|
|
// Apply an exponential backoff.
|
|
m_retryHcnServiceConnectionDurationMs *= 2;
|
|
}
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::UpdateHcnServiceTimer",
|
|
TraceLoggingValue(m_retryHcnServiceConnectionDurationMs, "m_retryHcnServiceConnectionDurationMs"));
|
|
|
|
FILETIME dueTime = wil::filetime::from_int64(
|
|
static_cast<ULONGLONG>(-1 * wil::filetime_duration::one_millisecond * m_retryHcnServiceConnectionDurationMs));
|
|
SetThreadpoolTimer(m_retryHcnServiceConnectionTimer.get(), &dueTime, 0, 1000);
|
|
}
|
|
else
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::UpdateHcnServiceTimer",
|
|
TraceLoggingValue(0, "retryHcnServiceConnectionDurationMs (service is not active)"));
|
|
THROW_WIN32(ERROR_SERVICE_NOT_ACTIVE);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
CATCH_RETURN()
|
|
|
|
_Requires_lock_held_(m_networkLock)
|
|
_Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::ResetHcnServiceSession() noexcept
|
|
try
|
|
{
|
|
if (!m_hcnCallback)
|
|
{
|
|
WSL_LOG("WslMirroredNetworkManager::ResetHcnServiceSession - attempting to re-register"); // Attempt to resubscribe to HNS notifications.
|
|
m_hcnCallback = windows::common::hcs::RegisterServiceCallback(HcnCallback, this);
|
|
|
|
// if we can reregister, reset the retry timer.
|
|
m_retryHcnServiceConnectionDurationMs = 0;
|
|
SetThreadpoolTimer(m_retryHcnServiceConnectionTimer.get(), nullptr, 0, 0);
|
|
|
|
std::vector<GUID> enumeratedNetworkIds;
|
|
try
|
|
{
|
|
// Refresh the current list of networks. The list will then be kept
|
|
// up to date by the subscription notifications.
|
|
enumeratedNetworkIds = EnumerateMirroredNetworks();
|
|
}
|
|
catch (...)
|
|
{
|
|
const auto hr = wil::ResultFromCaughtException();
|
|
WSL_LOG(
|
|
"ResetHcnServiceSessionFailed",
|
|
TraceLoggingValue(hr, "result"),
|
|
TraceLoggingValue("HcnEnumerateNetworks", "executionStep"),
|
|
TraceLoggingValue("Mirrored", "networkingMode"),
|
|
TraceLoggingValue(m_vmConfig.EnableDnsTunneling, "DnsTunnelingEnabled"),
|
|
TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), "HyperVFirewallEnabled"),
|
|
TraceLoggingValue(m_vmConfig.EnableAutoProxy, "AutoProxyFeatureEnabled")); // the feature is enabled, but we don't know if proxy settings are actually configured
|
|
|
|
throw;
|
|
}
|
|
|
|
wil::unique_cotaskmem_string response;
|
|
wil::unique_cotaskmem_string error;
|
|
const auto enumEndpointsHr = HcnEnumerateEndpoints(nullptr, &response, &error);
|
|
if (FAILED(enumEndpointsHr))
|
|
{
|
|
WSL_LOG(
|
|
"ResetHcnServiceSessionFailed",
|
|
TraceLoggingValue(enumEndpointsHr, "result"),
|
|
TraceLoggingValue("HcnEnumerateEndpoints", "executionStep"),
|
|
TraceLoggingValue("Mirrored", "networkingMode"),
|
|
TraceLoggingValue(m_vmConfig.EnableDnsTunneling, "DnsTunnelingEnabled"),
|
|
TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), "HyperVFirewallEnabled"),
|
|
TraceLoggingValue(m_vmConfig.EnableAutoProxy, "AutoProxyFeatureEnabled")); // the feature is enabled, but we don't know if proxy settings are actually configured
|
|
}
|
|
else
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::ResetHcnServiceSession - HcnEnumerateEndpoints",
|
|
TraceLoggingValue(response.get(), "response"));
|
|
}
|
|
|
|
for (const auto& networkId : enumeratedNetworkIds)
|
|
{
|
|
// Must call back through MirroredNetworking to create a new Endpoint
|
|
// note that the callback will not block - it just queues the work in MirroredNetworking
|
|
LOG_IF_FAILED(AddNetwork(networkId));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WSL_LOG("WslMirroredNetworkManager::ResetHcnServiceSession - already re-registered");
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
CATCH_RETURN()
|
|
|
|
void wsl::core::networking::WslMirroredNetworkManager::TelemetryConnectionCallback(NLM_CONNECTIVITY hostConnectivity, uint32_t telemetryCounter) noexcept
|
|
try
|
|
{
|
|
WSL_LOG("WslMirroredNetworkManager::TelemetryConnectionCallback");
|
|
|
|
const auto lock = m_networkLock.lock_exclusive();
|
|
if (m_state == State::Stopped)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// if this is the inital callback for checking container connectivity, push this through as telemetry, so we can observe the time-to-connect
|
|
if ((telemetryCounter > 1) && !(hostConnectivity & NLM_CONNECTIVITY_IPV4_INTERNET) && !(hostConnectivity & NLM_CONNECTIVITY_IPV6_INTERNET))
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::TelemetryConnectionCallback - not testing connectivity - host is not connected",
|
|
TraceLoggingValue(telemetryCounter, "telemetryCounter"),
|
|
TraceLoggingValue(wsl::core::networking::ToString(hostConnectivity).c_str(), "HostConnectivityLevel"));
|
|
return;
|
|
}
|
|
|
|
int returnedIPv4Value{};
|
|
LOG_IF_FAILED(m_gnsCallbackQueue.submit_and_wait([&] {
|
|
return m_callbackForGnsMessage(LxGnsMessageConnectTestRequest, c_ipv4TestRequestTarget, GnsCallbackFlags::Wait, &returnedIPv4Value);
|
|
}));
|
|
|
|
int returnedIPv6Value{};
|
|
LOG_IF_FAILED(m_gnsCallbackQueue.submit_and_wait([&] {
|
|
return m_callbackForGnsMessage(LxGnsMessageConnectTestRequest, c_ipv6TestRequestTarget, GnsCallbackFlags::Wait, &returnedIPv6Value);
|
|
}));
|
|
|
|
// make the same connect requests as we just requested from the container
|
|
const auto hostConnectivityCheck =
|
|
wsl::shared::conncheck::CheckConnection(c_ipv4TestRequestTargetA, c_ipv6TestRequestTargetA, "80");
|
|
const auto WindowsIpv4ConnCheckStatus = static_cast<uint32_t>(hostConnectivityCheck.Ipv4Status);
|
|
const auto WindowsIpv6ConnCheckStatus = static_cast<uint32_t>(hostConnectivityCheck.Ipv6Status);
|
|
const auto WindowsIPv4NlmConnectivityLevel = ConnectivityTelemetry::WindowsIPv4NlmConnectivityLevel(hostConnectivity);
|
|
const auto WindowsIPv6NlmConnectivityLevel = ConnectivityTelemetry::WindowsIPv6NlmConnectivityLevel(hostConnectivity);
|
|
const auto LinuxIPv4ConnCheckStatus = ConnectivityTelemetry::LinuxIPv4ConnCheckResult(returnedIPv4Value);
|
|
const auto LinuxIPv6ConnCheckStatus = ConnectivityTelemetry::LinuxIPv6ConnCheckResult(returnedIPv6Value);
|
|
|
|
const auto timeFromObjectCreation = std::chrono::steady_clock::now() - m_objectCreationTime;
|
|
|
|
// Logs when network connectivity changes, used to compare network connectivity in the guest to the host to determine networking health
|
|
WSL_LOG_TELEMETRY(
|
|
"TelemetryConnectionCallback",
|
|
PDT_ProductAndServicePerformance,
|
|
TraceLoggingValue("Mirrored", "networkingMode"),
|
|
TraceLoggingValue(telemetryCounter, "telemetryCounter"),
|
|
TraceLoggingValue(
|
|
(std::chrono::duration_cast<std::chrono::milliseconds>(timeFromObjectCreation)).count(), "timeFromObjectCreationMs"),
|
|
TraceLoggingValue(wsl::core::networking::ToString(hostConnectivity).c_str(), "HostConnectivityLevel"),
|
|
TraceLoggingValue(WindowsIPv4NlmConnectivityLevel, "WindowsIPv4ConnectivityLevel"),
|
|
TraceLoggingValue(WindowsIPv6NlmConnectivityLevel, "WindowsIPv6ConnectivityLevel"),
|
|
TraceLoggingValue(LinuxIPv4ConnCheckStatus, "LinuxIPv4ConnCheckStatus"),
|
|
TraceLoggingValue(LinuxIPv6ConnCheckStatus, "LinuxIPv6ConnCheckStatus"),
|
|
TraceLoggingValue(WindowsIpv4ConnCheckStatus, "WindowsIpv4ConnCheckStatus"),
|
|
TraceLoggingValue(WindowsIpv6ConnCheckStatus, "WindowsIpv6ConnCheckStatus"),
|
|
TraceLoggingValue(m_vmConfig.EnableDnsTunneling, "DnsTunnelingEnabled"),
|
|
TraceLoggingValue(m_dnsTunnelingIpAddress.c_str(), "DnsTunnelingIpAddress"),
|
|
TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), "HyperVFirewallEnabled"),
|
|
TraceLoggingValue(m_vmConfig.EnableAutoProxy, "AutoProxyFeatureEnabled")); // the feature is enabled, but we don't know if proxy settings are actually configured
|
|
}
|
|
CATCH_LOG()
|
|
|
|
void __stdcall wsl::core::networking::WslMirroredNetworkManager::HcnServiceConnectionTimerCallback(
|
|
_Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_TIMER) noexcept
|
|
{
|
|
WSL_LOG("WslMirroredNetworkManager::HcnServiceConnectionTimerCallback");
|
|
|
|
auto* const manager = static_cast<WslMirroredNetworkManager*>(Context);
|
|
|
|
const auto lock = manager->m_networkLock.lock_exclusive();
|
|
WI_ASSERT(manager->m_state == State::Started);
|
|
if (manager->m_state == State::Stopped)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (FAILED(manager->ResetHcnServiceSession()))
|
|
{
|
|
// The retry attempt was unsuccessful, re-arm the timer to try again.
|
|
LOG_IF_FAILED(manager->UpdateHcnServiceTimer());
|
|
}
|
|
}
|
|
|
|
void CALLBACK wsl::core::networking::WslMirroredNetworkManager::HcnCallback(
|
|
_In_ DWORD NotificationType, _In_opt_ void* Context, _In_ HRESULT, _In_opt_ PCWSTR NotificationData) noexcept
|
|
try
|
|
{
|
|
hns::NotificationBase data = {};
|
|
if (NotificationType == HcnNotificationNetworkCreate || NotificationType == HcnNotificationNetworkPreDelete)
|
|
{
|
|
data = FromJson<hns::NotificationBase>(NotificationData);
|
|
}
|
|
|
|
auto* const manager = static_cast<WslMirroredNetworkManager*>(Context);
|
|
|
|
const auto lock = manager->m_networkLock.lock_exclusive();
|
|
WI_ASSERT(manager->m_state == State::Started);
|
|
if (manager->m_state == State::Stopped)
|
|
{
|
|
return;
|
|
}
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::HcnCallback [HcnRegisterServiceCallback]",
|
|
TraceLoggingValue(NotificationType, "notificationType"),
|
|
TraceLoggingValue(wsl::windows::common::stringify::HcnNotificationsToString(NotificationType), "notificationTypeString"),
|
|
TraceLoggingValue(data.ID, "networkId"),
|
|
TraceLoggingValue(data.Flags, "flags"),
|
|
TraceLoggingValue(NotificationData, "notificationData"));
|
|
|
|
switch (NotificationType)
|
|
{
|
|
case HcnNotificationNetworkCreate:
|
|
{
|
|
// convert the enum to integer to allow for bitmap comparisons
|
|
if (!WI_IsFlagSet(data.Flags, WI_EnumValue(hns::NetworkFlags::EnableFlowSteering)))
|
|
{
|
|
WSL_LOG("WslMirroredNetworkManager::HcnCallback [HcnRegisterServiceCallback] - not a mirrored network");
|
|
return;
|
|
}
|
|
|
|
LOG_IF_FAILED(manager->AddNetwork(data.ID));
|
|
break;
|
|
}
|
|
|
|
case HcnNotificationNetworkPreDelete:
|
|
{
|
|
// This notification is fired off right before HNS network deletion.
|
|
// Ensure Containers release endpoints whether network deletion
|
|
// is successful or not.
|
|
LOG_IF_FAILED(manager->RemoveNetwork(data.ID));
|
|
break;
|
|
}
|
|
|
|
case HcnNotificationServiceDisconnect:
|
|
{
|
|
// This notification indicates that the subscription has become invalid due to a loss
|
|
// of connection to the server. This typically means that the HNS service has been
|
|
// stopped or restarted.
|
|
manager->m_hcnCallback.reset();
|
|
|
|
manager->m_networkEndpoints.clear();
|
|
|
|
LOG_IF_FAILED(manager->UpdateHcnServiceTimer());
|
|
break;
|
|
}
|
|
}
|
|
manager->UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, "HcnCallback");
|
|
}
|
|
CATCH_LOG()
|
|
|
|
const char* wsl::core::networking::WslMirroredNetworkManager::StateToString(State state) noexcept
|
|
{
|
|
switch (state)
|
|
{
|
|
case State::Stopped:
|
|
return "Stopped";
|
|
case State::Started:
|
|
return "Started";
|
|
case State::Starting:
|
|
return "Starting";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
void wsl::core::networking::WslMirroredNetworkManager::TraceLoggingRundown() const
|
|
{
|
|
auto lock = m_networkLock.lock_shared();
|
|
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::TraceLoggingRundown",
|
|
TraceLoggingValue("Global State"),
|
|
TraceLoggingValue(StateToString(m_state), "state"),
|
|
TraceLoggingValue(GenerateResolvConf(m_trackedDnsInfo).c_str(), "dnsInfo"));
|
|
|
|
for (const auto& network : m_networkEndpoints)
|
|
{
|
|
WSL_LOG("WslMirroredNetworkManager::TraceLoggingRundown", TRACE_NETWORKSETTINGS_OBJECT(network.Network));
|
|
|
|
if (network.StateTracking)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::TraceLoggingRundown",
|
|
TraceLoggingValue("IpStateTracking Interface Info"),
|
|
TraceLoggingValue(network.StateTracking->InterfaceGuid, "interfaceGuid"),
|
|
TraceLoggingValue(network.StateTracking->InterfaceMtu, "mtu"));
|
|
|
|
for (const auto& address : network.StateTracking->IpAddresses)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::TraceLoggingRundown",
|
|
TraceLoggingValue("IpStateTracking::IpAddresses"),
|
|
TraceLoggingValue(address.Address.AddressString.c_str(), "address"),
|
|
TraceLoggingValue(address.Address.PrefixLength, "prefixLength"),
|
|
TraceLoggingValue(wsl::core::networking::ToString(address.SyncStatus), "syncStatus"),
|
|
TraceLoggingValue(address.SyncRetryCount, "syncRetryCount"),
|
|
TraceLoggingValue(address.LoopbackSyncRetryCount, "loopbackSyncRetryCount"));
|
|
}
|
|
|
|
for (const auto& route : network.StateTracking->Routes)
|
|
{
|
|
WSL_LOG(
|
|
"WslMirroredNetworkManager::TraceLoggingRundown",
|
|
TraceLoggingValue("IpStateTracking::Routes"),
|
|
TraceLoggingValue(route.Route.ToString().c_str(), "route"),
|
|
TraceLoggingValue(route.Route.Metric, "metric"),
|
|
TraceLoggingValue(
|
|
!route.CanConflictWithLinuxAutoGenRoute() || route.LinuxConflictRemoved,
|
|
"linuxConflictRemovedOrDoesntExist"),
|
|
TraceLoggingValue(wsl::core::networking::ToString(route.SyncStatus), "syncStatus"),
|
|
TraceLoggingValue(route.SyncRetryCount, "syncRetryCount"));
|
|
}
|
|
}
|
|
}
|
|
}
|