master
cpp 2,720 lines 127 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 WslMirroredNetworking.cpp
8
9 Abstract:
10
11 This file contains WSL mirrored networking function definitions.
12
13 --*/
14
15 #include "precomp.h"
16 #include "WslMirroredNetworking.h"
17 #include "WslCoreMessageQueue.h"
18 #include "Stringify.h"
19 #include "WslCoreNetworkingSupport.h"
20 #include "WslCoreNetworkEndpointSettings.h"
21 #include "WslCoreHostDnsInfo.h"
22 #include "hcs.hpp"
23 #include "hns_schema.h"
24
25 static constexpr auto c_loopbackDeviceName = TEXT(LX_INIT_LOOPBACK_DEVICE_NAME);
26 static constexpr auto c_initialMirroredGoalStateWaitTimeoutMs = 5 * 1000;
27
28 using namespace wsl::windows::common;
29 using namespace wsl::shared;
30 using wsl::core::networking::EndpointIpAddress;
31 using wsl::core::networking::EndpointRoute;
32
33 namespace {
34 inline const auto HnsModifyRequestTypeToString(const hns::ModifyRequestType requestType)
35 {
36 return JsonEnumToString<hns::ModifyRequestType>(requestType);
37 }
38 } // namespace
39
40 _Requires_lock_held_(m_networkLock)
41 void wsl::core::networking::WslMirroredNetworkManager::ProcessConnectivityChange()
42 {
43 const std::set<GUID, wsl::windows::common::helpers::GuidLess> initialConnectedInterfaces{std::move(m_hostConnectedInterfaces)};
44 m_hostConnectedInterfaces.clear();
45
46 const auto coInit = wil::CoInitializeEx();
47 const wil::com_ptr<INetworkListManager> networkListManager = wil::CoCreateInstance<NetworkListManager, INetworkListManager>();
48
49 wil::com_ptr<IEnumNetworks> networksEnumerator;
50 THROW_IF_FAILED(networkListManager->GetNetworks(NLM_ENUM_NETWORK_CONNECTED, &networksEnumerator));
51
52 for (;;)
53 {
54 ULONG fetched{};
55 wil::com_ptr<INetwork> networkInstance;
56 auto hr = networksEnumerator->Next(1, &networkInstance, &fetched);
57 THROW_IF_FAILED(hr);
58 if (hr == S_FALSE || fetched == 0)
59 {
60 break;
61 }
62
63 // each NLM network could have multiple interfaces - walk through each
64 // if we fail trying to access an individual interface, continue the loop for the other interfaces
65
66 wil::com_ptr<IEnumNetworkConnections> enumNetworkConnections;
67 hr = networkInstance->GetNetworkConnections(&enumNetworkConnections);
68 if (FAILED(hr))
69 {
70 WSL_LOG(
71 "WslMirroredNetworkManager::ProcessConnectivityChange - ignoring interface after processing "
72 "INetworkConnection::GetAdapterId",
73 TraceLoggingValue(hr, "hr"));
74 continue;
75 }
76
77 for (;;)
78 {
79 ULONG fetchedNetworkConnections{};
80 wil::com_ptr<INetworkConnection> networkConnection;
81 hr = enumNetworkConnections->Next(1, &networkConnection, &fetchedNetworkConnections);
82 if (FAILED(hr) || hr == S_FALSE || fetchedNetworkConnections == 0)
83 {
84 break;
85 }
86
87 GUID interfaceGuid{};
88 hr = networkConnection->GetAdapterId(&interfaceGuid);
89 if (FAILED(hr))
90 {
91 WSL_LOG(
92 "WslMirroredNetworkManager::ProcessConnectivityChange - ignoring interface INetworkConnection::GetAdapterId "
93 "failed",
94 TraceLoggingValue(hr, "hr"));
95 continue;
96 }
97
98 NLM_CONNECTIVITY connectivity{};
99 hr = networkConnection->GetConnectivity(&connectivity);
100 if (FAILED(hr) || connectivity == NLM_CONNECTIVITY_DISCONNECTED)
101 {
102 WSL_LOG(
103 "WslMirroredNetworkManager::ProcessConnectivityChange - ignoring interface after processing "
104 "INetworkConnection::GetConnectivity",
105 TraceLoggingValue(wsl::shared::string::GuidToString<wchar_t>(interfaceGuid).c_str(), "interfaceGuid"),
106 TraceLoggingValue(connectivity == NLM_CONNECTIVITY_DISCONNECTED, "is_NLM_CONNECTIVITY_DISCONNECTED"),
107 TraceLoggingValue(hr, "hr"));
108 continue;
109 }
110
111 m_hostConnectedInterfaces.insert(interfaceGuid);
112 }
113 }
114
115 if (initialConnectedInterfaces != m_hostConnectedInterfaces)
116 {
117 WSL_LOG(
118 "WslMirroredNetworkManager::ProcessConnectivityChange - reset goal state",
119 TraceLoggingValue(initialConnectedInterfaces.size(), "previous_interfaces_size"),
120 TraceLoggingValue(m_hostConnectedInterfaces.size(), "updated_interfaces_size"));
121
122 m_inMirroredGoalState.ResetEvent();
123 m_connectivityTelemetry.UpdateTimer();
124
125 std::wstring guids;
126 for (const auto& connectedInterface : initialConnectedInterfaces)
127 {
128 guids.append(wsl::shared::string::GuidToString<wchar_t>(connectedInterface) + L",");
129 }
130
131 WSL_LOG(
132 "WslMirroredNetworkManager::ProcessConnectivityChange [previous]",
133 TraceLoggingValue(guids.c_str(), "connectedInterfaces"));
134
135 guids.clear();
136 for (const auto& connectedInterface : m_hostConnectedInterfaces)
137 {
138 guids.append(wsl::shared::string::GuidToString<wchar_t>(connectedInterface) + L",");
139 }
140
141 WSL_LOG(
142 "WslMirroredNetworkManager::ProcessConnectivityChange [updated]",
143 TraceLoggingValue(guids.c_str(), "connectedInterfaces"));
144 }
145 }
146
147 _Requires_lock_held_(m_networkLock)
148 void wsl::core::networking::WslMirroredNetworkManager::ProcessIpAddressChange()
149 {
150 wsl::core::networking::unique_address_table addressTable{};
151 THROW_IF_WIN32_ERROR(GetUnicastIpAddressTable(AF_UNSPEC, &addressTable));
152
153 for (auto& endpoint : m_networkEndpoints)
154 {
155 const auto initialAddresses{std::move(endpoint.Network->IpAddresses)};
156 endpoint.Network->IpAddresses.clear();
157
158 // if the interface isn't connected, ensure we always track zero addresses
159 if (!endpoint.Network->IsConnected)
160 {
161 continue;
162 }
163
164 for (const auto& address : wil::make_range(addressTable.get()->Table, addressTable.get()->NumEntries))
165 {
166 if (address.InterfaceIndex != endpoint.Network->InterfaceIndex)
167 {
168 continue;
169 }
170
171 const auto endpointAddress = EndpointIpAddress(address);
172 if (endpointAddress.IsPreferred())
173 {
174 endpoint.Network->IpAddresses.insert(endpointAddress);
175 }
176 }
177
178 if (initialAddresses != endpoint.Network->IpAddresses)
179 {
180 WSL_LOG(
181 "WslMirroredNetworkManager::ProcessIpAddressChange - reset goal state",
182 TraceLoggingValue(endpoint.EndpointId, "endpointId"),
183 TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
184 TraceLoggingValue(initialAddresses.size(), "previous_addresses_size"),
185 TraceLoggingValue(endpoint.Network->IpAddresses.size(), "updated_addresses_size"));
186
187 m_inMirroredGoalState.ResetEvent();
188 m_connectivityTelemetry.UpdateTimer();
189
190 for (const auto& address : initialAddresses)
191 {
192 WSL_LOG(
193 "WslMirroredNetworkManager::ProcessIpAddressChange [previous]",
194 TraceLoggingValue(endpoint.EndpointId, "endpointId"),
195 TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
196 TraceLoggingValue(address.AddressString.c_str(), "address"),
197 TraceLoggingValue(address.PrefixLength, "prefixLength"));
198 }
199 for (const auto& address : endpoint.Network->IpAddresses)
200 {
201 WSL_LOG(
202 "WslMirroredNetworkManager::ProcessIpAddressChange [updated]",
203 TraceLoggingValue(endpoint.EndpointId, "endpointId"),
204 TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
205 TraceLoggingValue(address.AddressString.c_str(), "address"),
206 TraceLoggingValue(address.PrefixLength, "prefixLength"));
207 }
208 }
209 }
210 }
211
212 _Requires_lock_held_(m_networkLock)
213 void wsl::core::networking::WslMirroredNetworkManager::ProcessRouteChange()
214 {
215 wsl::core::networking::unique_address_table addressTable{};
216 wsl::core::networking::unique_forward_table routeTable{};
217 THROW_IF_WIN32_ERROR(GetIpForwardTable2(AF_UNSPEC, &routeTable));
218
219 for (auto& endpoint : m_networkEndpoints)
220 {
221 const auto initialRoutes = endpoint.Network->Routes;
222 endpoint.Network->Routes.clear();
223
224 // if the interface isn't connected, ensure we always track zero routes
225 // Windows can have routes assigned on disconnected interfaces, Linux cannot
226 if (!endpoint.Network->IsConnected)
227 {
228 continue;
229 }
230
231 // Gather endpoint address prefixes and raw address
232 std::unordered_set<std::wstring> addressPrefixes{};
233 std::unordered_set<std::wstring> addresses{};
234 std::unordered_set<std::wstring> ipv4broadcastAddresses{};
235
236 for (const auto& endpointAddress : endpoint.Network->IpAddresses)
237 {
238 addresses.insert(endpointAddress.AddressString);
239
240 auto addressPrefix = endpointAddress.GetPrefix();
241 WI_ASSERT(!addressPrefix.empty());
242 if (!addressPrefix.empty())
243 {
244 addressPrefixes.insert(std::move(addressPrefix));
245 }
246
247 if (endpointAddress.Address.si_family == AF_INET)
248 {
249 auto v4BroadcastMaskAddress = endpointAddress.GetIpv4BroadcastMask();
250 WI_ASSERT(!v4BroadcastMaskAddress.empty());
251 if (!v4BroadcastMaskAddress.empty())
252 {
253 ipv4broadcastAddresses.emplace(std::move(v4BroadcastMaskAddress));
254 }
255 }
256 }
257
258 for (const auto& route : wil::make_range(routeTable.get()->Table, routeTable.get()->NumEntries))
259 {
260 if (route.InterfaceIndex == endpoint.Network->InterfaceIndex)
261 {
262 auto endpointRoute = EndpointRoute(route);
263
264 endpointRoute.IsAutoGeneratedPrefixRoute =
265 endpointRoute.IsNextHopOnlink() && addressPrefixes.contains(endpointRoute.GetFullDestinationPrefix());
266
267 // Ignore host IPv4 routes, e.g. 192.168.5.2/32 -> 0.0.0.0
268 if (addresses.contains(endpointRoute.DestinationPrefixString))
269 {
270 continue;
271 }
272
273 // ignore host routes for deprecated addresses
274 // the address will not be in the 'addresses' variable above since it's deprecated
275 // e.g. a route 2001:0:d5b:9458:1ceb:518b:7c94:609e/128, but the matching local IP address is deprecated
276 bool shouldIgnoreUnicastAddressRoute = false;
277 if (endpointRoute.IsUnicastAddressRoute())
278 {
279 if (!addressTable)
280 {
281 THROW_IF_WIN32_ERROR(GetUnicastIpAddressTable(AF_UNSPEC, &addressTable));
282 }
283 // find the address matching this destination prefix
284 for (const auto& address : wil::make_range(addressTable.get()->Table, addressTable.get()->NumEntries))
285 {
286 if (address.InterfaceIndex != endpoint.Network->InterfaceIndex)
287 {
288 continue;
289 }
290
291 const auto endpointAddress = EndpointIpAddress(address);
292 if (endpointAddress.Address == route.DestinationPrefix.Prefix)
293 {
294 if (!endpointAddress.IsPreferred())
295 {
296 shouldIgnoreUnicastAddressRoute = true;
297 break;
298 }
299 }
300 }
301 }
302 if (shouldIgnoreUnicastAddressRoute)
303 {
304 continue;
305 }
306
307 if (endpointRoute.DestinationPrefix.Prefix.si_family == AF_INET)
308 {
309 if (endpoint.Network->DisableIpv4DefaultRoutes && endpointRoute.IsDefault())
310 {
311 continue;
312 }
313
314 const auto addressType =
315 Ipv4AddressType(reinterpret_cast<const UCHAR*>(&endpointRoute.DestinationPrefix.Prefix.Ipv4.sin_addr));
316 if (addressType != NlatUnspecified && addressType != NlatUnicast)
317 {
318 // ignore broadcast and multicast routes - Linux doesn't seem to create those like Windows
319 continue;
320 }
321
322 if (ipv4broadcastAddresses.contains(endpointRoute.DestinationPrefixString))
323 {
324 continue;
325 }
326 }
327 else if (endpointRoute.DestinationPrefix.Prefix.si_family == AF_INET6)
328 {
329 if (endpoint.Network->DisableIpv6DefaultRoutes && endpointRoute.IsDefault())
330 {
331 continue;
332 }
333
334 const auto addressType =
335 Ipv6AddressType(reinterpret_cast<const UCHAR*>(&endpointRoute.DestinationPrefix.Prefix.Ipv6.sin6_addr));
336 if (addressType != NlatUnspecified && addressType != NlatUnicast)
337 {
338 // ignore broadcast and multicast routes - Linux doesn't seem to create those like Windows
339 continue;
340 }
341 }
342
343 // update the route metric for Linux - which to be equivalent to Windows must be the sum of the interface metric and route metric
344 endpointRoute.Metric += (endpointRoute.Family == AF_INET) ? endpoint.Network->IPv4InterfaceMetric.value_or(0)
345 : endpoint.Network->IPv6InterfaceMetric.value_or(0);
346 if (endpointRoute.Metric > UINT16_MAX)
347 {
348 endpointRoute.Metric = UINT16_MAX;
349 }
350
351 // Some Windows interfaces (like VPNs) can have metric 0 and routes over that interface with metric also 0, adding
352 // up to 0. Linux treats metric 0 as unspecified and will default to a 1024 metric. The highest priority metric in
353 // Linux is 1 instead so we need to switch the metric from 0 to 1.
354 if (endpointRoute.Metric == 0)
355 {
356 endpointRoute.Metric = 1;
357 }
358
359 endpoint.Network->Routes.insert(endpointRoute);
360 }
361 }
362
363 // Linux requires that there's an onlink route for any route with a NextHop address that's not all-zeros (on-link)
364 // "normal" network deployments with Windows creates an address prefix route that includes that next hop
365 // but some deployments, like some VPNs, do not include a prefix route that includes the nexthop
366 // While that works in Windows (all nexthop addresses in a route *must* be on-link), it won't work in Linux
367 // thus we must guarantee an onlink route for all routes with a non-zero nexthop
368 std::vector<EndpointRoute> newRoutes;
369 for (const auto& route : endpoint.Network->Routes)
370 {
371 if (!route.IsNextHopOnlink())
372 {
373 EndpointRoute newRoute;
374 newRoute.Family = route.Family;
375 newRoute.Metric = route.Metric;
376 newRoute.SitePrefixLength = route.GetMaxPrefixLength();
377
378 // update the destination prefix to the nexthop address /32 (for ipv4) or /128 (for ipv6)
379 newRoute.DestinationPrefix.Prefix = route.NextHop;
380 newRoute.DestinationPrefix.PrefixLength = route.GetMaxPrefixLength();
381 newRoute.DestinationPrefixString = windows::common::string::SockAddrInetToWstring(newRoute.DestinationPrefix.Prefix);
382
383 // update the destination prefix to be all zeros (on-link)
384 ZeroMemory(&newRoute.NextHop, sizeof newRoute.NextHop);
385 newRoute.NextHop.si_family = route.NextHop.si_family;
386 newRoute.NextHopString = windows::common::string::SockAddrInetToWstring(newRoute.NextHop);
387
388 // force a copy so the route strings are re-calculated in the new EndpointRoute object
389 newRoutes.emplace_back(std::move(newRoute));
390 }
391 }
392 for (const auto& route : newRoutes)
393 {
394 endpoint.Network->Routes.insert(route);
395 }
396
397 if (initialRoutes != endpoint.Network->Routes)
398 {
399 WSL_LOG(
400 "WslMirroredNetworkManager::ProcessRouteChange - reset goal state",
401 TraceLoggingValue(endpoint.EndpointId, "endpointId"),
402 TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
403 TraceLoggingValue(initialRoutes.size(), "previous_routes_size"),
404 TraceLoggingValue(endpoint.Network->Routes.size(), "updated_routes_size"));
405
406 m_inMirroredGoalState.ResetEvent();
407 m_connectivityTelemetry.UpdateTimer();
408
409 for (const auto& route : initialRoutes)
410 {
411 WSL_LOG(
412 "WslMirroredNetworkManager::ProcessRouteChange [previous]",
413 TraceLoggingValue(endpoint.EndpointId, "endpointId"),
414 TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
415 TraceLoggingValue(route.Metric, "metric"),
416 TraceLoggingValue(route.NextHopString.c_str(), "nextHop"),
417 TraceLoggingValue(route.DestinationPrefixString.c_str(), "destinationPrefix"),
418 TraceLoggingValue(route.DestinationPrefix.PrefixLength, "destinationPrefixLength"));
419 }
420 for (const auto& route : endpoint.Network->Routes)
421 {
422 WSL_LOG(
423 "WslMirroredNetworkManager::ProcessRouteChange [updated]",
424 TraceLoggingValue(endpoint.EndpointId, "endpointId"),
425 TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
426 TraceLoggingValue(route.Metric, "metric"),
427 TraceLoggingValue(route.NextHopString.c_str(), "nextHop"),
428 TraceLoggingValue(route.DestinationPrefixString.c_str(), "destinationPrefix"),
429 TraceLoggingValue(route.DestinationPrefix.PrefixLength, "destinationPrefixLength"));
430 }
431 }
432 }
433 }
434
435 _Requires_lock_held_(m_networkLock)
436 void wsl::core::networking::WslMirroredNetworkManager::ProcessDNSChange()
437 {
438 const auto initialDnsInfo = m_dnsInfo;
439
440 if (m_vmConfig.EnableDnsTunneling)
441 {
442 m_dnsInfo = wsl::core::networking::HostDnsInfo::GetDnsTunnelingSettings(m_dnsTunnelingIpAddress);
443 }
444 else
445 {
446 m_dnsInfo = wsl::core::networking::HostDnsInfo::GetDnsSettings(
447 wsl::core::networking::DnsSettingsFlags::IncludeVpn | wsl::core::networking::DnsSettingsFlags::IncludeIpv6Servers |
448 wsl::core::networking::DnsSettingsFlags::IncludeAllSuffixes);
449 }
450
451 if (initialDnsInfo != m_dnsInfo)
452 {
453 WSL_LOG("WslMirroredNetworkManager::ProcessDNSChange - reset goal state");
454 m_inMirroredGoalState.ResetEvent();
455 m_connectivityTelemetry.UpdateTimer();
456
457 WSL_LOG(
458 "WslMirroredNetworkManager::ProcessDNSChange [previous]",
459 TraceLoggingValue(wsl::shared::string::Join(initialDnsInfo.Domains, ',').c_str(), "domainList"),
460 TraceLoggingValue(wsl::shared::string::Join(initialDnsInfo.Servers, ',').c_str(), "dnsServerList"));
461
462 WSL_LOG(
463 "WslMirroredNetworkManager::ProcessDNSChange [updated]",
464 TraceLoggingValue(wsl::shared::string::Join(m_dnsInfo.Domains, ',').c_str(), "domainList"),
465 TraceLoggingValue(wsl::shared::string::Join(m_dnsInfo.Servers, ',').c_str(), "dnsServerList"));
466 }
467 }
468
469 _Requires_lock_held_(m_networkLock)
470 void wsl::core::networking::WslMirroredNetworkManager::ProcessInterfaceChange()
471 {
472 wsl::core::networking::unique_interface_table interfaceTable{};
473 THROW_IF_WIN32_ERROR(::GetIpInterfaceTable(AF_UNSPEC, &interfaceTable));
474
475 for (auto& endpoint : m_networkEndpoints)
476 {
477 const auto originalIPv4DisableDefaultRoutes = endpoint.Network->DisableIpv4DefaultRoutes;
478 const auto originalIPv6DisableDefaultRoutes = endpoint.Network->DisableIpv6DefaultRoutes;
479 const auto originallyConnected = endpoint.Network->IsConnected;
480 const auto originalMinimumMtu = endpoint.Network->GetEffectiveMtu();
481 const auto originalMinimumMetric = endpoint.Network->GetMinimumMetric();
482
483 endpoint.Network->IsConnected = false;
484
485 auto interfaceFoundCount = 0;
486 for (const auto& ipInterface : wil::make_range(interfaceTable.get()->Table, interfaceTable.get()->NumEntries))
487 {
488 if (ipInterface.InterfaceIndex != endpoint.Network->InterfaceIndex ||
489 (ipInterface.Family != AF_INET && ipInterface.Family != AF_INET6))
490 {
491 continue;
492 }
493
494 // Endpoint is marked as connected if either IPv4 or IPv6 interface is connected
495 endpoint.Network->IsConnected = endpoint.Network->IsConnected || !!ipInterface.Connected;
496
497 if (ipInterface.Family == AF_INET)
498 {
499 endpoint.Network->IPv4InterfaceMtu = ipInterface.NlMtu;
500 endpoint.Network->IPv4InterfaceMetric = ipInterface.Metric;
501 endpoint.Network->DisableIpv4DefaultRoutes = ipInterface.DisableDefaultRoutes;
502 }
503 else
504 {
505 endpoint.Network->IPv6InterfaceMtu = ipInterface.NlMtu;
506 endpoint.Network->IPv6InterfaceMetric = ipInterface.Metric;
507 endpoint.Network->DisableIpv6DefaultRoutes = ipInterface.DisableDefaultRoutes;
508 }
509
510 ++interfaceFoundCount;
511 if (interfaceFoundCount > 1)
512 {
513 // we already found both v4 and v6
514 break;
515 }
516 }
517
518 const auto disableDefaultRoutesUpdated = originalIPv4DisableDefaultRoutes != endpoint.Network->DisableIpv4DefaultRoutes ||
519 originalIPv6DisableDefaultRoutes != endpoint.Network->DisableIpv6DefaultRoutes;
520 const auto connectedStateUpdated = originallyConnected != endpoint.Network->IsConnected;
521 const auto minimumMtu = endpoint.Network->GetEffectiveMtu();
522 const auto mtuUpdated = originalMinimumMtu != minimumMtu;
523 const auto minimumMetric = endpoint.Network->GetMinimumMetric();
524 const auto metricUpdate = originalMinimumMetric != minimumMetric;
525
526 endpoint.Network->PendingIPInterfaceUpdate |= connectedStateUpdated || mtuUpdated || metricUpdate;
527
528 if (disableDefaultRoutesUpdated || connectedStateUpdated || mtuUpdated || metricUpdate)
529 {
530 // we want to trace when disableDefaultRoutesUpdated, but that won't trigger resetting the goal-state
531 // if disableDefaultRoutesUpdated affects routes, then ProcessRouteChange will reset the goal-state accordingly
532 // but we do want to trace when disableDefaultRoutes get updated - to greatly help debugging
533 if (connectedStateUpdated || mtuUpdated || metricUpdate)
534 {
535 WSL_LOG("WslMirroredNetworkManager::ProcessInterfaceChange - reset goal state");
536 m_inMirroredGoalState.ResetEvent();
537 m_connectivityTelemetry.UpdateTimer();
538 }
539
540 WSL_LOG(
541 "WslMirroredNetworkManager::ProcessInterfaceChange [previous]",
542 TraceLoggingValue(endpoint.EndpointId, "endpointId"),
543 TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
544 TraceLoggingValue(originallyConnected, "isConnected"),
545 TraceLoggingValue(originalMinimumMtu, "EffectiveMtu"),
546 TraceLoggingValue(originalMinimumMetric, "MinimumMetric"),
547 TraceLoggingValue(originalIPv4DisableDefaultRoutes, "disableIpv4DefaultRoutes"),
548 TraceLoggingValue(originalIPv6DisableDefaultRoutes, "disableIpv6DefaultRoutes"));
549
550 WSL_LOG(
551 "WslMirroredNetworkManager::ProcessInterfaceChange [updated]",
552 TraceLoggingValue(endpoint.EndpointId, "endpointId"),
553 TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
554 TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
555 TraceLoggingValue(endpoint.Network->IPv4InterfaceMtu, "ipv4InterfaceMtu"),
556 TraceLoggingValue(endpoint.Network->IPv6InterfaceMtu, "ipv6InterfaceMtu"),
557 TraceLoggingValue(endpoint.Network->IPv4InterfaceMetric.value_or(0xffffffff), "IPv4InterfaceMetric"),
558 TraceLoggingValue(endpoint.Network->IPv6InterfaceMetric.value_or(0xffffffff), "IPv6InterfaceMetric"),
559 TraceLoggingValue(endpoint.Network->DisableIpv4DefaultRoutes, "disableIpv4DefaultRoutes"),
560 TraceLoggingValue(endpoint.Network->DisableIpv6DefaultRoutes, "disableIpv6DefaultRoutes"));
561 }
562 }
563 }
564
565 wsl::core::networking::WslMirroredNetworkManager::WslMirroredNetworkManager(
566 HCS_SYSTEM hcsSystem,
567 const Config& config,
568 GnsMessageCallbackWithCallbackResult&& GnsMessageCallbackWithCallbackResult,
569 AddNetworkEndpointCallback&& addNetworkEndpointCallback,
570 const std::pair<uint16_t, uint16_t>& ephemeralPortRange) :
571 m_callbackForGnsMessage(std::move(GnsMessageCallbackWithCallbackResult)),
572 m_addNetworkEndpointCallback(std::move(addNetworkEndpointCallback)),
573 m_hcsSystem{hcsSystem},
574 m_vmConfig{config},
575 m_ephemeralPortRange(ephemeralPortRange),
576 m_state(State::Starting)
577 {
578 // ensure the MTA apartment stays alive for the lifetime of this object in this process
579 // we do not want to risk COM unloading / reloading when we need to make our WinRT API calls
580 // which by default will be in the MTA
581 LOG_IF_FAILED(CoIncrementMTAUsage(&m_mtaCookie));
582
583 // locking in the c'tor in case any of the below callbacks fire before this object is fully constructed
584 const auto lock = m_networkLock.lock_exclusive();
585
586 // keep the WinRT DLL loaded for the lifetime of this instance. we instantiate it repeatedly,
587 // and today we are loading and unloading 7 dll's over and over again - each time we call it.
588 // this also circumvents many performance optimizations we made with our WinRT API
589 const auto roInit = wil::RoInitialize();
590 m_networkInformationStatics = wil::GetActivationFactory<ABI::Windows::Networking::Connectivity::INetworkInformationStatics>(
591 RuntimeClass_Windows_Networking_Connectivity_NetworkInformation);
592
593 m_netListManager = wil::CoCreateInstance<NetworkListManager, INetworkListManager>();
594 // create an event sink for NLM Network change notifications, then register (Advise) with NLM
595 m_netListManagerEventSink = wil::com_ptr<INetworkEvents>(Microsoft::WRL::Make<PublicNLMSink>(this));
596 // INetworkListManager is actually an inproc COM API - it just calls private COM APIs which are hosted in a service
597 m_netListManagerAdviseHandler.AdviseInProcObject<INetworkEvents>(m_netListManager, m_netListManagerEventSink.get());
598
599 // Subscribe for network change notifications. This is done before
600 // obtaining the initial list of networks to connect to, in order to
601 // avoid a race condition between the initial enumeration and any network
602 // changes that may be occurring at the same time. The subscription will
603 // receive network change events, but will not be able to react to them
604 // the lock is released.
605 m_hcnCallback = windows::common::hcs::RegisterServiceCallback(HcnCallback, this);
606
607 // Create the timer used to retry the HNS service connection.
608 m_retryHcnServiceConnectionTimer.reset(CreateThreadpoolTimer(HcnServiceConnectionTimerCallback, this, nullptr));
609 THROW_IF_NULL_ALLOC(m_retryHcnServiceConnectionTimer);
610
611 // Create the timer used to retry syncing pending IP state with Linux.
612 m_retryLinuxIpStateSyncTimer.reset(CreateThreadpoolTimer(RetryLinuxIpStateSyncTimerCallback, this, nullptr));
613 THROW_IF_NULL_ALLOC(m_retryLinuxIpStateSyncTimer);
614
615 m_debounceUpdateAllEndpointsDefaultTimer.reset(CreateThreadpoolTimer(DebounceUpdateAllEndpointsDefaultTimerFired, this, nullptr));
616 THROW_IF_NULL_ALLOC(m_debounceUpdateAllEndpointsDefaultTimer);
617
618 m_debounceCreateEndpointFailureTimer.reset(CreateThreadpoolTimer(DebounceCreateEndpointFailureTimerFired, this, nullptr));
619 THROW_IF_NULL_ALLOC(m_debounceCreateEndpointFailureTimer);
620
621 // Populate the initial list of networks. The list will then be kept
622 // up to date by the above subscription notifications.
623 for (const auto& networkId : EnumerateMirroredNetworks())
624 {
625 // Must call back through MirroredNetworking to create a new Endpoint
626 // note that the callback will not block - it just queues the work in MirroredNetworking
627 LOG_IF_FAILED(AddNetwork(networkId));
628 }
629
630 // once HNS has started creating networks, start our telemetry timer
631 if (config.EnableTelemetry && !WslTraceLoggingShouldDisableTelemetry())
632 {
633 m_connectivityTelemetry.StartTimer([&](NLM_CONNECTIVITY hostConnectivity, uint32_t telemetryCounter) {
634 TelemetryConnectionCallback(hostConnectivity, telemetryCounter);
635 });
636 }
637
638 if (config.DnsTunnelingIpAddress.has_value())
639 {
640 m_dnsTunnelingIpAddress = wsl::windows::common::string::IntegerIpv4ToWstring(config.DnsTunnelingIpAddress.value());
641 }
642
643 m_state = State::Started;
644 }
645
646 wsl::core::networking::WslMirroredNetworkManager::~WslMirroredNetworkManager() noexcept
647 {
648 Stop();
649 }
650
651 wsl::core::networking::WslMirroredNetworkManager::HnsStatus wsl::core::networking::WslMirroredNetworkManager::Stop() noexcept
652 {
653 HnsStatus returnStatus{};
654 try
655 {
656 // scope to the lock to flip the bit that we are stopping
657 {
658 const auto lock = m_networkLock.lock_exclusive();
659 m_state = State::Stopped;
660 returnStatus = m_latestHnsStatus;
661 }
662
663 // must set state first so all other threads won't make forward progress
664 // since we are about to stop all timers and callbacks
665 // which must be stopped not holding our lock
666
667 // Next stop the telemetry timer which could queue work to linux (through m_gnsCallbackQueue)
668 m_connectivityTelemetry.Reset();
669
670 // Next stop the timer which could reset the hcnCallback
671 m_retryHcnServiceConnectionTimer.reset();
672
673 // Next stop the Hcn callback, which could add/remove networks
674 m_hcnCallback.reset();
675
676 m_debounceUpdateAllEndpointsDefaultTimer.reset();
677
678 m_debounceCreateEndpointFailureTimer.reset();
679
680 // Stop the linux ip state sync timer
681 m_retryLinuxIpStateSyncTimer.reset();
682
683 // canceling the callback queue only after stopping all sources that could queue a callback
684 m_gnsCallbackQueue.cancel();
685 m_hnsQueue.cancel();
686
687 // all of the above must be done outside holding a lock to avoid deadlocks
688 const auto lock = m_networkLock.lock_exclusive();
689 m_networkEndpoints.clear();
690 }
691 CATCH_LOG()
692
693 return returnStatus;
694 }
695
696 void wsl::core::networking::WslMirroredNetworkManager::DebounceUpdateAllEndpointsDefaultTimerFired(
697 _Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_TIMER)
698 try
699 {
700 auto* const instance = static_cast<WslMirroredNetworkManager*>(Context);
701
702 const auto lock = instance->m_networkLock.lock_exclusive();
703 instance->m_IsDebounceUpdateAllEndpointsDefaultTimerSet = false;
704 if (instance->m_state == State::Stopped)
705 {
706 return;
707 }
708
709 instance->UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, "DebounceUpdateAllEndpointsDefaultTimerFired");
710 }
711 CATCH_LOG()
712
713 void wsl::core::networking::WslMirroredNetworkManager::DebounceCreateEndpointFailureTimerFired(
714 _Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_TIMER)
715 try
716 {
717 auto* const instance = static_cast<WslMirroredNetworkManager*>(Context);
718
719 const auto lock = instance->m_networkLock.lock_exclusive();
720 if (instance->m_state == State::Stopped)
721 {
722 return;
723 }
724
725 if (!instance->m_failedEndpointProperties.empty())
726 {
727 // AddEndpointImpl will update m_failedEndpointProperties if any re-attempts to add the endpoint fail
728 // thus we must first move everything out
729 auto failedEndpointProperties = std::move(instance->m_failedEndpointProperties);
730 instance->m_failedEndpointProperties.clear();
731 for (auto& endpointProperties : failedEndpointProperties)
732 {
733 instance->AddEndpointImpl(std::move(endpointProperties));
734 }
735 }
736 }
737 CATCH_LOG()
738
739 _Requires_lock_held_(m_networkLock)
740 std::vector<GUID> wsl::core::networking::WslMirroredNetworkManager::EnumerateMirroredNetworks() const noexcept
741 try
742 {
743 WI_ASSERT(m_state == State::Started || m_state == State::Starting);
744
745 return EnumerateMirroredNetworksAndHyperVFirewall(m_vmConfig.FirewallConfig.Enabled());
746 }
747 catch (...)
748 {
749 LOG_CAUGHT_EXCEPTION();
750 return {};
751 }
752
753 _Requires_lock_held_(m_networkLock)
754 _Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::AddNetwork(const GUID& networkId) noexcept
755 try
756 {
757 WSL_LOG("WslMirroredNetworkManager::AddNetwork", TraceLoggingValue(networkId, "networkId"));
758
759 // Inform the parent class to create a new endpoint object which we can then connect into the container
760 m_hnsQueue.submit([this, networkId] { m_addNetworkEndpointCallback(networkId); });
761
762 return S_OK;
763 }
764 CATCH_RETURN()
765
766 _Requires_lock_held_(m_networkLock)
767 _Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::RemoveNetwork(const GUID& networkId) noexcept
768 try
769 {
770 WSL_LOG("WslMirroredNetworkManager::RemoveNetwork", TraceLoggingValue(networkId, "networkId"));
771
772 const auto foundEndpoint =
773 std::ranges::find_if(m_networkEndpoints, [&](const auto& endpoint) { return endpoint.NetworkId == networkId; });
774 if (foundEndpoint == std::end(m_networkEndpoints))
775 {
776 WSL_LOG("WslMirroredNetworkManager::RemoveNetwork - Network not found", TraceLoggingValue(networkId, "networkId"));
777 return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
778 }
779
780 // RemoveEndpoint will remove this endpoint from m_networkEndpoints
781 return RemoveEndpoint(foundEndpoint->EndpointId);
782 }
783 CATCH_RETURN()
784
785 void __stdcall wsl::core::networking::WslMirroredNetworkManager::RetryLinuxIpStateSyncTimerCallback(
786 _Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_TIMER) noexcept
787 {
788 auto* const manager = static_cast<WslMirroredNetworkManager*>(Context);
789 const auto lock = manager->m_networkLock.lock_exclusive();
790 if (manager->m_state == State::Stopped)
791 {
792 return;
793 }
794
795 manager->UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, "RetryLinuxIpStateSyncTimerCallback");
796 }
797
798 _Requires_lock_held_(m_networkLock)
799 _Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::SendAddressRequestToGns(
800 const NetworkEndpoint& endpoint, const TrackedIpAddress& address, hns::ModifyRequestType requestType) noexcept
801 try
802 {
803 hns::ModifyGuestEndpointSettingRequest<hns::IPAddress> modifyRequest;
804 modifyRequest.ResourceType = hns::GuestEndpointResourceType::IPAddress;
805 modifyRequest.RequestType = requestType;
806 modifyRequest.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(endpoint.InterfaceGuid);
807 modifyRequest.Settings = address.ConvertToHnsSettingsMsg();
808
809 WSL_LOG(
810 "WslMirroredNetworkManager::SendAddressRequestToGns",
811 TraceLoggingValue("ModifyGuestDeviceSettingRequest - set address [queued]", "GnsMessage"),
812 TraceLoggingValue(HnsModifyRequestTypeToString(requestType).c_str(), "requestType"),
813 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
814 TraceLoggingValue(address.Address.AddressString.c_str(), "ipAddress"),
815 TraceLoggingValue(address.Address.PrefixLength, "prefixLength"),
816 TraceLoggingValue(address.Address.IsPreferred(), "isPreferred"));
817
818 int linuxResultCode{};
819 // can safely capture by ref since we are waiting
820 const auto hr = m_gnsCallbackQueue.submit_and_wait([&] {
821 return m_callbackForGnsMessage(LxGnsMessageDeviceSettingRequest, ToJsonW(modifyRequest), GnsCallbackFlags::Wait, &linuxResultCode);
822 });
823
824 WSL_LOG(
825 "WslMirroredNetworkManager::SendAddressRequestToGns",
826 TraceLoggingValue("ModifyGuestDeviceSettingRequest - set address [completed]", "GnsMessage"),
827 TraceLoggingHResult(hr, "hr"),
828 TraceLoggingValue(linuxResultCode, "linuxResultCode"));
829
830 address.SyncRetryCount = (address.SyncRetryCount > 0) ? address.SyncRetryCount - 1 : 0;
831 return hr;
832 }
833 CATCH_RETURN()
834
835 _Requires_lock_held_(m_networkLock)
836 _Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::SendRouteRequestToGns(
837 const NetworkEndpoint& endpoint, const TrackedRoute& route, hns::ModifyRequestType requestType) noexcept
838 try
839 {
840 hns::ModifyGuestEndpointSettingRequest<hns::Route> modifyRequest;
841 modifyRequest.ResourceType = hns::GuestEndpointResourceType::Route;
842 modifyRequest.RequestType = requestType;
843 modifyRequest.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(endpoint.InterfaceGuid);
844 modifyRequest.Settings = route.ConvertToHnsSettingsMsg();
845
846 WSL_LOG(
847 "WslMirroredNetworkManager::SendRouteRequestToGns",
848 TraceLoggingValue("ModifyGuestDeviceSettingRequest : set route [queued]", "GnsMessage"),
849 TraceLoggingValue(HnsModifyRequestTypeToString(requestType).c_str(), "requestType"),
850 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
851 TraceLoggingValue(route.Route.DestinationPrefixString.c_str(), "destinationPrefix"),
852 TraceLoggingValue(route.Route.DestinationPrefix.PrefixLength, "prefixLength"),
853 TraceLoggingValue(route.Route.NextHopString.c_str(), "nextHop"),
854 TraceLoggingValue(route.Route.Metric, "metric"));
855
856 int linuxResultCode{};
857 // can safely capture by ref since we are waiting
858 const auto hr = m_gnsCallbackQueue.submit_and_wait([&] {
859 return m_callbackForGnsMessage(LxGnsMessageDeviceSettingRequest, ToJsonW(modifyRequest), GnsCallbackFlags::Wait, &linuxResultCode);
860 });
861
862 WSL_LOG(
863 "WslMirroredNetworkManager::SendRouteRequestToGns",
864 TraceLoggingValue("ModifyGuestDeviceSettingRequest : set route [completed]", "GnsMessage"),
865 TraceLoggingHResult(hr, "hr"),
866 TraceLoggingValue(linuxResultCode, "linuxResultCode"));
867
868 route.SyncRetryCount = (route.SyncRetryCount > 0) ? route.SyncRetryCount - 1 : 0;
869 return hr;
870 }
871 CATCH_RETURN()
872
873 _Requires_lock_held_(m_networkLock)
874 _Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::SendLoopbackRequestToGns(
875 const NetworkEndpoint& endpoint, const TrackedIpAddress& address, hns::OperationType operation) noexcept
876 try
877 {
878 hns::LoopbackRoutesRequest loopbackRequest;
879 loopbackRequest.operation = operation;
880 loopbackRequest.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(endpoint.InterfaceGuid);
881 loopbackRequest.family = address.Address.Address.si_family;
882 loopbackRequest.ipAddress = address.Address.AddressString;
883
884 WSL_LOG(
885 "WslMirroredNetworkManager::SendLoopbackRequestToGns",
886 TraceLoggingValue("LoopbackRoutesRequest [queued]", "GnsMessage"),
887 TraceLoggingValue(JsonEnumToString(operation).c_str(), "requestType"),
888 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
889 TraceLoggingValue(address.Address.AddressString.c_str(), "ipAddress"));
890
891 int linuxResultCode{};
892 // can safely capture by ref since we are waiting
893 const auto hr = m_gnsCallbackQueue.submit_and_wait([&]() {
894 return m_callbackForGnsMessage(LxGnsMessageLoopbackRoutesRequest, ToJsonW(loopbackRequest), GnsCallbackFlags::Wait, &linuxResultCode);
895 });
896
897 WSL_LOG(
898 "WslMirroredNetworkManager::SendLoopbackRequestToGns",
899 TraceLoggingValue("LoopbackRoutesRequest [completed]", "GnsMessage"),
900 TraceLoggingHResult(hr, "hr"),
901 TraceLoggingValue(linuxResultCode, "linuxResultCode"));
902
903 return hr;
904 }
905 CATCH_RETURN()
906
907 _Requires_lock_held_(m_networkLock)
908 _Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::SendDnsRequestToGns(
909 const NetworkEndpoint& endpoint, const DnsInfo& dnsInfo, hns::ModifyRequestType requestType) noexcept
910 try
911 {
912 hns::ModifyGuestEndpointSettingRequest<hns::DNS> modifyRequest;
913 modifyRequest.ResourceType = hns::GuestEndpointResourceType::DNS;
914 modifyRequest.RequestType = requestType;
915 modifyRequest.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(endpoint.InterfaceGuid);
916 modifyRequest.Settings = BuildDnsNotification(dnsInfo);
917
918 WSL_LOG(
919 "WslMirroredNetworkManager::SendDnsRequestToGns",
920 TraceLoggingValue("ModifyGuestDeviceSettingRequest : set DNS [queued]", "GnsMessage"),
921 TraceLoggingValue(HnsModifyRequestTypeToString(requestType).c_str(), "requestType"),
922 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
923 TraceLoggingValue(m_vmConfig.EnableDnsTunneling, "m_vmConfig.EnableDnsTunneling"),
924 TraceLoggingValue(wsl::shared::string::Join(dnsInfo.Servers, ',').c_str(), "server list"),
925 TraceLoggingValue(wsl::shared::string::Join(dnsInfo.Domains, ',').c_str(), "suffix list"));
926
927 int linuxResultCode{};
928 // can safely capture by ref since we are waiting
929 const auto hr = m_gnsCallbackQueue.submit_and_wait([&] {
930 return m_callbackForGnsMessage(LxGnsMessageDeviceSettingRequest, ToJsonW(modifyRequest), GnsCallbackFlags::Wait, &linuxResultCode);
931 });
932
933 WSL_LOG(
934 "WslMirroredNetworkManager::SendDnsRequestToGns",
935 TraceLoggingValue("ModifyGuestDeviceSettingRequest : set DNS [completed]", "GnsMessage"),
936 TraceLoggingHResult(hr, "hr"),
937 TraceLoggingValue(linuxResultCode, "linuxResultCode"));
938
939 return hr;
940 }
941 CATCH_RETURN()
942
943 _Requires_lock_held_(m_networkLock)
944 _Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::SendInterfaceRequestToGns(const NetworkEndpoint& endpoint) noexcept
945 try
946 {
947 const auto interfaceConnected = endpoint.Network->IsConnected;
948 const auto interfaceMtu = endpoint.Network->GetEffectiveMtu();
949 const auto interfaceMetric = endpoint.Network->GetMinimumMetric();
950
951 hns::ModifyGuestEndpointSettingRequest<hns::NetworkInterface> modifyRequest;
952 modifyRequest.Settings.Connected = interfaceConnected;
953 modifyRequest.Settings.NlMtu = interfaceMtu;
954 modifyRequest.Settings.Metric = interfaceMetric;
955 modifyRequest.ResourceType = hns::GuestEndpointResourceType::Interface;
956 modifyRequest.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(endpoint.InterfaceGuid);
957
958 WSL_LOG(
959 "WslMirroredNetworkManager::SendInterfaceRequestToGns",
960 TraceLoggingValue("ModifyGuestDeviceSettingRequest : update interface properties [queued]", "GnsMessage"),
961 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
962 TraceLoggingValue(interfaceConnected, "connected"),
963 TraceLoggingValue(interfaceMtu, "mtu"),
964 TraceLoggingValue(interfaceMetric, "metric"));
965
966 int linuxResultCode{};
967 // can safely capture by ref since we are waiting
968 const auto hr = m_gnsCallbackQueue.submit_and_wait([&] {
969 return m_callbackForGnsMessage(LxGnsMessageModifyGuestDeviceSettingRequest, ToJsonW(modifyRequest), GnsCallbackFlags::Wait, &linuxResultCode);
970 });
971
972 WSL_LOG(
973 "WslMirroredNetworkManager::SendInterfaceRequestToGns",
974 TraceLoggingValue("ModifyGuestDeviceSettingRequest : update interface properties [completed]", "GnsMessage"),
975 TraceLoggingHResult(hr, "hr"),
976 TraceLoggingValue(linuxResultCode, "linuxResultCode"));
977
978 return hr;
979 }
980 CATCH_RETURN()
981
982 _Requires_lock_held_(m_networkLock)
983 _Check_return_ bool wsl::core::networking::WslMirroredNetworkManager::SyncIpStateWithLinux(NetworkEndpoint& endpoint)
984 {
985 using hns::GuestEndpointResourceType;
986 using hns::IPAddress;
987 using hns::Route;
988 using TrackedIpStateSyncStatus::PendingAdd;
989 using TrackedIpStateSyncStatus::PendingRemoval;
990 using TrackedIpStateSyncStatus::PendingUpdate;
991 using TrackedIpStateSyncStatus::Synced;
992
993 bool syncSuccessful = true;
994
995 if (!endpoint.StateTracking->InitialSyncComplete)
996 {
997 // Tell GNS that we're ready to start pushing addresses and routes to Linux on this interface.
998 hns::InitialIpConfigurationNotification notification{};
999 notification.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(endpoint.InterfaceGuid);
1000 WI_SetAllFlags(
1001 notification.flags,
1002 (hns::InitialIpConfigurationNotificationFlags::SkipPrimaryRoutingTableUpdate |
1003 hns::InitialIpConfigurationNotificationFlags::SkipLoopbackRouteReset));
1004
1005 WSL_LOG(
1006 "WslMirroredNetworkManager::SyncIpStateWithLinux",
1007 TraceLoggingValue("InitialIpConfigurationNotification [queued]", "GnsMessage"),
1008 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"));
1009
1010 int linuxResultCode{};
1011 // can safely capture by ref since we are waiting
1012 const auto hr = m_gnsCallbackQueue.submit_and_wait([&] {
1013 return m_callbackForGnsMessage(
1014 LxGnsMessageInitialIpConfigurationNotification, ToJsonW(notification), GnsCallbackFlags::Wait, &linuxResultCode);
1015 });
1016
1017 WSL_LOG(
1018 "WslMirroredNetworkManager::SyncIpStateWithLinux",
1019 TraceLoggingValue("InitialIpConfigurationNotification [completed]", "GnsMessage"),
1020 TraceLoggingHResult(hr, "hr"),
1021 TraceLoggingValue(linuxResultCode, "linuxResultCode"));
1022 }
1023
1024 const auto makingIpInterfaceUpdate = endpoint.Network->PendingIPInterfaceUpdate;
1025 // Linux may delete routes behind us when making interface, address, and route changes
1026 // will track when to refresh v4 and v6 routes to ensure routes are still present after changes
1027 // a few customers have seen this when we update temporary v6 addresses, for example
1028 auto refreshAllRoutes = false;
1029
1030 // First: update Linux with any interface updates
1031 // If IsHidden is set, then also indicate to Linux that the interface should be disconnected
1032 if (endpoint.Network->PendingIPInterfaceUpdate || endpoint.Network->IsHidden)
1033 {
1034 const auto originalConnectValue = endpoint.Network->IsConnected;
1035 if (endpoint.Network->IsHidden)
1036 {
1037 endpoint.Network->IsConnected = false;
1038 }
1039
1040 if (FAILED(SendInterfaceRequestToGns(endpoint)))
1041 {
1042 WSL_LOG(
1043 "WslMirroredNetworkManager::SyncIpStateWithLinux",
1044 TraceLoggingValue("Failed to update Interface properties", "message"),
1045 TraceLoggingValue(endpoint.Network->IsConnected, "connected"),
1046 TraceLoggingValue(endpoint.Network->GetEffectiveMtu(), "mtu"),
1047 TraceLoggingValue(endpoint.Network->GetMinimumMetric(), "metric"),
1048 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"));
1049
1050 syncSuccessful = false;
1051 // interfaces are in an unknown state - push route updates in case Linux deleted routes behind us
1052 refreshAllRoutes = true;
1053 }
1054 else
1055 {
1056 endpoint.Network->PendingIPInterfaceUpdate = false;
1057 }
1058
1059 endpoint.Network->IsConnected = originalConnectValue;
1060 if (originalConnectValue)
1061 {
1062 // interface potentially just moved from disconnected -> connected
1063 // push route updates in case Linux deleted routes behind us
1064 refreshAllRoutes = true;
1065 }
1066 }
1067
1068 // Second: update Linux with any addresses to remove
1069 auto addressIt = endpoint.StateTracking->IpAddresses.begin();
1070 while (addressIt != endpoint.StateTracking->IpAddresses.end())
1071 {
1072 auto address = addressIt++;
1073
1074 // if the interface is hidden, we need to remove addresses
1075 // 'continue' to keep the address
1076 if (!endpoint.Network->IsHidden)
1077 {
1078 if (endpoint.Network->IpAddresses.contains(address->Address))
1079 {
1080 if (address->SyncStatus == PendingRemoval)
1081 {
1082 // This address was slated for removal but still exists on the host. It should be kept around instead.
1083 // We'll send an update just in case.
1084 address->SyncStatus = PendingUpdate;
1085 address->SyncRetryCount = TrackedIpAddress::MaxSyncRetryCount;
1086 }
1087 else if (makingIpInterfaceUpdate)
1088 {
1089 // if we pushed an interface update, ensure our addresses are up to date
1090 address->SyncStatus = PendingUpdate;
1091 address->SyncRetryCount = TrackedIpAddress::MaxSyncRetryCount;
1092 }
1093
1094 continue;
1095 }
1096 }
1097
1098 // We found an address that should be removed from the guest.
1099
1100 if (address->SyncStatus != PendingRemoval)
1101 {
1102 // We've never attempted to remove this address before, so reset the sync retry count.
1103 address->SyncRetryCount = TrackedIpAddress::MaxSyncRetryCount;
1104 }
1105 address->SyncStatus = PendingRemoval;
1106
1107 if (m_vmConfig.EnableHostAddressLoopback)
1108 {
1109 if (FAILED(SendLoopbackRequestToGns(endpoint, *address, hns::OperationType::Remove)))
1110 {
1111 WSL_LOG(
1112 "WslMirroredNetworkManager::SyncIpStateWithLinux",
1113 TraceLoggingValue("Failed to remove loopback routes for local address", "message"),
1114 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
1115 TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
1116 TraceLoggingValue(address->Address.AddressString.c_str(), "address"),
1117 TraceLoggingValue(address->Address.PrefixLength, "prefixLength"));
1118 }
1119 }
1120
1121 if (FAILED(SendAddressRequestToGns(endpoint, *address, hns::ModifyRequestType::Remove)))
1122 {
1123 if (address->SyncRetryCount == 0)
1124 {
1125 WSL_LOG(
1126 "WslMirroredNetworkManager::SyncIpStateWithLinux",
1127 TraceLoggingValue(
1128 "Reached maximum retries to remove an address - we will no longer schedule the retry timer", "message"),
1129 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
1130 TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
1131 TraceLoggingValue(address->Address.AddressString.c_str(), "address"),
1132 TraceLoggingValue(address->Address.PrefixLength, "prefixLength"));
1133 }
1134 else
1135 {
1136 syncSuccessful = false;
1137 }
1138 }
1139 else
1140 {
1141 WSL_LOG(
1142 "WslMirroredNetworkManager::SyncIpStateWithLinux",
1143 TraceLoggingValue("Address synced (removed)", "message"),
1144 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
1145 TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
1146 TraceLoggingValue(address->Address.AddressString.c_str(), "address"),
1147 TraceLoggingValue(address->Address.PrefixLength, "prefixLength"));
1148 endpoint.StateTracking->IpAddresses.erase(address);
1149 }
1150 // push route updates in case Linux deleted routes behind us after removing addresses
1151 refreshAllRoutes = true;
1152 }
1153
1154 // Third: update Linux with any routes to remove
1155 auto routeIt = endpoint.StateTracking->Routes.begin();
1156 while (routeIt != endpoint.StateTracking->Routes.end())
1157 {
1158 auto route = routeIt++;
1159
1160 // if the interface is hidden, we need to remove routes
1161 // 'continue' to keep the route
1162 if (!endpoint.Network->IsHidden)
1163 {
1164 if (endpoint.Network->Routes.contains(route->Route))
1165 {
1166 if (route->SyncStatus == PendingRemoval)
1167 {
1168 // This route was slated for removal but still exists on the host. It should be kept around instead.
1169 // We'll send an update just in case.
1170 route->SyncStatus = PendingUpdate;
1171 route->SyncRetryCount = TrackedRoute::MaxSyncRetryCount;
1172 }
1173 else if (makingIpInterfaceUpdate)
1174 {
1175 // if we pushed an interface update, ensure our routes are up to date
1176 route->SyncStatus = PendingUpdate;
1177 route->SyncRetryCount = TrackedRoute::MaxSyncRetryCount;
1178 }
1179
1180 continue;
1181 }
1182 }
1183
1184 // We found a route that should be removed from the guest.
1185
1186 if (route->SyncStatus != PendingRemoval)
1187 {
1188 // We've never attempted to remove this route before, so reset the sync retry count.
1189 route->SyncRetryCount = TrackedRoute::MaxSyncRetryCount;
1190 }
1191 route->SyncStatus = PendingRemoval;
1192
1193 if (FAILED(SendRouteRequestToGns(endpoint, *route, hns::ModifyRequestType::Remove)))
1194 {
1195 if (route->SyncRetryCount == 0)
1196 {
1197 WSL_LOG(
1198 "WslMirroredNetworkManager::SyncIpStateWithLinux",
1199 TraceLoggingValue(
1200 "Reached maximum retries to remove a route - we will no longer schedule the retry timer", "message"),
1201 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
1202 TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
1203 TraceLoggingValue(route->Route.DestinationPrefixString.c_str(), "destinationPrefix"),
1204 TraceLoggingValue(route->Route.DestinationPrefix.PrefixLength, "prefixLength"),
1205 TraceLoggingValue(route->Route.NextHopString.c_str(), "nextHop"),
1206 TraceLoggingValue(route->Route.Metric, "metric"));
1207 }
1208 else
1209 {
1210 syncSuccessful = false;
1211 }
1212 }
1213 else
1214 {
1215 WSL_LOG(
1216 "WslMirroredNetworkManager::SyncIpStateWithLinux",
1217 TraceLoggingValue("Route synced (removed) succeeded", "message"),
1218 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
1219 TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
1220 TraceLoggingValue(route->Route.DestinationPrefixString.c_str(), "destinationPrefix"),
1221 TraceLoggingValue(route->Route.DestinationPrefix.PrefixLength, "prefixLength"),
1222 TraceLoggingValue(route->Route.NextHopString.c_str(), "nextHop"),
1223 TraceLoggingValue(route->Route.Metric, "metric"));
1224 endpoint.StateTracking->Routes.erase(route);
1225 }
1226 // push route updates in case Linux deleted routes behind us after removing other various routes
1227 refreshAllRoutes = true;
1228 }
1229
1230 // Fourth: update Linux with any addresses to add
1231 if (!endpoint.Network->IsHidden && endpoint.Network->IsConnected)
1232 {
1233 bool shouldRefreshAllAddresses = false;
1234 for (auto& hostAddress : endpoint.Network->IpAddresses)
1235 {
1236 auto trackedAddress = endpoint.StateTracking->IpAddresses.emplace(TrackedIpAddress(hostAddress)).first;
1237 // detect if previously sync'd addresses need to be updated
1238 // this addresses issues we've seen where addresses were removed from Linux without our knowledge
1239 shouldRefreshAllAddresses |= trackedAddress->SyncStatus == PendingAdd || trackedAddress->SyncStatus == PendingUpdate;
1240 }
1241
1242 for (auto& trackedAddress : endpoint.StateTracking->IpAddresses)
1243 {
1244 std::optional<HRESULT> hr{};
1245 switch (trackedAddress.SyncStatus)
1246 {
1247 case PendingAdd:
1248 {
1249 hr = SendAddressRequestToGns(endpoint, trackedAddress, hns::ModifyRequestType::Add);
1250 if (FAILED(hr.value()))
1251 {
1252 // try to update it instead if it already exists
1253 hr = SendAddressRequestToGns(endpoint, trackedAddress, hns::ModifyRequestType::Update);
1254 }
1255
1256 if (SUCCEEDED(hr.value()) && m_vmConfig.EnableHostAddressLoopback)
1257 {
1258 // Add a special loopback route so that loopback packets flow through the host and back.
1259 hr = SendLoopbackRequestToGns(endpoint, trackedAddress, hns::OperationType::Create);
1260 if (FAILED(hr.value()))
1261 {
1262 WSL_LOG(
1263 "WslMirroredNetworkManager::SyncIpStateWithLinux",
1264 TraceLoggingValue("Failed to create loopback routes for local address", "message"),
1265 TraceLoggingValue(wsl::core::networking::ToString(trackedAddress.SyncStatus), "AddressSyncStatus"),
1266 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
1267 TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
1268 TraceLoggingValue(trackedAddress.Address.AddressString.c_str(), "address"),
1269 TraceLoggingValue(trackedAddress.Address.PrefixLength, "prefixLength"));
1270 }
1271 }
1272 // push route updates in case Linux deleted routes behind us after refreshing addresses
1273 refreshAllRoutes = true;
1274 break;
1275 }
1276
1277 case Synced:
1278 {
1279 auto fallThroughToUpdateAddress = shouldRefreshAllAddresses;
1280
1281 // Check if this address needs to be updated (i.e., its PreferredLifetime / DAD state needs to be updated)
1282 auto hostAddress = endpoint.Network->IpAddresses.find(trackedAddress.Address);
1283 if (hostAddress != endpoint.Network->IpAddresses.end())
1284 {
1285 if (trackedAddress.Address.IsPreferred() != hostAddress->IsPreferred())
1286 {
1287 trackedAddress.Address.PreferredLifetime = hostAddress->PreferredLifetime;
1288 fallThroughToUpdateAddress = true;
1289 }
1290 }
1291
1292 if (!fallThroughToUpdateAddress)
1293 {
1294 break;
1295 }
1296
1297 trackedAddress.SyncStatus = PendingUpdate;
1298 trackedAddress.SyncRetryCount = TrackedIpAddress::MaxSyncRetryCount;
1299 __fallthrough;
1300 }
1301
1302 case PendingUpdate:
1303 hr = SendAddressRequestToGns(endpoint, trackedAddress, hns::ModifyRequestType::Update);
1304 if (FAILED(hr.value()))
1305 {
1306 // try to add it if it was removed in Linux
1307 hr = SendAddressRequestToGns(endpoint, trackedAddress, hns::ModifyRequestType::Add);
1308 }
1309
1310 if (SUCCEEDED(hr.value()) && m_vmConfig.EnableHostAddressLoopback)
1311 {
1312 // Add a special loopback route so that loopback packets flow through the host and back.
1313 hr = SendLoopbackRequestToGns(endpoint, trackedAddress, hns::OperationType::Create);
1314 if (FAILED(hr.value()))
1315 {
1316 WSL_LOG(
1317 "WslMirroredNetworkManager::SyncIpStateWithLinux",
1318 TraceLoggingValue("Failed to create loopback routes for local address", "message"),
1319 TraceLoggingValue(wsl::core::networking::ToString(trackedAddress.SyncStatus), "AddressSyncStatus"),
1320 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
1321 TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
1322 TraceLoggingValue(trackedAddress.Address.AddressString.c_str(), "address"),
1323 TraceLoggingValue(trackedAddress.Address.PrefixLength, "prefixLength"));
1324 }
1325 }
1326
1327 // push route updates in case Linux deleted routes behind us after refreshing addresses
1328 refreshAllRoutes = true;
1329 break;
1330
1331 case PendingRemoval:
1332 // This address is still slated for removal, which we'll try again later.
1333 continue;
1334
1335 default:
1336 WI_ASSERT(false);
1337 continue;
1338 }
1339
1340 if (SUCCEEDED(hr.value_or(E_FAIL)))
1341 {
1342 trackedAddress.SyncStatus = Synced;
1343 WSL_LOG(
1344 "WslMirroredNetworkManager::SyncIpStateWithLinux",
1345 TraceLoggingValue("Address synced", "message"),
1346 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
1347 TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
1348 TraceLoggingValue(trackedAddress.Address.AddressString.c_str(), "address"),
1349 TraceLoggingValue(trackedAddress.Address.PrefixLength, "prefixLength"));
1350 }
1351
1352 if (trackedAddress.SyncRetryCount == 0)
1353 {
1354 WSL_LOG(
1355 "WslMirroredNetworkManager::SyncIpStateWithLinux",
1356 TraceLoggingValue(
1357 "Reached maximum retries to sync an address - we will no longer schedule the retry timer", "message"),
1358 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
1359 TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
1360 TraceLoggingValue(trackedAddress.Address.AddressString.c_str(), "address"),
1361 TraceLoggingValue(trackedAddress.Address.PrefixLength, "prefixLength"));
1362 }
1363
1364 syncSuccessful &= (trackedAddress.SyncStatus == Synced || trackedAddress.SyncRetryCount == 0);
1365 }
1366 }
1367 else
1368 {
1369 WSL_LOG(
1370 "WslMirroredNetworkManager::SyncIpStateWithLinux",
1371 TraceLoggingValue("Not adding addresses for hidden or disconnected interface", "message"),
1372 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
1373 TraceLoggingValue(endpoint.Network->IsHidden, "isHidden"),
1374 TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"));
1375 }
1376
1377 // Fourth: update Linux with any routes to add
1378 if (!endpoint.Network->IsHidden && endpoint.Network->IsConnected)
1379 {
1380 for (auto& hostRoute : endpoint.Network->Routes)
1381 {
1382 const auto trackedRoute = endpoint.StateTracking->Routes.emplace(TrackedRoute(hostRoute)).first;
1383 // detect if previously sync'd routes need to be updated
1384 // this addresses issues we've seen where routes were removed from Linux without our knowledge
1385 // and routes couldn't be updated later because required routes, like the prefix route, wasn't there
1386 refreshAllRoutes |= trackedRoute->SyncStatus == PendingAdd || trackedRoute->SyncStatus == PendingUpdate;
1387 }
1388
1389 if (refreshAllRoutes)
1390 {
1391 WSL_LOG("WslMirroredNetworkManager::SyncIpStateWithLinux", TraceLoggingValue("Refreshing all routes", "message"));
1392 }
1393
1394 for (auto& trackedRoute : endpoint.StateTracking->Routes)
1395 {
1396 std::optional<HRESULT> hr{};
1397 switch (trackedRoute.SyncStatus)
1398 {
1399 case PendingAdd:
1400 hr = SendRouteRequestToGns(endpoint, trackedRoute, hns::ModifyRequestType::Add);
1401 if (FAILED(hr.value()))
1402 {
1403 // try to update it instead if it already exists
1404 hr = SendRouteRequestToGns(endpoint, trackedRoute, hns::ModifyRequestType::Update);
1405 }
1406 break;
1407
1408 case Synced:
1409 if (refreshAllRoutes)
1410 {
1411 hr = SendRouteRequestToGns(endpoint, trackedRoute, hns::ModifyRequestType::Update);
1412 if (FAILED(hr.value()))
1413 {
1414 // try to add it
1415 hr = SendRouteRequestToGns(endpoint, trackedRoute, hns::ModifyRequestType::Add);
1416 }
1417 if (FAILED(hr.value()))
1418 {
1419 trackedRoute.SyncStatus = PendingUpdate;
1420 trackedRoute.SyncRetryCount = TrackedRoute::MaxSyncRetryCount;
1421 }
1422 }
1423 break;
1424
1425 case PendingUpdate:
1426 hr = SendRouteRequestToGns(endpoint, trackedRoute, hns::ModifyRequestType::Update);
1427 if (FAILED(hr.value()))
1428 {
1429 // try to add it
1430 hr = SendRouteRequestToGns(endpoint, trackedRoute, hns::ModifyRequestType::Add);
1431 }
1432 break;
1433
1434 case PendingRemoval:
1435 // This route is still slated for removal, which we'll try again later.
1436 continue;
1437
1438 default:
1439 WI_ASSERT(false);
1440 continue;
1441 }
1442
1443 if (SUCCEEDED(hr.value_or(E_FAIL)))
1444 {
1445 trackedRoute.SyncStatus = Synced;
1446 WSL_LOG(
1447 "WslMirroredNetworkManager::SyncIpStateWithLinux",
1448 TraceLoggingValue("Route synced", "message"),
1449 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
1450 TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
1451 TraceLoggingValue(trackedRoute.Route.DestinationPrefixString.c_str(), "destinationPrefix"),
1452 TraceLoggingValue(trackedRoute.Route.DestinationPrefix.PrefixLength, "prefixLength"),
1453 TraceLoggingValue(trackedRoute.Route.NextHopString.c_str(), "nextHop"),
1454 TraceLoggingValue(trackedRoute.Route.Metric, "metric"));
1455 }
1456
1457 if (trackedRoute.SyncRetryCount == 0)
1458 {
1459 WSL_LOG(
1460 "WslMirroredNetworkManager::SyncIpStateWithLinux",
1461 TraceLoggingValue(
1462 "Reached maximum amount of retries to sync a route. This can happen if the route's next hop is not "
1463 "reachable, as Linux does not allow such routes to be plumbed. Failure to sync the route will no longer "
1464 "schedule the retry timer.",
1465 "message"),
1466 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
1467 TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"),
1468 TraceLoggingValue(trackedRoute.Route.DestinationPrefixString.c_str(), "destinationPrefix"),
1469 TraceLoggingValue(trackedRoute.Route.DestinationPrefix.PrefixLength, "prefixLength"),
1470 TraceLoggingValue(trackedRoute.Route.NextHopString.c_str(), "nextHop"),
1471 TraceLoggingValue(trackedRoute.Route.Metric, "metric"));
1472 }
1473
1474 syncSuccessful &= (trackedRoute.SyncStatus == Synced || trackedRoute.SyncRetryCount == 0);
1475 }
1476 }
1477 else
1478 {
1479 WSL_LOG(
1480 "WslMirroredNetworkManager::SyncIpStateWithLinux",
1481 TraceLoggingValue("Not adding routes for hidden or disconnected interface", "message"),
1482 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
1483 TraceLoggingValue(endpoint.Network->IsHidden, "isHidden"),
1484 TraceLoggingValue(endpoint.Network->IsConnected, "isConnected"));
1485 }
1486
1487 // Fifth: update Linux with updated DNS information
1488 if (m_dnsInfo != m_trackedDnsInfo)
1489 {
1490 if (FAILED(SendDnsRequestToGns(endpoint, m_dnsInfo, hns::ModifyRequestType::Update)))
1491 {
1492 syncSuccessful = false;
1493 }
1494 else
1495 {
1496 m_trackedDnsInfo = m_dnsInfo;
1497 }
1498 }
1499
1500 endpoint.StateTracking->InitialSyncComplete = true;
1501
1502 WSL_LOG(
1503 "WslMirroredNetworkManager::SyncIpStateWithLinux",
1504 TraceLoggingValue(endpoint.InterfaceGuid, "InterfaceGuid"),
1505 TraceLoggingValue(syncSuccessful, "syncSuccessful"));
1506 return syncSuccessful;
1507 }
1508
1509 // We must determine what IP changes to push to Linux
1510 _Requires_lock_held_(m_networkLock)
1511 void wsl::core::networking::WslMirroredNetworkManager::UpdateAllEndpointsImpl(UpdateEndpointFlag updateFlag, _In_ PCSTR callingSource) noexcept
1512 try
1513 {
1514 static long s_updateAllEndpointsCounter = 0;
1515 const auto instanceCounter = InterlockedIncrement(&s_updateAllEndpointsCounter);
1516
1517 if (updateFlag == UpdateEndpointFlag::None)
1518 {
1519 WSL_LOG(
1520 "WslMirroredNetworkManager::UpdateAllEndpointsImpl",
1521 TraceLoggingValue(instanceCounter, "instanceCounter"),
1522 TraceLoggingValue(callingSource, "callingSource"),
1523 TraceLoggingValue("None [exiting early]", "updateFlag"));
1524 return;
1525 }
1526
1527 if (updateFlag == UpdateEndpointFlag::Default)
1528 {
1529 const auto currentTickCount = GetTickCount64();
1530 const auto timeFromLastUpdate = currentTickCount - m_lastUpdateAllEndpointsDefaultTime;
1531
1532 if (timeFromLastUpdate >= m_debounceUpdateAllEndpointsTimerMs)
1533 {
1534 // It's been >= m_debounceUpdateAllEndpointsTimerMs since we last attempted an update, so go ahead and process it.
1535 WSL_LOG(
1536 "WslMirroredNetworkManager::UpdateAllEndpointsImpl",
1537 TraceLoggingValue(instanceCounter, "instanceCounter"),
1538 TraceLoggingValue(callingSource, "callingSource"),
1539 TraceLoggingValue(wsl::core::networking::ToString(updateFlag), "updateFlag"),
1540 TraceLoggingValue("Debounce time reset - continuing update", "state"),
1541 TraceLoggingValue(timeFromLastUpdate, "timeFromLastUpdate"),
1542 TraceLoggingValue(m_debounceUpdateAllEndpointsTimerMs, "m_debounceUpdateAllEndpointsTimerMs"));
1543 m_lastUpdateAllEndpointsDefaultTime = currentTickCount;
1544 }
1545 else if (!m_IsDebounceUpdateAllEndpointsDefaultTimerSet)
1546 {
1547 // The debounce timer is not already scheduled, so schedule it.
1548 WSL_LOG(
1549 "WslMirroredNetworkManager::UpdateAllEndpointsImpl",
1550 TraceLoggingValue(instanceCounter, "instanceCounter"),
1551 TraceLoggingValue(callingSource, "callingSource"),
1552 TraceLoggingValue(wsl::core::networking::ToString(updateFlag), "updateFlag"),
1553 TraceLoggingValue("Debouncing Notification - setting timer", "state"),
1554 TraceLoggingValue(timeFromLastUpdate, "timeFromLastUpdate"),
1555 TraceLoggingValue(m_debounceUpdateAllEndpointsTimerMs, "m_debounceUpdateAllEndpointsTimerMs"));
1556
1557 // Set the due time just past the debounce timer duration, relative to the last update time.
1558 m_IsDebounceUpdateAllEndpointsDefaultTimerSet = true;
1559 FILETIME dueTime = wil::filetime::from_int64(static_cast<ULONGLONG>(
1560 -1 * (wil::filetime_duration::one_millisecond * (20 + m_debounceUpdateAllEndpointsTimerMs - timeFromLastUpdate))));
1561 SetThreadpoolTimer(m_debounceUpdateAllEndpointsDefaultTimer.get(), &dueTime, 0, 0);
1562 return;
1563 }
1564 else
1565 {
1566 // The debounce timer is already scheduled, so ignore this update.
1567 WSL_LOG(
1568 "WslMirroredNetworkManager::UpdateAllEndpointsImpl",
1569 TraceLoggingValue(instanceCounter, "instanceCounter"),
1570 TraceLoggingValue(callingSource, "callingSource"),
1571 TraceLoggingValue(wsl::core::networking::ToString(updateFlag), "updateFlag"),
1572 TraceLoggingValue("Debouncing Notification - timer already set", "state"),
1573 TraceLoggingValue(timeFromLastUpdate, "timeFromLastUpdate"),
1574 TraceLoggingValue(m_debounceUpdateAllEndpointsTimerMs, "m_debounceUpdateAllEndpointsTimerMs"));
1575 return;
1576 }
1577 }
1578 else
1579 {
1580 WSL_LOG(
1581 "WslMirroredNetworkManager::UpdateAllEndpointsImpl",
1582 TraceLoggingValue(instanceCounter, "instanceCounter"),
1583 TraceLoggingValue(callingSource, "callingSource"),
1584 TraceLoggingValue(wsl::core::networking::ToString(updateFlag), "updateFlag"));
1585 }
1586
1587 m_latestHnsStatus = HnsStatus::NetworkConnectedWithHnsNotification;
1588
1589 // Update IP properties on all interfaces on the host
1590 // N.B. We must process the DisableDefaultRoutes property of each host interface before we
1591 // process the host routes, as this property might impact the set of routes we choose to mirror.
1592 ProcessConnectivityChange();
1593 ProcessInterfaceChange();
1594 ProcessIpAddressChange();
1595 ProcessRouteChange();
1596 ProcessDNSChange();
1597
1598 // Push IP state to Linux
1599 bool syncSuccessful = true;
1600 std::set<GUID, wsl::windows::common::helpers::GuidLess> mirroredConnectedInterfaces;
1601 for (auto& endpoint : m_networkEndpoints)
1602 {
1603 if (IsInterfaceIndexOfGelnic(endpoint.Network->InterfaceIndex))
1604 {
1605 continue;
1606 }
1607
1608 // there may be more mirrored interfaces than 'host-connected' interfaces
1609 // e.g. network adapters which are disconnected or hidden
1610 // track all host-connected interfaces that have been successfully mirrored
1611 if (m_hostConnectedInterfaces.contains(endpoint.InterfaceGuid))
1612 {
1613 if (endpoint.Network->IsHidden)
1614 {
1615 WSL_LOG(
1616 "WslMirroredNetworkManager::UpdateAllEndpointsImpl",
1617 TraceLoggingValue(instanceCounter, "instanceCounter"),
1618 TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
1619 TraceLoggingValue(
1620 "Resetting IsHidden to false and PendingIPInterfaceUpdate to true to update the Interface", "message"));
1621 endpoint.Network->IsHidden = false;
1622 // setting PendingIPInterfaceUpdate to tell SyncIpStateWithLinux to update the Interface state
1623 endpoint.Network->PendingIPInterfaceUpdate = true;
1624 }
1625 mirroredConnectedInterfaces.insert(endpoint.InterfaceGuid);
1626 }
1627 else
1628 {
1629 // if the host has hidden the interface that was mirrored by HNS
1630 // ensure the interface is not connected in Linux
1631 // we are deliberately overriding the endpoint state in this case
1632 WSL_LOG(
1633 "WslMirroredNetworkManager::UpdateAllEndpointsImpl",
1634 TraceLoggingValue(instanceCounter, "instanceCounter"),
1635 TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
1636 TraceLoggingValue(
1637 "Setting IsHidden to true - this interface is hidden on the host and must not be connected in the container",
1638 "message"));
1639 endpoint.Network->IsHidden = true;
1640 // setting PendingIPInterfaceUpdate to tell SyncIpStateWithLinux to update the Interface state
1641 endpoint.Network->PendingIPInterfaceUpdate = true;
1642 }
1643
1644 if (!SyncIpStateWithLinux(endpoint))
1645 {
1646 // We failed to sync some bit of state. Let's schedule a timer to try again in a bit.
1647 WSL_LOG(
1648 "WslMirroredNetworkManager::UpdateAllEndpointsImpl",
1649 TraceLoggingValue(instanceCounter, "instanceCounter"),
1650 TraceLoggingValue(endpoint.InterfaceGuid, "interfaceGuid"),
1651 TraceLoggingValue("Some IP state did not sync with Linux - scheduling a retry attempt", "message"),
1652 TraceLoggingValue(m_linuxIpStateRetryDebounceTimerMilliseconds, "m_linuxIpStateRetryDebounceTimerMilliseconds"));
1653
1654 FILETIME dueTime = wil::filetime::from_int64(
1655 static_cast<ULONGLONG>(-1 * wil::filetime_duration::one_millisecond * m_linuxIpStateRetryDebounceTimerMilliseconds));
1656
1657 SetThreadpoolTimer(m_retryLinuxIpStateSyncTimer.get(), &dueTime, 0, 1000);
1658
1659 if (syncSuccessful)
1660 {
1661 // set to false the first pass through the for loop
1662 syncSuccessful = false;
1663
1664 // Increase the IP state retry timer according to exponential back-off, capping at a maximum value.
1665 m_linuxIpStateRetryDebounceTimerMilliseconds =
1666 std::min(m_linuxIpStateRetryDebounceTimerMilliseconds * 2, m_linuxIpStateRetryDebounceTimerMaxMilliseconds);
1667 }
1668 }
1669 }
1670
1671 // If all of the following occurs, then we have entered the goal state.
1672 // 1) Mirrored all usable host interfaces
1673 // 2) Successfully sync'd all settings on those interfaces
1674 // 3) Not currently in the goal state
1675 if (syncSuccessful)
1676 {
1677 // Reset the IP state retry timer back to the minimum value.
1678 m_linuxIpStateRetryDebounceTimerMilliseconds = m_linuxIpStateRetryDebounceTimerMinMilliseconds;
1679
1680 // if any host-connected interfaces are not yet mirrored, don't indicate we are in sync
1681 bool hnsMirroredInSyncWithHost = mirroredConnectedInterfaces == m_hostConnectedInterfaces;
1682 if (mirroredConnectedInterfaces != m_hostConnectedInterfaces)
1683 {
1684 // mirroredConnectedInterfaces won't equal m_hostConnectedInterfaces when:
1685 // - there are hidden host interfaces
1686 // i.e., interfaces are in m_networkEndpoints but not in m_hostConnectedInterfaces
1687 // - when HNS hasn't yet mirrored a connected host interface
1688 // i.e. interfaces are in m_hostConnectedInterfaces but not in m_networkEndpoints
1689 //
1690 // if HNS has not yet mirrored a host interface, we should not indicate we are in sync
1691 // but if hidden interfaces should not block being in sync
1692
1693 // reset to true until we see if HNS hasn't yet mirrored a connected host interface
1694 hnsMirroredInSyncWithHost = true;
1695 // verify that HNS has mirrored all host-connected interfaces
1696 for (const auto& connectedHostInterface : m_hostConnectedInterfaces)
1697 {
1698 bool interfaceMatched = false;
1699 for (const auto& hnsEndpoint : m_networkEndpoints)
1700 {
1701 if (connectedHostInterface == hnsEndpoint.InterfaceGuid)
1702 {
1703 interfaceMatched = true;
1704 break;
1705 }
1706 }
1707
1708 if (!interfaceMatched)
1709 {
1710 WSL_LOG(
1711 "WslMirroredNetworkManager::UpdateAllEndpointsImpl",
1712 TraceLoggingValue(instanceCounter, "instanceCounter"),
1713 TraceLoggingValue("HNS has not yet mirrored a host connected Interface", "message"),
1714 TraceLoggingValue(connectedHostInterface, "interfaceGuid"));
1715 hnsMirroredInSyncWithHost = false;
1716 }
1717 }
1718 }
1719
1720 if (hnsMirroredInSyncWithHost && !m_inMirroredGoalState.is_signaled())
1721 {
1722 WSL_LOG(
1723 "WslMirroredNetworkManager::UpdateAllEndpointsImpl",
1724 TraceLoggingValue(instanceCounter, "instanceCounter"),
1725 TraceLoggingValue("Reached goal state", "message"));
1726 m_inMirroredGoalState.SetEvent();
1727
1728 // Telemetry to see how long it takes to reach the mirrored goal state for the first time.
1729 if (std::chrono::duration_cast<std::chrono::milliseconds>(m_initialMirroredGoalStateEndTime.time_since_epoch()) ==
1730 std::chrono::milliseconds::zero())
1731 {
1732 m_initialMirroredGoalStateEndTime = std::chrono::steady_clock::now();
1733
1734 const auto waitTime = m_initialMirroredGoalStateEndTime - m_objectCreationTime;
1735 WSL_LOG(
1736 "WslMirroringInitialGoalStateWait",
1737 TraceLoggingValue((std::chrono::duration_cast<std::chrono::milliseconds>(waitTime)).count(), "waitTimeMs"),
1738 TraceLoggingValue(m_vmConfig.EnableDnsTunneling, "DnsTunnelingEnabled"),
1739 TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), "HyperVFirewallEnabled"),
1740 TraceLoggingValue(m_vmConfig.EnableAutoProxy, "AutoProxyFeatureEnabled")); // the feature is enabled, but we don't know if proxy settings are actually configured
1741 }
1742 }
1743 }
1744 }
1745 CATCH_LOG()
1746
1747 _Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::EnumerateNetworks(_Out_ std::vector<GUID>& NetworkIds) const noexcept
1748 try
1749 {
1750 auto lock = m_networkLock.lock_shared();
1751 WI_ASSERT(m_state == State::Started);
1752 if (m_state == State::Stopped)
1753 {
1754 return E_ABORT;
1755 }
1756
1757 NetworkIds = EnumerateMirroredNetworks();
1758 return S_OK;
1759 }
1760 CATCH_RETURN()
1761
1762 void wsl::core::networking::WslMirroredNetworkManager::AddEndpoint(NetworkEndpoint&& newEndpoint, hns::HNSEndpoint&& endpointProperties) noexcept
1763 {
1764 const auto lock = m_networkLock.lock_exclusive();
1765 if (m_state == State::Stopped)
1766 {
1767 return;
1768 }
1769
1770 constexpr uint32_t defaultRetryCount = 0ul;
1771 AddEndpointImpl({std::move(newEndpoint), std::move(endpointProperties), defaultRetryCount});
1772 }
1773
1774 _Requires_lock_held_(m_networkLock)
1775 void wsl::core::networking::WslMirroredNetworkManager::AddEndpointImpl(EndpointTracking&& endpointTrackingObject) noexcept
1776 {
1777 PCSTR executionStep = "";
1778 try
1779 {
1780 // Hot-add the network endpoint to the utility VM.
1781 hcs::NetworkAdapter networkEndpoint{};
1782
1783 networkEndpoint.MacAddress = wsl::shared::string::ParseMacAddress(endpointTrackingObject.m_hnsEndpoint.MacAddress);
1784
1785 // Set the instance id to the mirrored interfaceGuid so HNS -> netvsc can optimally use the same vmNIC constructs when the InterfaceGuid is the same
1786 hcs::ModifySettingRequest<hcs::NetworkAdapter> addEndpointRequest{};
1787 addEndpointRequest.ResourcePath =
1788 c_networkAdapterPrefix + wsl::shared::string::GuidToString<wchar_t>(endpointTrackingObject.m_networkEndpoint.InterfaceGuid);
1789 addEndpointRequest.RequestType = hcs::ModifyRequestType::Add;
1790 addEndpointRequest.Settings.EndpointId = endpointTrackingObject.m_hnsEndpoint.ID;
1791 addEndpointRequest.Settings.InstanceId = endpointTrackingObject.m_networkEndpoint.InterfaceGuid;
1792
1793 addEndpointRequest.Settings.MacAddress = wsl::shared::string::ParseMacAddress(endpointTrackingObject.m_hnsEndpoint.MacAddress);
1794 auto addEndpointRequestString = wsl::shared::ToJsonW(addEndpointRequest);
1795
1796 WSL_LOG(
1797 "WslMirroredNetworkManager::AddEndpoint [Creating HCS endpoint]",
1798 TraceLoggingValue(addEndpointRequestString.c_str(), "networkRequestString"));
1799
1800 executionStep = "AddHcsEndpoint";
1801 auto hr = m_hnsQueue.submit_and_wait([&] {
1802 // RetryWithTimeout throws if fails every attempt - which is caught and returned by m_gnsMessageQueue
1803 auto retryCount = 0ul;
1804 return wsl::shared::retry::RetryWithTimeout<HRESULT>(
1805 [&] {
1806 const auto retryHr = wil::ResultFromException(
1807 [&] { wsl::windows::common::hcs::ModifyComputeSystem(m_hcsSystem, addEndpointRequestString.c_str()); });
1808
1809 WSL_LOG(
1810 "WslMirroredNetworkManager::AddEndpoint [ModifyComputeSystem(ModifyRequestType::Add)]",
1811 TraceLoggingValue(endpointTrackingObject.m_hnsEndpoint.ID, "endpointId"),
1812 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "instanceId"),
1813 TraceLoggingValue(retryHr, "retryHr"),
1814 TraceLoggingValue(retryCount, "retryCount"));
1815
1816 ++retryCount;
1817 return THROW_IF_FAILED(retryHr);
1818 },
1819 wsl::core::networking::AddEndpointRetryPeriod,
1820 wsl::core::networking::AddEndpointRetryTimeout,
1821 wsl::core::networking::AddEndpointRetryPredicate);
1822 });
1823
1824 if (hr == HCN_E_ENDPOINT_ALREADY_ATTACHED)
1825 {
1826 WSL_LOG(
1827 "WslMirroredNetworkManager::AddEndpoint [Adding the endpoint returned HCN_E_ENDPOINT_ALREADY_ATTACHED - "
1828 "continuing]",
1829 TraceLoggingValue(endpointTrackingObject.m_hnsEndpoint.ID, "endpointId"));
1830
1831 hr = S_OK;
1832 }
1833 else if (FAILED(hr))
1834 {
1835 THROW_HR(hr);
1836 }
1837
1838 auto removeEndpointOnError = wil::scope_exit([&] {
1839 // try to delete the endpoint in HCS if anything failed
1840 // Set the instance id to the mirrored interfaceGuid so HNS -> netvsc can optimally use the same vmNIC constructs when the InterfaceGuid is the same
1841 hcs::ModifySettingRequest<hcs::NetworkAdapter> networkRequest{};
1842 networkRequest.ResourcePath =
1843 c_networkAdapterPrefix + wsl::shared::string::GuidToString<wchar_t>(endpointTrackingObject.m_networkEndpoint.InterfaceGuid);
1844 networkRequest.RequestType = hcs::ModifyRequestType::Remove;
1845 networkRequest.Settings.EndpointId = endpointTrackingObject.m_hnsEndpoint.ID;
1846 networkRequest.Settings.InstanceId = endpointTrackingObject.m_networkEndpoint.InterfaceGuid;
1847
1848 const auto networkRequestString = wsl::shared::ToJsonW(std::move(networkRequest));
1849
1850 // capturing by ref because we wait for the workitem to complete
1851 const auto modifyResult = m_hnsQueue.submit_and_wait([&] {
1852 windows::common::hcs::ModifyComputeSystem(m_hcsSystem, networkRequestString.c_str());
1853 return S_OK; // ModifyComputeSystem throws errors, caught by m_gnsMessageQueue
1854 });
1855 WSL_LOG(
1856 "WslMirroredNetworkManager::AddEndpoint [Removing the HCS mirrored endpoint after failure to Add]",
1857 TraceLoggingHResult(modifyResult, "hr"),
1858 TraceLoggingValue(endpointTrackingObject.m_hnsEndpoint.ID, "endpointId"),
1859 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "instanceId"));
1860
1861 if (FAILED(modifyResult))
1862 {
1863 WSL_LOG(
1864 "AddMirroredEndpointFailed",
1865 TraceLoggingHResult(modifyResult, "result"),
1866 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "InterfaceGuid"),
1867 TraceLoggingValue(
1868 endpointTrackingObject.m_networkEndpoint.Network ? endpointTrackingObject.m_networkEndpoint.Network->InterfaceType : 0,
1869 "InterfaceType"),
1870 TraceLoggingValue("RemoveHcsEndpointOnFailure", "executionStep"),
1871 TraceLoggingValue(m_vmConfig.EnableDnsTunneling, "DnsTunnelingEnabled"),
1872 TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), "HyperVFirewallEnabled"),
1873 TraceLoggingValue(m_vmConfig.EnableAutoProxy, "AutoProxyFeatureEnabled"), // the feature is enabled, but we don't know if proxy settings are actually configured
1874 TraceLoggingValue(endpointTrackingObject.m_retryCount, "retryCount"));
1875 }
1876
1877 // Inform the parent class to remove the endpoint object from GNS registration since we couldn't add the endpoint
1878 });
1879
1880 // Refreshing the endpoint causes it to reach the GNSInterfaceState::Synchronized state in HNS
1881 // which is required to receive notifications.
1882 // When HcnModifyEndpoint returns, all GNS notifications have been processed and the interface is fully configured.
1883 hns::ModifyGuestEndpointSettingRequest<void> refreshRequest{};
1884 refreshRequest.RequestType = hns::ModifyRequestType::Refresh;
1885 refreshRequest.ResourceType = hns::GuestEndpointResourceType::Port;
1886
1887 const auto refreshEndpointRequestString = ToJsonW(refreshRequest);
1888 WSL_LOG(
1889 "WslMirroredNetworkManager::AddEndpoint [Synchronizing HNS state]",
1890 TraceLoggingValue(endpointTrackingObject.m_hnsEndpoint.ID, "endpointId"),
1891 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "instanceId"));
1892
1893 executionStep = "RefreshHcsEndpoint";
1894 THROW_IF_FAILED(m_hnsQueue.submit_and_wait([&] {
1895 // Don't retry if HcnModifyEndpoint fails with HCN_E_ENDPOINT_NOT_FOUND which indicates that the underlying network object was deleted.
1896 constexpr auto retryPredicate = [] { return wil::ResultFromCaughtException() != HCN_E_ENDPOINT_NOT_FOUND; };
1897 auto retryCount = 0ul;
1898 // RetryWithTimeout throws if fails every attempt - which is caught and returned by m_hnsQueue
1899 return wsl::shared::retry::RetryWithTimeout<HRESULT>(
1900 [&] {
1901 wil::unique_cotaskmem_string error;
1902 const auto retryHr = HcnModifyEndpoint(
1903 endpointTrackingObject.m_networkEndpoint.Endpoint.get(), refreshEndpointRequestString.c_str(), &error);
1904
1905 WSL_LOG(
1906 "WslMirroredNetworkManager::AddEndpoint [HcnModifyEndpoint(ModifyRequestType::Refresh)]",
1907 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.EndpointId, "endpointId"),
1908 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "instanceId"),
1909 TraceLoggingValue(refreshEndpointRequestString.c_str(), "json"),
1910 TraceLoggingHResult(retryHr, "retryHr"),
1911 TraceLoggingValue(error.is_valid() ? error.get() : L"", "errorString"),
1912 TraceLoggingValue(retryCount, "retryCount"));
1913
1914 ++retryCount;
1915 return THROW_IF_FAILED(retryHr);
1916 },
1917 wsl::core::networking::AddEndpointRetryPeriod,
1918 wsl::core::networking::AddEndpointRetryTimeout,
1919 retryPredicate);
1920 }));
1921
1922 // Notify GNS of the new adapter
1923 hns::VmNicCreatedNotification newAdapterNotification;
1924 // Set the adapterId == instanceId of the created Endpoint == the mirrored interfaceGuid
1925 newAdapterNotification.adapterId = endpointTrackingObject.m_networkEndpoint.InterfaceGuid;
1926
1927 constexpr auto type = GnsMessageType(newAdapterNotification);
1928 const auto jsonString = ToJsonW(newAdapterNotification);
1929 WSL_LOG(
1930 "WslMirroredNetworkManager::AddEndpoint",
1931 TraceLoggingValue("VmNicCreatedNotification [queued]", "GnsMessage"),
1932 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "adapterId"),
1933 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.NetworkId, "networkId"),
1934 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.EndpointId, "endpointId"),
1935 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "interfaceGuid"),
1936 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.Network->InterfaceIndex, "interfaceIndex"),
1937 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.Network->InterfaceType, "interfaceType"),
1938 TraceLoggingValue(jsonString.c_str(), "jsonString"));
1939
1940 int linuxResultCode{};
1941 // can safely capture by ref since we are waiting
1942 hr = m_gnsCallbackQueue.submit_and_wait(
1943 [&] { return m_callbackForGnsMessage(type, jsonString, GnsCallbackFlags::Wait, &linuxResultCode); });
1944 WSL_LOG(
1945 "WslMirroredNetworkManager::AddEndpoint",
1946 TraceLoggingValue("VmNicCreatedNotification [completed]", "GnsMessage"),
1947 TraceLoggingHResult(hr, "hr"),
1948 TraceLoggingValue(linuxResultCode, "linuxResultCode"));
1949
1950 // Send the endpoint state (link status) to gns.
1951 // Also set the loopback device name to allow configuration by name.
1952 //
1953 // Temporarily set endpoint ID and PortFriendlyName to what LxGnsMessageInterfaceConfiguration expects.
1954 GUID originalEndpointId = endpointTrackingObject.m_hnsEndpoint.ID;
1955 std::wstring originalPortFriendlyName = endpointTrackingObject.m_hnsEndpoint.PortFriendlyName;
1956 endpointTrackingObject.m_hnsEndpoint.ID = endpointTrackingObject.m_networkEndpoint.InterfaceGuid;
1957 if (IsInterfaceIndexOfGelnic(endpointTrackingObject.m_networkEndpoint.Network->InterfaceIndex))
1958 {
1959 endpointTrackingObject.m_hnsEndpoint.PortFriendlyName = c_loopbackDeviceName;
1960 }
1961 WI_ASSERT(endpointTrackingObject.m_hnsEndpoint.IPAddress.empty());
1962
1963 executionStep = "SendEndpointStateToGns";
1964 linuxResultCode = {};
1965 // can safely capture by ref since we are waiting
1966 hr = m_gnsCallbackQueue.submit_and_wait([&] {
1967 return m_callbackForGnsMessage(
1968 LxGnsMessageInterfaceConfiguration, ToJsonW(endpointTrackingObject.m_hnsEndpoint), GnsCallbackFlags::Wait, &linuxResultCode);
1969 });
1970 // restore the Endpoint ID GUID and PortFriendlyName
1971 endpointTrackingObject.m_hnsEndpoint.ID = originalEndpointId;
1972 endpointTrackingObject.m_hnsEndpoint.PortFriendlyName = originalPortFriendlyName;
1973 WSL_LOG(
1974 "WslMirroredNetworkManager::AddEndpoint [Update link status]",
1975 TraceLoggingHResult(hr, "hr"),
1976 TraceLoggingValue(linuxResultCode, "linuxResultCode"),
1977 TraceLoggingValue(endpointTrackingObject.m_hnsEndpoint.ID, "endpointId"),
1978 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "instanceId"),
1979 TraceLoggingValue(endpointTrackingObject.m_hnsEndpoint.PortFriendlyName.c_str(), "PortFriendlyName"));
1980 THROW_IF_FAILED(hr);
1981
1982 endpointTrackingObject.m_networkEndpoint.Network->MacAddress = endpointTrackingObject.m_hnsEndpoint.MacAddress;
1983
1984 if (IsInterfaceIndexOfGelnic(endpointTrackingObject.m_networkEndpoint.Network->InterfaceIndex))
1985 {
1986 // Create loopback device in the container which will also set up loopback communication with the host.
1987 hns::CreateDeviceRequest createLoopbackDevice;
1988 createLoopbackDevice.deviceName = c_loopbackDeviceName;
1989 createLoopbackDevice.type = hns::DeviceType::Loopback;
1990 // Set the lowerEdgeAdapterId == the InstanceId of the Endpoint == the mirrored interfaceGuid
1991 createLoopbackDevice.lowerEdgeAdapterId = endpointTrackingObject.m_networkEndpoint.InterfaceGuid;
1992
1993 WSL_LOG(
1994 "WslMirroredNetworkManager::AddEndpoint",
1995 TraceLoggingValue("CreateDeviceRequest - loopback [queued]", "GnsMessage"),
1996 TraceLoggingValue(c_loopbackDeviceName, "deviceName"),
1997 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "lowerEdgeAdapterId"),
1998 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.EndpointId, "endpointId"),
1999 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "interfaceGuid"));
2000 constexpr auto gnsMessageType = GnsMessageType(createLoopbackDevice);
2001
2002 linuxResultCode = {};
2003 // can safely capture by ref since we are waiting
2004 hr = m_gnsCallbackQueue.submit_and_wait([&] {
2005 return m_callbackForGnsMessage(gnsMessageType, ToJsonW(createLoopbackDevice), GnsCallbackFlags::Wait, &linuxResultCode);
2006 });
2007 WSL_LOG(
2008 "WslMirroredNetworkManager::AddEndpoint",
2009 TraceLoggingValue("CreateDeviceRequest - loopback [completed]", "GnsMessage"),
2010 TraceLoggingHResult(hr, "hr"),
2011 TraceLoggingValue(linuxResultCode, "linuxResultCode"));
2012 }
2013 else
2014 {
2015 // Perform per-interface configuration of net filter rules.
2016 hns::InterfaceNetFilterRequest interfaceNetFilterRequest;
2017 interfaceNetFilterRequest.targetDeviceName =
2018 wsl::shared::string::GuidToString<wchar_t>(endpointTrackingObject.m_networkEndpoint.InterfaceGuid);
2019 interfaceNetFilterRequest.operation = hns::OperationType::Create;
2020 interfaceNetFilterRequest.ephemeralPortRangeStart = m_ephemeralPortRange.first;
2021 interfaceNetFilterRequest.ephemeralPortRangeEnd = m_ephemeralPortRange.second;
2022
2023 linuxResultCode = {};
2024 // can safely capture by ref since we are waiting
2025 hr = m_gnsCallbackQueue.submit_and_wait([&] {
2026 return m_callbackForGnsMessage(
2027 LxGnsMessageInterfaceNetFilter, ToJsonW(interfaceNetFilterRequest), GnsCallbackFlags::Wait, &linuxResultCode);
2028 });
2029 LOG_IF_FAILED(hr);
2030 WSL_LOG(
2031 "WslMirroredNetworkManager::AddEndpoint [InterfaceNetFilterRequest]",
2032 TraceLoggingHResult(hr, "hr"),
2033 TraceLoggingValue(linuxResultCode, "linuxResultCode"),
2034 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.EndpointId, "endpointId"),
2035 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "interfaceGuid"),
2036 TraceLoggingValue(m_ephemeralPortRange.first, "ephemeralPortRangeStart"),
2037 TraceLoggingValue(m_ephemeralPortRange.second, "ephemeralPortRangeEnd"));
2038 }
2039
2040 // WSL will track state for every endpoint (interface)
2041 endpointTrackingObject.m_networkEndpoint.StateTracking.emplace(m_vmConfig.FirewallConfig.VmCreatorId);
2042 endpointTrackingObject.m_networkEndpoint.StateTracking->SeedInitialState(*endpointTrackingObject.m_networkEndpoint.Network);
2043
2044 m_networkEndpoints.emplace_back(std::move(endpointTrackingObject.m_networkEndpoint));
2045
2046 // successfully tracked the added endpoint - release the scope guards
2047 removeEndpointOnError.release();
2048
2049 // after added, we must determine what is the preferred interface to indicate to bond to connect
2050 UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, "AddEndpoint");
2051
2052 WSL_LOG(
2053 "CreateMirroredEndpointEnd",
2054 TraceLoggingHResult(S_OK, "result"),
2055 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "InterfaceGuid"),
2056 TraceLoggingValue(
2057 endpointTrackingObject.m_networkEndpoint.Network ? endpointTrackingObject.m_networkEndpoint.Network->InterfaceType : 0,
2058 "InterfaceType"),
2059 TraceLoggingValue(m_vmConfig.EnableDnsTunneling, "DnsTunnelingEnabled"),
2060 TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), "HyperVFirewallEnabled"),
2061 TraceLoggingValue(m_vmConfig.EnableAutoProxy, "AutoProxyFeatureEnabled"), // the feature is enabled, but we don't know if proxy settings are actually configured
2062 TraceLoggingValue(endpointTrackingObject.m_retryCount, "retryCount"));
2063 }
2064 catch (...)
2065 {
2066 const auto hr = wil::ResultFromCaughtException();
2067
2068 WSL_LOG(
2069 "AddMirroredEndpointFailed",
2070 TraceLoggingHResult(hr, "result"),
2071 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "InterfaceGuid"),
2072 TraceLoggingValue(
2073 endpointTrackingObject.m_networkEndpoint.Network ? endpointTrackingObject.m_networkEndpoint.Network->InterfaceType : 0,
2074 "InterfaceType"),
2075 TraceLoggingValue(executionStep, "executionStep"),
2076 TraceLoggingValue(m_vmConfig.EnableDnsTunneling, "DnsTunnelingEnabled"),
2077 TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), "HyperVFirewallEnabled"),
2078 TraceLoggingValue(m_vmConfig.EnableAutoProxy, "AutoProxyFeatureEnabled"), // the feature is enabled, but we don't know if proxy settings are actually configured
2079 TraceLoggingValue(endpointTrackingObject.m_retryCount, "retryCount"));
2080
2081 if (hr == HCN_E_ENDPOINT_NOT_FOUND)
2082 {
2083 WSL_LOG(
2084 "WslMirroredNetworkManager::AddEndpoint",
2085 TraceLoggingValue("HCN/HCS returned HCN_E_ENDPOINT_NOT_FOUND - not retrying", "GnsMessage"),
2086 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.NetworkId, "networkId"),
2087 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.EndpointId, "endpointId"),
2088 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "interfaceGuid"),
2089 TraceLoggingHResult(hr, "hr"));
2090 return;
2091 }
2092
2093 try
2094 {
2095 ++endpointTrackingObject.m_retryCount;
2096
2097 if (endpointTrackingObject.m_retryCount > m_maxAddEndpointRetryCount)
2098 {
2099 WSL_LOG(
2100 "BlockedNetworkEndpoint",
2101 TraceLoggingValue("WslMirroredNetworkManager::AddEndpoint", "where"),
2102 TraceLoggingHResult(hr, "result"),
2103 TraceLoggingValue(executionStep, "executionStep"),
2104 TraceLoggingValue(endpointTrackingObject.m_networkEndpoint.InterfaceGuid, "InterfaceGuid"),
2105 TraceLoggingValue(
2106 endpointTrackingObject.m_networkEndpoint.Network ? endpointTrackingObject.m_networkEndpoint.Network->InterfaceType : 0,
2107 "InterfaceType"),
2108 TraceLoggingValue(m_vmConfig.EnableDnsTunneling, "DnsTunnelingEnabled"),
2109 TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), "HyperVFirewallEnabled"),
2110 TraceLoggingValue(m_vmConfig.EnableAutoProxy, "AutoProxyFeatureEnabled")); // the feature is enabled, but we don't know if proxy settings are actually configured
2111
2112 // we now need to guarantee that Update* gets called again - but we can't do it from this thread
2113 // will update our debounce-timer to fire soon to invoke Update - which will trigger the Blocked* path
2114 // since we are now blocked on this interface
2115 FILETIME dueTime = wil::filetime::from_int64(
2116 static_cast<ULONGLONG>(-1 * wil::filetime_duration::one_millisecond * m_debounceUpdateAllEndpointsTimerMs));
2117 SetThreadpoolTimer(m_debounceUpdateAllEndpointsDefaultTimer.get(), &dueTime, 0, 0);
2118 return;
2119 }
2120
2121 m_failedEndpointProperties.emplace_back(
2122 std::move(endpointTrackingObject.m_networkEndpoint),
2123 std::move(endpointTrackingObject.m_hnsEndpoint),
2124 endpointTrackingObject.m_retryCount);
2125
2126 FILETIME dueTime = wil::filetime::from_int64(
2127 static_cast<ULONGLONG>(-1 * wil::filetime_duration::one_millisecond * m_debounceCreateEndpointFailureTimerMs));
2128 SetThreadpoolTimer(m_debounceCreateEndpointFailureTimer.get(), &dueTime, 0, 0);
2129 }
2130 CATCH_LOG()
2131 }
2132 }
2133
2134 _Requires_lock_held_(m_networkLock)
2135 _Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::RemoveEndpoint(const GUID& endpointId) noexcept
2136 try
2137 {
2138 const auto removedFailedEndpointCount = std::erase_if(m_failedEndpointProperties, [&](const auto& endpointTracking) {
2139 return endpointTracking.m_networkEndpoint.EndpointId == endpointId;
2140 });
2141 if (removedFailedEndpointCount > 0)
2142 {
2143 WSL_LOG(
2144 "WslMirroredNetworkManager::RemoveEndpoint - Endpoint removed from m_failedEndpointProperties",
2145 TraceLoggingValue(endpointId, "endpointId"));
2146 }
2147
2148 WSL_LOG("WslMirroredNetworkManager::RemoveEndpoint", TraceLoggingValue(endpointId, "endpointId"));
2149
2150 std::vector<NetworkEndpoint>::const_iterator foundEndpoint;
2151
2152 try
2153 {
2154 foundEndpoint = std::find_if(m_networkEndpoints.cbegin(), m_networkEndpoints.cend(), [&](const auto& endpoint) {
2155 return endpoint.EndpointId == endpointId;
2156 });
2157
2158 if (foundEndpoint == m_networkEndpoints.cend())
2159 {
2160 WSL_LOG("WslMirroredNetworkManager::RemoveEndpoint - Endpoint not found", TraceLoggingValue(endpointId, "endpointId"));
2161 return S_OK;
2162 }
2163
2164 // Perform per-interface configuration of net filter rules.
2165 hns::InterfaceNetFilterRequest interfaceNetFilterRequest;
2166 interfaceNetFilterRequest.targetDeviceName = wsl::shared::string::GuidToString<wchar_t>(foundEndpoint->InterfaceGuid);
2167 interfaceNetFilterRequest.operation = hns::OperationType::Remove;
2168
2169 int linuxResultCode{};
2170 // can safely capture by ref since we are waiting
2171 auto hr = m_gnsCallbackQueue.submit_and_wait([&] {
2172 return m_callbackForGnsMessage(
2173 LxGnsMessageInterfaceNetFilter, ToJsonW(std::move(interfaceNetFilterRequest)), GnsCallbackFlags::Wait, &linuxResultCode);
2174 });
2175 LOG_IF_FAILED(hr);
2176 WSL_LOG(
2177 "WslMirroredNetworkManager::RemoveEndpoint [InterfaceNetFilterRequest]",
2178 TraceLoggingHResult(hr, "hr"),
2179 TraceLoggingValue(linuxResultCode, "linuxResultCode"),
2180 TraceLoggingValue(endpointId, "endpointId"),
2181 TraceLoggingValue(foundEndpoint->InterfaceGuid, "interfaceGuid"));
2182
2183 // A race exists between already queued operations for this interface on the GNS queue and HNS endpoint removal.
2184 // In order to resolve the race, while holding the m_networkLock, flush the GNS queue then delete the endpoint in HCS.
2185 WSL_LOG("WslMirroredNetworkManager::RemoveEndpoint", TraceLoggingValue("Flush GNS queue [queued]", "message"));
2186
2187 linuxResultCode = {};
2188 // can safely capture by ref since we are waiting
2189 hr = m_gnsCallbackQueue.submit_and_wait([&] {
2190 return m_callbackForGnsMessage(LxGnsMessageNoOp, std::wstring(L""), GnsCallbackFlags::Wait, &linuxResultCode);
2191 });
2192 WSL_LOG(
2193 "WslMirroredNetworkManager::RemoveEndpoint",
2194 TraceLoggingValue("Flush GNS queue [completed]", "message"),
2195 TraceLoggingHResult(hr, "hr"),
2196 TraceLoggingValue(linuxResultCode, "linuxResultCode"));
2197
2198 // try to delete the endpoint in HCS
2199 // Set the instance id to the mirrored interfaceGuid so HNS -> netvsc can optimally use the same vmNIC constructs when the InterfaceGuid is the same
2200
2201 hcs::ModifySettingRequest<hcs::NetworkAdapter> networkRequest{};
2202 networkRequest.ResourcePath = c_networkAdapterPrefix + wsl::shared::string::GuidToString<wchar_t>(foundEndpoint->InterfaceGuid);
2203 networkRequest.RequestType = hcs::ModifyRequestType::Remove;
2204 networkRequest.Settings.InstanceId = foundEndpoint->InterfaceGuid;
2205 networkRequest.Settings.EndpointId = endpointId;
2206
2207 const auto networkRequestString = wsl::shared::ToJsonW(networkRequest);
2208
2209 WSL_LOG(
2210 "WslMirroredNetworkManager::RemoveEndpoint : Removing the HCS mirrored endpoint [queued]",
2211 TraceLoggingValue(networkRequestString.c_str(), "networkRequest"),
2212 TraceLoggingValue(endpointId, "endpointId"));
2213 // capturing by ref because we wait for the workitem to complete
2214 hr = m_hnsQueue.submit_and_wait([&] {
2215 windows::common::hcs::ModifyComputeSystem(m_hcsSystem, networkRequestString.c_str());
2216 return S_OK;
2217 });
2218 WSL_LOG(
2219 "WslMirroredNetworkManager::RemoveEndpoint : Removing the HCS mirrored endpoint [completed]",
2220 TraceLoggingHResult(hr, "hr"));
2221
2222 if (FAILED(hr))
2223 {
2224 WSL_LOG(
2225 "RemoveMirroredEndpointFailed",
2226 TraceLoggingHResult(hr, "result"),
2227 TraceLoggingValue("RemoveHcsEndpoint", "executionStep"),
2228 TraceLoggingValue(m_vmConfig.EnableDnsTunneling, "DnsTunnelingEnabled"),
2229 TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), "HyperVFirewallEnabled"),
2230 TraceLoggingValue(m_vmConfig.EnableAutoProxy, "AutoProxyFeatureEnabled")); // the feature is enabled, but we don't know if proxy settings are actually configured
2231 }
2232 }
2233 CATCH_LOG()
2234
2235 // Remove the endpoint and its tracked state
2236 // Linux will delete any addresses and routes associated with the interface
2237 m_networkEndpoints.erase(foundEndpoint);
2238 WSL_LOG(
2239 "WslMirroredNetworkManager::RemoveEndpoint - Endpoint removed from m_networkEndpoints",
2240 TraceLoggingValue(endpointId, "endpointId"));
2241
2242 // Is this necessary?
2243 UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, "RemoveEndpoint");
2244
2245 return S_OK;
2246 }
2247 CATCH_RETURN()
2248
2249 void wsl::core::networking::WslMirroredNetworkManager::SendCreateNotificationsForInitialEndpoints() noexcept
2250 {
2251 WSL_LOG("WslMirroredNetworkManager::SendCreateNotificationsForInitialEndpoints");
2252 const auto lock = m_networkLock.lock_shared();
2253 if (m_state == State::Stopped)
2254 {
2255 return;
2256 }
2257
2258 // Perform global configuration of net filter rules.
2259 int linuxResultCode{};
2260 // can safely capture by ref since we are waiting
2261 const auto hr = m_gnsCallbackQueue.submit_and_wait([&] {
2262 return m_callbackForGnsMessage(LxGnsMessageGlobalNetFilter, std::wstring(L""), GnsCallbackFlags::Wait, &linuxResultCode);
2263 });
2264 WSL_LOG(
2265 "WslMirroredNetworkManager::SendCreateNotificationsForInitialEndpoints",
2266 TraceLoggingValue("Sent message to perform global configuration of net filter rules", "message"),
2267 TraceLoggingHResult(hr, "hr"),
2268 TraceLoggingValue(linuxResultCode, "linuxResultCode"));
2269 LOG_IF_FAILED(hr);
2270 }
2271
2272 HRESULT wsl::core::networking::WslMirroredNetworkManager::WaitForMirroredGoalState() noexcept
2273 {
2274 WSL_LOG("WslMirroredNetworkManager::WaitForMirroredGoalState");
2275
2276 return (m_inMirroredGoalState.wait(c_initialMirroredGoalStateWaitTimeoutMs)) ? S_OK : HRESULT_FROM_WIN32(ERROR_TIMEOUT);
2277 }
2278
2279 _Check_return_ bool wsl::core::networking::WslMirroredNetworkManager::DoesEndpointExist(GUID networkId) const noexcept
2280 try
2281 {
2282 const auto lock = m_networkLock.lock_shared();
2283 if (m_state == State::Stopped)
2284 {
2285 return false;
2286 }
2287
2288 return std::ranges::any_of(m_networkEndpoints, [&](const NetworkEndpoint& endpoint) { return endpoint.NetworkId == networkId; });
2289 }
2290 catch (...)
2291 {
2292 LOG_CAUGHT_EXCEPTION();
2293 return false;
2294 }
2295
2296 _Requires_lock_not_held_(m_networkLock)
2297 void wsl::core::networking::WslMirroredNetworkManager::UpdateAllEndpoints(_In_ PCSTR sourceName) noexcept
2298 {
2299 const auto lock = m_networkLock.lock_exclusive();
2300 if (m_state == State::Stopped)
2301 {
2302 return;
2303 }
2304
2305 UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, sourceName);
2306 }
2307
2308 void wsl::core::networking::WslMirroredNetworkManager::OnNetworkConnectivityHintChange() noexcept
2309 {
2310 const auto lock = m_networkLock.lock_exclusive();
2311 if (m_state == State::Stopped)
2312 {
2313 return;
2314 }
2315
2316 UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, "OnNetworkConnectivityHintChange");
2317 }
2318
2319 // Strategy for handling notifications from HNS:
2320 // 1) Always consume the data immediately.
2321 // 2) If UpdateAllEndpointsImpl hasn't run for >= m_debounceUpdateAllEndpointsTimerMs then run it.
2322 // 3) If UpdateAllEndpointsImpl has run < m_debounceUpdateAllEndpointsTimerMs ago, schedule the timer.
2323 void wsl::core::networking::WslMirroredNetworkManager::OnNetworkEndpointChange() noexcept
2324 {
2325 const auto lock = m_networkLock.lock_exclusive();
2326 if (m_state == State::Stopped)
2327 {
2328 return;
2329 }
2330
2331 UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, "OnNetworkEndpointChange");
2332 }
2333
2334 void wsl::core::networking::WslMirroredNetworkManager::OnDnsSuffixChange() noexcept
2335 try
2336 {
2337 const auto lock = m_networkLock.lock_exclusive();
2338 if (m_state == State::Stopped)
2339 {
2340 return;
2341 }
2342
2343 UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, "OnDnsSuffixChange");
2344 }
2345 CATCH_LOG();
2346
2347 void wsl::core::networking::WslMirroredNetworkManager::TunAdapterStateChanged(_In_ const std::string& interfaceName, _In_ bool up) noexcept
2348 {
2349 }
2350
2351 void wsl::core::networking::WslMirroredNetworkManager::ReconnectGuestNetwork()
2352 {
2353 auto lock = m_networkLock.lock_exclusive();
2354 if (m_state == State::Stopped)
2355 {
2356 return;
2357 }
2358
2359 WSL_LOG("WslMirroredNetworkManager::ReconnectGuestNetwork");
2360 UpdateAllEndpointsImpl(UpdateEndpointFlag::ForceUpdate, "ReconnectGuestNetwork");
2361 }
2362
2363 _Requires_lock_held_(m_networkLock)
2364 wsl::core::networking::NetworkSettings wsl::core::networking::WslMirroredNetworkManager::GetNetworkSettingsOfInterface(DWORD ifIndex) const
2365 {
2366 const auto matchingEndpoint =
2367 std::ranges::find_if(m_networkEndpoints, [&](const auto& endpoint) { return endpoint.Network->InterfaceIndex == ifIndex; });
2368 if (matchingEndpoint == std::end(m_networkEndpoints))
2369 {
2370 WSL_LOG("GetNetworkSettingsOfInterface - Network not found", TraceLoggingValue(ifIndex, "ifIndex"));
2371 return {};
2372 }
2373 else
2374 {
2375 WSL_LOG("GetNetworkSettingsOfInterface", TRACE_NETWORKSETTINGS_OBJECT(matchingEndpoint->Network.get()));
2376 return *matchingEndpoint->Network;
2377 }
2378 }
2379
2380 std::shared_ptr<wsl::core::networking::NetworkSettings> wsl::core::networking::WslMirroredNetworkManager::GetEndpointSettings(
2381 const hns::HNSEndpoint& endpointProperties) const
2382 {
2383 return wsl::core::networking::GetEndpointSettings(endpointProperties);
2384 }
2385
2386 _Requires_lock_held_(m_networkLock)
2387 _Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::UpdateHcnServiceTimer() noexcept
2388 try
2389 {
2390 // These values are chosen so that the connection will be retried 5 times.
2391 static constexpr DWORD INITIAL_RETRY_HCN_SERVICE_CONNECTION_TIMER_DURATION_MS = 1000; // 1 second.
2392 static constexpr DWORD MAX_RETRY_HCN_SERVICE_CONNECTION_TIMER_DURATION_MS = 80000; // ~1.3 minute.
2393
2394 // Check if the maximum retry attempt count has been reached.
2395 if (m_retryHcnServiceConnectionDurationMs <= MAX_RETRY_HCN_SERVICE_CONNECTION_TIMER_DURATION_MS)
2396 {
2397 // Determine how long until the timer should fire.
2398 if (m_retryHcnServiceConnectionDurationMs == 0)
2399 {
2400 // Use the initial duration value as this is the first time the timer is being armed.
2401 m_retryHcnServiceConnectionDurationMs = INITIAL_RETRY_HCN_SERVICE_CONNECTION_TIMER_DURATION_MS;
2402 }
2403 else
2404 {
2405 // Make sure that the timer duration can't overflow.
2406 static_assert(MAX_RETRY_HCN_SERVICE_CONNECTION_TIMER_DURATION_MS < (DWORD_MAX / 2));
2407
2408 // Apply an exponential backoff.
2409 m_retryHcnServiceConnectionDurationMs *= 2;
2410 }
2411
2412 WSL_LOG(
2413 "WslMirroredNetworkManager::UpdateHcnServiceTimer",
2414 TraceLoggingValue(m_retryHcnServiceConnectionDurationMs, "m_retryHcnServiceConnectionDurationMs"));
2415
2416 FILETIME dueTime = wil::filetime::from_int64(
2417 static_cast<ULONGLONG>(-1 * wil::filetime_duration::one_millisecond * m_retryHcnServiceConnectionDurationMs));
2418 SetThreadpoolTimer(m_retryHcnServiceConnectionTimer.get(), &dueTime, 0, 1000);
2419 }
2420 else
2421 {
2422 WSL_LOG(
2423 "WslMirroredNetworkManager::UpdateHcnServiceTimer",
2424 TraceLoggingValue(0, "retryHcnServiceConnectionDurationMs (service is not active)"));
2425 THROW_WIN32(ERROR_SERVICE_NOT_ACTIVE);
2426 }
2427
2428 return S_OK;
2429 }
2430 CATCH_RETURN()
2431
2432 _Requires_lock_held_(m_networkLock)
2433 _Check_return_ HRESULT wsl::core::networking::WslMirroredNetworkManager::ResetHcnServiceSession() noexcept
2434 try
2435 {
2436 if (!m_hcnCallback)
2437 {
2438 WSL_LOG("WslMirroredNetworkManager::ResetHcnServiceSession - attempting to re-register"); // Attempt to resubscribe to HNS notifications.
2439 m_hcnCallback = windows::common::hcs::RegisterServiceCallback(HcnCallback, this);
2440
2441 // if we can reregister, reset the retry timer.
2442 m_retryHcnServiceConnectionDurationMs = 0;
2443 SetThreadpoolTimer(m_retryHcnServiceConnectionTimer.get(), nullptr, 0, 0);
2444
2445 std::vector<GUID> enumeratedNetworkIds;
2446 try
2447 {
2448 // Refresh the current list of networks. The list will then be kept
2449 // up to date by the subscription notifications.
2450 enumeratedNetworkIds = EnumerateMirroredNetworks();
2451 }
2452 catch (...)
2453 {
2454 const auto hr = wil::ResultFromCaughtException();
2455 WSL_LOG(
2456 "ResetHcnServiceSessionFailed",
2457 TraceLoggingValue(hr, "result"),
2458 TraceLoggingValue("HcnEnumerateNetworks", "executionStep"),
2459 TraceLoggingValue("Mirrored", "networkingMode"),
2460 TraceLoggingValue(m_vmConfig.EnableDnsTunneling, "DnsTunnelingEnabled"),
2461 TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), "HyperVFirewallEnabled"),
2462 TraceLoggingValue(m_vmConfig.EnableAutoProxy, "AutoProxyFeatureEnabled")); // the feature is enabled, but we don't know if proxy settings are actually configured
2463
2464 throw;
2465 }
2466
2467 wil::unique_cotaskmem_string response;
2468 wil::unique_cotaskmem_string error;
2469 const auto enumEndpointsHr = HcnEnumerateEndpoints(nullptr, &response, &error);
2470 if (FAILED(enumEndpointsHr))
2471 {
2472 WSL_LOG(
2473 "ResetHcnServiceSessionFailed",
2474 TraceLoggingValue(enumEndpointsHr, "result"),
2475 TraceLoggingValue("HcnEnumerateEndpoints", "executionStep"),
2476 TraceLoggingValue("Mirrored", "networkingMode"),
2477 TraceLoggingValue(m_vmConfig.EnableDnsTunneling, "DnsTunnelingEnabled"),
2478 TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), "HyperVFirewallEnabled"),
2479 TraceLoggingValue(m_vmConfig.EnableAutoProxy, "AutoProxyFeatureEnabled")); // the feature is enabled, but we don't know if proxy settings are actually configured
2480 }
2481 else
2482 {
2483 WSL_LOG(
2484 "WslMirroredNetworkManager::ResetHcnServiceSession - HcnEnumerateEndpoints",
2485 TraceLoggingValue(response.get(), "response"));
2486 }
2487
2488 for (const auto& networkId : enumeratedNetworkIds)
2489 {
2490 // Must call back through MirroredNetworking to create a new Endpoint
2491 // note that the callback will not block - it just queues the work in MirroredNetworking
2492 LOG_IF_FAILED(AddNetwork(networkId));
2493 }
2494 }
2495 else
2496 {
2497 WSL_LOG("WslMirroredNetworkManager::ResetHcnServiceSession - already re-registered");
2498 }
2499
2500 return S_OK;
2501 }
2502 CATCH_RETURN()
2503
2504 void wsl::core::networking::WslMirroredNetworkManager::TelemetryConnectionCallback(NLM_CONNECTIVITY hostConnectivity, uint32_t telemetryCounter) noexcept
2505 try
2506 {
2507 WSL_LOG("WslMirroredNetworkManager::TelemetryConnectionCallback");
2508
2509 const auto lock = m_networkLock.lock_exclusive();
2510 if (m_state == State::Stopped)
2511 {
2512 return;
2513 }
2514
2515 // if this is the inital callback for checking container connectivity, push this through as telemetry, so we can observe the time-to-connect
2516 if ((telemetryCounter > 1) && !(hostConnectivity & NLM_CONNECTIVITY_IPV4_INTERNET) && !(hostConnectivity & NLM_CONNECTIVITY_IPV6_INTERNET))
2517 {
2518 WSL_LOG(
2519 "WslMirroredNetworkManager::TelemetryConnectionCallback - not testing connectivity - host is not connected",
2520 TraceLoggingValue(telemetryCounter, "telemetryCounter"),
2521 TraceLoggingValue(wsl::core::networking::ToString(hostConnectivity).c_str(), "HostConnectivityLevel"));
2522 return;
2523 }
2524
2525 int returnedIPv4Value{};
2526 LOG_IF_FAILED(m_gnsCallbackQueue.submit_and_wait([&] {
2527 return m_callbackForGnsMessage(LxGnsMessageConnectTestRequest, c_ipv4TestRequestTarget, GnsCallbackFlags::Wait, &returnedIPv4Value);
2528 }));
2529
2530 int returnedIPv6Value{};
2531 LOG_IF_FAILED(m_gnsCallbackQueue.submit_and_wait([&] {
2532 return m_callbackForGnsMessage(LxGnsMessageConnectTestRequest, c_ipv6TestRequestTarget, GnsCallbackFlags::Wait, &returnedIPv6Value);
2533 }));
2534
2535 // make the same connect requests as we just requested from the container
2536 const auto hostConnectivityCheck =
2537 wsl::shared::conncheck::CheckConnection(c_ipv4TestRequestTargetA, c_ipv6TestRequestTargetA, "80");
2538 const auto WindowsIpv4ConnCheckStatus = static_cast<uint32_t>(hostConnectivityCheck.Ipv4Status);
2539 const auto WindowsIpv6ConnCheckStatus = static_cast<uint32_t>(hostConnectivityCheck.Ipv6Status);
2540 const auto WindowsIPv4NlmConnectivityLevel = ConnectivityTelemetry::WindowsIPv4NlmConnectivityLevel(hostConnectivity);
2541 const auto WindowsIPv6NlmConnectivityLevel = ConnectivityTelemetry::WindowsIPv6NlmConnectivityLevel(hostConnectivity);
2542 const auto LinuxIPv4ConnCheckStatus = ConnectivityTelemetry::LinuxIPv4ConnCheckResult(returnedIPv4Value);
2543 const auto LinuxIPv6ConnCheckStatus = ConnectivityTelemetry::LinuxIPv6ConnCheckResult(returnedIPv6Value);
2544
2545 const auto timeFromObjectCreation = std::chrono::steady_clock::now() - m_objectCreationTime;
2546
2547 // Logs when network connectivity changes, used to compare network connectivity in the guest to the host to determine networking health
2548 WSL_LOG_TELEMETRY(
2549 "TelemetryConnectionCallback",
2550 PDT_ProductAndServicePerformance,
2551 TraceLoggingValue("Mirrored", "networkingMode"),
2552 TraceLoggingValue(telemetryCounter, "telemetryCounter"),
2553 TraceLoggingValue(
2554 (std::chrono::duration_cast<std::chrono::milliseconds>(timeFromObjectCreation)).count(), "timeFromObjectCreationMs"),
2555 TraceLoggingValue(wsl::core::networking::ToString(hostConnectivity).c_str(), "HostConnectivityLevel"),
2556 TraceLoggingValue(WindowsIPv4NlmConnectivityLevel, "WindowsIPv4ConnectivityLevel"),
2557 TraceLoggingValue(WindowsIPv6NlmConnectivityLevel, "WindowsIPv6ConnectivityLevel"),
2558 TraceLoggingValue(LinuxIPv4ConnCheckStatus, "LinuxIPv4ConnCheckStatus"),
2559 TraceLoggingValue(LinuxIPv6ConnCheckStatus, "LinuxIPv6ConnCheckStatus"),
2560 TraceLoggingValue(WindowsIpv4ConnCheckStatus, "WindowsIpv4ConnCheckStatus"),
2561 TraceLoggingValue(WindowsIpv6ConnCheckStatus, "WindowsIpv6ConnCheckStatus"),
2562 TraceLoggingValue(m_vmConfig.EnableDnsTunneling, "DnsTunnelingEnabled"),
2563 TraceLoggingValue(m_dnsTunnelingIpAddress.c_str(), "DnsTunnelingIpAddress"),
2564 TraceLoggingValue(m_vmConfig.FirewallConfig.Enabled(), "HyperVFirewallEnabled"),
2565 TraceLoggingValue(m_vmConfig.EnableAutoProxy, "AutoProxyFeatureEnabled")); // the feature is enabled, but we don't know if proxy settings are actually configured
2566 }
2567 CATCH_LOG()
2568
2569 void __stdcall wsl::core::networking::WslMirroredNetworkManager::HcnServiceConnectionTimerCallback(
2570 _Inout_ PTP_CALLBACK_INSTANCE, _Inout_opt_ PVOID Context, _Inout_ PTP_TIMER) noexcept
2571 {
2572 WSL_LOG("WslMirroredNetworkManager::HcnServiceConnectionTimerCallback");
2573
2574 auto* const manager = static_cast<WslMirroredNetworkManager*>(Context);
2575
2576 const auto lock = manager->m_networkLock.lock_exclusive();
2577 if (manager->m_state == State::Stopped)
2578 {
2579 return;
2580 }
2581
2582 if (FAILED(manager->ResetHcnServiceSession()))
2583 {
2584 // The retry attempt was unsuccessful, re-arm the timer to try again.
2585 LOG_IF_FAILED(manager->UpdateHcnServiceTimer());
2586 }
2587 }
2588
2589 void CALLBACK wsl::core::networking::WslMirroredNetworkManager::HcnCallback(
2590 _In_ DWORD NotificationType, _In_opt_ void* Context, _In_ HRESULT, _In_opt_ PCWSTR NotificationData) noexcept
2591 try
2592 {
2593 hns::NotificationBase data = {};
2594 if (NotificationType == HcnNotificationNetworkCreate || NotificationType == HcnNotificationNetworkPreDelete)
2595 {
2596 data = FromJson<hns::NotificationBase>(NotificationData);
2597 }
2598
2599 auto* const manager = static_cast<WslMirroredNetworkManager*>(Context);
2600
2601 const auto lock = manager->m_networkLock.lock_exclusive();
2602 if (manager->m_state == State::Stopped)
2603 {
2604 return;
2605 }
2606
2607 WSL_LOG(
2608 "WslMirroredNetworkManager::HcnCallback [HcnRegisterServiceCallback]",
2609 TraceLoggingValue(NotificationType, "notificationType"),
2610 TraceLoggingValue(wsl::windows::common::stringify::HcnNotificationsToString(NotificationType), "notificationTypeString"),
2611 TraceLoggingValue(data.ID, "networkId"),
2612 TraceLoggingValue(data.Flags, "flags"),
2613 TraceLoggingValue(NotificationData, "notificationData"));
2614
2615 switch (NotificationType)
2616 {
2617 case HcnNotificationNetworkCreate:
2618 {
2619 // convert the enum to integer to allow for bitmap comparisons
2620 if (!WI_IsFlagSet(data.Flags, WI_EnumValue(hns::NetworkFlags::EnableFlowSteering)))
2621 {
2622 WSL_LOG("WslMirroredNetworkManager::HcnCallback [HcnRegisterServiceCallback] - not a mirrored network");
2623 return;
2624 }
2625
2626 LOG_IF_FAILED(manager->AddNetwork(data.ID));
2627 break;
2628 }
2629
2630 case HcnNotificationNetworkPreDelete:
2631 {
2632 // This notification is fired off right before HNS network deletion.
2633 // Ensure Containers release endpoints whether network deletion
2634 // is successful or not.
2635 LOG_IF_FAILED(manager->RemoveNetwork(data.ID));
2636 break;
2637 }
2638
2639 case HcnNotificationServiceDisconnect:
2640 {
2641 // This notification indicates that the subscription has become invalid due to a loss
2642 // of connection to the server. This typically means that the HNS service has been
2643 // stopped or restarted.
2644 manager->m_hcnCallback.reset();
2645
2646 manager->m_networkEndpoints.clear();
2647
2648 LOG_IF_FAILED(manager->UpdateHcnServiceTimer());
2649 break;
2650 }
2651 }
2652 manager->UpdateAllEndpointsImpl(UpdateEndpointFlag::Default, "HcnCallback");
2653 }
2654 CATCH_LOG()
2655
2656 const char* wsl::core::networking::WslMirroredNetworkManager::StateToString(State state) noexcept
2657 {
2658 switch (state)
2659 {
2660 case State::Stopped:
2661 return "Stopped";
2662 case State::Started:
2663 return "Started";
2664 case State::Starting:
2665 return "Starting";
2666 default:
2667 return "Unknown";
2668 }
2669 }
2670
2671 void wsl::core::networking::WslMirroredNetworkManager::TraceLoggingRundown() const
2672 {
2673 auto lock = m_networkLock.lock_shared();
2674
2675 WSL_LOG(
2676 "WslMirroredNetworkManager::TraceLoggingRundown",
2677 TraceLoggingValue("Global State"),
2678 TraceLoggingValue(StateToString(m_state), "state"),
2679 TraceLoggingValue(GenerateResolvConf(m_trackedDnsInfo).c_str(), "dnsInfo"));
2680
2681 for (const auto& network : m_networkEndpoints)
2682 {
2683 WSL_LOG("WslMirroredNetworkManager::TraceLoggingRundown", TRACE_NETWORKSETTINGS_OBJECT(network.Network));
2684
2685 if (network.StateTracking)
2686 {
2687 WSL_LOG(
2688 "WslMirroredNetworkManager::TraceLoggingRundown",
2689 TraceLoggingValue("IpStateTracking Interface Info"),
2690 TraceLoggingValue(network.StateTracking->InterfaceGuid, "interfaceGuid"),
2691 TraceLoggingValue(network.StateTracking->InterfaceMtu, "mtu"));
2692
2693 for (const auto& address : network.StateTracking->IpAddresses)
2694 {
2695 WSL_LOG(
2696 "WslMirroredNetworkManager::TraceLoggingRundown",
2697 TraceLoggingValue("IpStateTracking::IpAddresses"),
2698 TraceLoggingValue(address.Address.AddressString.c_str(), "address"),
2699 TraceLoggingValue(address.Address.PrefixLength, "prefixLength"),
2700 TraceLoggingValue(wsl::core::networking::ToString(address.SyncStatus), "syncStatus"),
2701 TraceLoggingValue(address.SyncRetryCount, "syncRetryCount"),
2702 TraceLoggingValue(address.LoopbackSyncRetryCount, "loopbackSyncRetryCount"));
2703 }
2704
2705 for (const auto& route : network.StateTracking->Routes)
2706 {
2707 WSL_LOG(
2708 "WslMirroredNetworkManager::TraceLoggingRundown",
2709 TraceLoggingValue("IpStateTracking::Routes"),
2710 TraceLoggingValue(route.Route.ToString().c_str(), "route"),
2711 TraceLoggingValue(route.Route.Metric, "metric"),
2712 TraceLoggingValue(
2713 !route.CanConflictWithLinuxAutoGenRoute() || route.LinuxConflictRemoved,
2714 "linuxConflictRemovedOrDoesntExist"),
2715 TraceLoggingValue(wsl::core::networking::ToString(route.SyncStatus), "syncStatus"),
2716 TraceLoggingValue(route.SyncRetryCount, "syncRetryCount"));
2717 }
2718 }
2719 }
2720 }