master
cpp 750 lines 26.6 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 LxssIptables.cpp
8
9 Abstract:
10
11 This file contains iptables-related function definitions.
12
13 --*/
14
15 #include "precomp.h"
16 #include <mi.h>
17 #include "LxssIpTables.h"
18
19 using unique_safearray = wil::unique_any<SAFEARRAY*, decltype(&SafeArrayDestroy), SafeArrayDestroy>;
20
21 using namespace std::placeholders;
22
23 // LxssIpTables class functions.
24
25 LxssIpTables::LxssIpTables()
26 {
27 return;
28 }
29
30 std::wstring LxssIpTables::AddressStringFromAddress(const IP_ADDRESS_PREFIX& Address, bool AddPrefixLength)
31 {
32 std::wstringstream addressStream;
33 addressStream << Address.Prefix.Ipv4.sin_addr.S_un.S_un_b.s_b1 << L"." << Address.Prefix.Ipv4.sin_addr.S_un.S_un_b.s_b2 << L"."
34 << Address.Prefix.Ipv4.sin_addr.S_un.S_un_b.s_b3 << L"." << Address.Prefix.Ipv4.sin_addr.S_un.S_un_b.s_b4;
35
36 if (AddPrefixLength)
37 {
38 addressStream << L"/" << Address.PrefixLength;
39 }
40
41 return addressStream.str();
42 }
43
44 void LxssIpTables::CleanupRemnants()
45 {
46 try
47 {
48 LxssNetworkingNat::CleanupRemnants();
49 }
50 CATCH_LOG()
51
52 try
53 {
54 LxssNetworkingFirewall::CleanupRemnants();
55 }
56 CATCH_LOG()
57 }
58
59 void LxssIpTables::EnableIpTablesSupport(_In_ const wil::unique_handle& InstanceHandle)
60 {
61 //
62 // Passing 'this' is OK because unregistration will be done in the
63 // destructor.
64 //
65
66 auto callback = std::bind(KernelCallbackProxy, this, _1, _2);
67 m_kernelCallback =
68 LxssUserCallback::Register(InstanceHandle.get(), LxBusUserCallbackTypeIptables, callback, sizeof(LXBUS_USER_CALLBACK_NETWORK_DATA));
69 }
70
71 bool LxssIpTables::IsAllowedInputPrefix(_In_ CONST IP_ADDRESS_PREFIX& InputPrefix)
72 {
73 if (InputPrefix.Prefix.si_family != AF_INET)
74 {
75 LOG_HR_MSG(E_NOTIMPL, "IPv6 addresses for NAT not supported");
76 return false;
77 }
78
79 if (InputPrefix.Prefix.Ipv4.sin_port != 0)
80 {
81 LOG_HR_MSG(E_NOTIMPL, "Specific ports for NAT not supported");
82 return false;
83 }
84
85 // TODO_LX: Currently there is an agreement in place with HNS to restrict
86 // the NAT address range to 172.17.0.0/16.
87 if ((InputPrefix.Prefix.Ipv4.sin_addr.S_un.S_un_b.s_b1 != 172) || (InputPrefix.Prefix.Ipv4.sin_addr.S_un.S_un_b.s_b2 != 17) ||
88 (InputPrefix.PrefixLength < 16))
89 {
90 LOG_HR_MSG(E_NOTIMPL, "Address not supported for NAT: %ls", LxssIpTables::AddressStringFromAddress(InputPrefix, true).c_str());
91
92 return false;
93 }
94
95 return true;
96 }
97
98 NTSTATUS
99 LxssIpTables::KernelCallback(_In_ PVOID CallbackBuffer, _In_ ULONG_PTR CallbackBufferSize)
100 {
101 if (CallbackBufferSize < sizeof(LXBUS_USER_CALLBACK_IPTABLES_DATA))
102 {
103 WI_ASSERT_MSG(false, "Kernel provided unexpected data for user-mode callback.");
104
105 return STATUS_INVALID_PARAMETER;
106 }
107
108 const auto callbackData = static_cast<PLXBUS_USER_CALLBACK_IPTABLES_DATA>(CallbackBuffer);
109
110 switch (callbackData->IptablesDataType)
111 {
112 case LxBusUserCallbackIptablesDataTypeMasquerade:
113 return KernelCallbackMasquerade(callbackData);
114 case LxBusUserCallbackIptablesDataTypePort:
115 return KernelCallbackFirewallPort(callbackData);
116 default:
117 WI_ASSERT_MSG(false, "Kernel provided unexpected data for user-mode callback.");
118
119 return STATUS_INVALID_PARAMETER;
120 }
121 }
122
123 NTSTATUS
124 LxssIpTables::KernelCallbackFirewallPort(_In_ PLXBUS_USER_CALLBACK_IPTABLES_DATA CallbackData)
125 {
126 CONST IP_ADDRESS_PREFIX& inputPrefix = CallbackData->Data.Port.InputPrefix;
127
128 if (inputPrefix.Prefix.si_family != AF_INET)
129 {
130 LOG_HR_MSG(E_INVALIDARG, "IPv6 addresses for firewall ports not supported");
131
132 return STATUS_INVALID_PARAMETER;
133 }
134
135 if (inputPrefix.Prefix.Ipv4.sin_port == 0)
136 {
137 LOG_HR_MSG(E_INVALIDARG, "No port specified");
138 return STATUS_INVALID_PARAMETER;
139 }
140
141 NTSTATUS status;
142 std::lock_guard<std::mutex> lock(m_lock);
143 if (CallbackData->Data.Port.Enable == FALSE)
144 {
145 status = STATUS_NOT_FOUND;
146 const auto foundEntry = std::find_if(m_firewallPorts.begin(), m_firewallPorts.end(), [&](const auto& next) {
147 return (memcmp(&inputPrefix, &next->Address(), sizeof(inputPrefix)) == 0);
148 });
149
150 if (foundEntry != m_firewallPorts.end())
151 {
152 status = STATUS_INVALID_PARAMETER;
153 try
154 {
155 m_firewallPorts.erase(foundEntry);
156 status = STATUS_SUCCESS;
157 }
158 CATCH_LOG_MSG("Failed to remove firewall port rule.")
159 }
160 }
161 else
162 {
163 status = STATUS_INVALID_PARAMETER;
164 try
165 {
166 std::shared_ptr<LxssNetworkingFirewall> firewall;
167 if (m_firewallPorts.empty())
168 {
169 firewall = std::make_shared<LxssNetworkingFirewall>();
170 }
171 else
172 {
173 firewall = m_firewallPorts.front()->Firewall();
174 }
175
176 auto newPortRule = std::make_unique<LxssNetworkingFirewallPort>(firewall, inputPrefix);
177
178 m_firewallPorts.emplace_back(std::move(newPortRule));
179 status = STATUS_SUCCESS;
180 }
181 CATCH_LOG_MSG("Failed to create new firewall port rule.")
182 }
183
184 return status;
185 }
186
187 NTSTATUS
188 LxssIpTables::KernelCallbackMasquerade(_In_ PLXBUS_USER_CALLBACK_IPTABLES_DATA CallbackData)
189 {
190 CONST IP_ADDRESS_PREFIX& inputPrefix = CallbackData->Data.Masquerade.InputPrefix;
191
192 if (!IsAllowedInputPrefix(inputPrefix))
193 {
194 return STATUS_INVALID_PARAMETER;
195 }
196
197 NTSTATUS status;
198 std::lock_guard<std::mutex> lock(m_lock);
199 if (CallbackData->Data.Masquerade.Enable == FALSE)
200 {
201 status = STATUS_NOT_FOUND;
202 const auto foundEntry = std::find_if(m_networkTranslators.begin(), m_networkTranslators.end(), [&](const auto& next) {
203 return (memcmp(&inputPrefix, &next->Address(), sizeof(inputPrefix)) == 0);
204 });
205
206 if (foundEntry != m_networkTranslators.end())
207 {
208 status = STATUS_INVALID_PARAMETER;
209 try
210 {
211 m_networkTranslators.erase(foundEntry);
212 status = STATUS_SUCCESS;
213 }
214 CATCH_LOG_MSG("Failed to remove NAT.")
215 }
216 }
217 else
218 {
219 status = STATUS_INVALID_PARAMETER;
220 try
221 {
222 auto newNat = std::make_unique<LxssNetworkingNat>(inputPrefix);
223 m_networkTranslators.emplace_back(std::move(newNat));
224 status = STATUS_SUCCESS;
225 }
226 CATCH_LOG_MSG("Failed to create new NAT.")
227 }
228
229 return status;
230 }
231
232 NTSTATUS
233 LxssIpTables::KernelCallbackProxy(_Inout_ LxssIpTables* Self, _In_ PVOID CallbackBuffer, _In_ ULONG_PTR CallbackBufferSize)
234 {
235 return Self->KernelCallback(CallbackBuffer, CallbackBufferSize);
236 }
237
238 // LxssManagementInterface class functions.
239
240 std::weak_ptr<MI_Application> LxssManagementInterface::s_application;
241 const std::wstring LxssManagementInterface::s_localRoot(L"ROOT/StandardCimv2");
242 std::mutex LxssManagementInterface::s_lock;
243
244 unique_mi_instance LxssManagementInterface::CloneInstance(_In_ const MI_Instance* InstanceToClone)
245 {
246 MI_Instance* rawInstance;
247 const MI_Result result = MI_Instance_Clone(InstanceToClone, &rawInstance);
248 THROW_HR_IF_MSG(E_FAIL, (result != MI_RESULT_OK), "Failed with error %d", result);
249
250 return unique_mi_instance(rawInstance, std::bind(CloseInstance, GetGlobalApplication(), _1));
251 }
252
253 void LxssManagementInterface::CloseGlobalApplication(_Inout_ MI_Application* Application)
254 {
255 WI_VERIFY(MI_Application_Close(Application) == MI_RESULT_OK);
256 delete Application;
257 }
258
259 void LxssManagementInterface::CloseInstance(_In_ std::shared_ptr<MI_Application>, _Inout_ MI_Instance* Instance)
260 {
261 WI_VERIFY(MI_Instance_Delete(Instance) == MI_RESULT_OK);
262 }
263
264 void LxssManagementInterface::CloseOperation(_Inout_ MI_Operation* Operation)
265 {
266 // If an operation is in progress, close will wait for it to complete.
267 // Always attempt to cancel the operation before closing it.
268 (void)MI_Operation_Cancel(Operation, MI_REASON_NONE);
269 WI_VERIFY(MI_Operation_Close(Operation) == MI_RESULT_OK);
270 }
271
272 void LxssManagementInterface::CloseSession(_In_ std::shared_ptr<MI_Application>, _Inout_ MI_Session* Session)
273 {
274 WI_VERIFY(MI_Session_Close(Session, NULL, NULL) == MI_RESULT_OK);
275 delete Session;
276 }
277
278 std::shared_ptr<MI_Application> LxssManagementInterface::GetGlobalApplication()
279 {
280 std::shared_ptr<MI_Application> globalApplicationInstance;
281 std::lock_guard<std::mutex> lock(s_lock);
282 globalApplicationInstance = s_application.lock();
283 if (!globalApplicationInstance)
284 {
285 std::unique_ptr<MI_Application> rawApplication(new MI_Application);
286 const MI_Result result = MI_Application_Initialize(0, nullptr, nullptr, rawApplication.get());
287
288 THROW_HR_IF_MSG(E_FAIL, (result != MI_RESULT_OK), "Failed with error %d", result);
289
290 globalApplicationInstance = std::shared_ptr<MI_Application>(rawApplication.release(), CloseGlobalApplication);
291
292 s_application = globalApplicationInstance;
293 }
294
295 return globalApplicationInstance;
296 }
297
298 unique_mi_instance LxssManagementInterface::NewInstance(_In_ const std::wstring& ClassName, _In_opt_ const MI_Class* Class)
299 {
300 auto application = GetGlobalApplication();
301 MI_Result result;
302 MI_Instance* rawInstance;
303 if (Class == nullptr)
304 {
305 result = MI_Application_NewInstance(application.get(), ClassName.c_str(), nullptr, &rawInstance);
306 }
307 else
308 {
309 result = MI_Application_NewInstanceFromClass(application.get(), ClassName.c_str(), Class, &rawInstance);
310 }
311
312 THROW_HR_IF_MSG(E_FAIL, (result != MI_RESULT_OK), "Failed with error %d", result);
313
314 WI_ASSERT(rawInstance != nullptr);
315
316 return unique_mi_instance(rawInstance, std::bind(CloseInstance, application, _1));
317 }
318
319 unique_mi_session LxssManagementInterface::NewSession()
320 {
321 auto application = GetGlobalApplication();
322 std::unique_ptr<MI_Session> rawSession(new MI_Session());
323 const MI_Result result = MI_Application_NewSession(application.get(), nullptr, nullptr, nullptr, nullptr, nullptr, rawSession.get());
324
325 THROW_HR_IF_MSG(E_FAIL, (result != MI_RESULT_OK), "Failed with error %d", result);
326
327 return unique_mi_session(rawSession.release(), std::bind(CloseSession, application, _1));
328 }
329
330 // LxssNetworkingFirewall class functions
331
332 const wil::unique_bstr LxssNetworkingFirewall::s_DefaultRuleDescription(wil::make_bstr_failfast(L"WSL iptables entry"));
333
334 const std::wstring LxssNetworkingFirewall::s_FriendlyNamePrefix(L"WSLRULE_17774471984f_");
335
336 LxssNetworkingFirewall::LxssNetworkingFirewall()
337 {
338 m_firewall = wil::CoCreateInstance<NetFwPolicy2, INetFwPolicy2>(CLSCTX_INPROC_SERVER);
339 }
340
341 void LxssNetworkingFirewall::CopyPartialArray(SAFEARRAY* Destination, SAFEARRAY* Source, ULONG DestinationIndexStart, ULONG SourceIndexStart, ULONG ElementsToCopy)
342 {
343 if (ElementsToCopy == 0)
344 {
345 return;
346 }
347
348 // Sanity check destination
349 THROW_HR_IF(E_INVALIDARG, SafeArrayGetDim(Destination) != 1);
350 LONG firstIndex;
351 // Only expecting arrays to start at 0, so enforce that for now.
352 THROW_IF_FAILED(SafeArrayGetLBound(Destination, 1, &firstIndex));
353 THROW_HR_IF(E_INVALIDARG, (firstIndex != 0));
354 ULONG lastIndex;
355 THROW_IF_FAILED(SafeArrayGetUBound(Destination, 1, (PLONG)&lastIndex));
356 ULONG requestedLastIndex;
357 THROW_IF_FAILED(ULongAdd(DestinationIndexStart, (ElementsToCopy - 1), &requestedLastIndex));
358
359 THROW_HR_IF(E_INVALIDARG, (requestedLastIndex > lastIndex));
360 // Sanity check source.
361 THROW_HR_IF(E_INVALIDARG, SafeArrayGetDim(Source) != 1);
362 // Only expecting arrays to start at 0, so enforce that for now.
363 THROW_IF_FAILED(SafeArrayGetLBound(Source, 1, &firstIndex));
364 THROW_HR_IF(E_INVALIDARG, (firstIndex != 0));
365 THROW_IF_FAILED(SafeArrayGetUBound(Source, 1, (PLONG)&lastIndex));
366 THROW_IF_FAILED(ULongAdd(SourceIndexStart, (ElementsToCopy - 1), &requestedLastIndex));
367
368 THROW_HR_IF(E_INVALIDARG, (requestedLastIndex > lastIndex));
369 // Perform the copy.
370 ULONG curSourceIndex = SourceIndexStart;
371 ULONG curDestIndex = DestinationIndexStart;
372 THROW_IF_FAILED(SafeArrayLock(Source));
373 auto releaseSource = wil::scope_exit([Source]() { WI_VERIFY(SUCCEEDED(SafeArrayUnlock(Source))); });
374
375 for (ULONG index = 0; index < ElementsToCopy; index += 1)
376 {
377 VARIANT* nextAdapter;
378 THROW_IF_FAILED(SafeArrayPtrOfIndex(Source, (PLONG)&curSourceIndex, (PVOID*)&nextAdapter));
379
380 curSourceIndex += 1;
381 THROW_IF_FAILED(SafeArrayPutElement(Destination, (PLONG)&curDestIndex, nextAdapter));
382
383 curDestIndex += 1;
384 }
385
386 return;
387 }
388
389 std::wstring LxssNetworkingFirewall::AddPortRule(const IP_ADDRESS_PREFIX& Address) const
390 {
391 auto newRule = wil::CoCreateInstance<NetFwRule, INetFwRule>(CLSCTX_INPROC_SERVER);
392
393 // Open a port via the firewall by creating a rule that specifies the local
394 // address and the local port to allow. Currently this rule only applies to
395 // the public profile as that is the one invoked with traffic between
396 // network compartments.
397 THROW_IF_FAILED(newRule->put_Action(NET_FW_ACTION_ALLOW));
398 THROW_IF_FAILED(newRule->put_Direction(NET_FW_RULE_DIR_IN));
399 THROW_IF_FAILED(newRule->put_Profiles(NET_FW_PROFILE2_PUBLIC));
400 THROW_IF_FAILED(newRule->put_Protocol(NET_FW_IP_PROTOCOL_TCP));
401 const std::wstring addressString = LxssIpTables::AddressStringFromAddress(Address, false);
402
403 const auto localAddress = wil::make_bstr_failfast(addressString.c_str());
404 THROW_IF_FAILED(newRule->put_LocalAddresses(localAddress.get()));
405 const auto localPort = wil::make_bstr_failfast(std::to_wstring(Address.Prefix.Ipv4.sin_port).c_str());
406
407 THROW_IF_FAILED(newRule->put_LocalPorts(localPort.get()));
408 std::wstring generatedName = GeneratePortRuleName(Address);
409 const auto friendlyName = wil::make_bstr_failfast(generatedName.c_str());
410 THROW_IF_FAILED(newRule->put_Name(friendlyName.get()));
411 THROW_IF_FAILED(newRule->put_Description(s_DefaultRuleDescription.get()));
412 THROW_IF_FAILED(newRule->put_Enabled(VARIANT_TRUE));
413 // Add the rule to the existing set.
414 wil::com_ptr<INetFwRules> rules;
415 THROW_IF_FAILED(m_firewall->get_Rules(&rules));
416 THROW_IF_FAILED(rules->Add(newRule.get()));
417 // Return the unique rule name to the caller.
418 return generatedName;
419 }
420
421 void LxssNetworkingFirewall::CleanupRemnants()
422 {
423 auto firewall = std::make_shared<LxssNetworkingFirewall>();
424 THROW_HR_IF(E_OUTOFMEMORY, !firewall);
425 wil::com_ptr<INetFwRules> rules;
426 THROW_IF_FAILED(firewall->m_firewall->get_Rules(&rules));
427 wil::com_ptr<IUnknown> enumInterface;
428 THROW_IF_FAILED(rules->get__NewEnum(enumInterface.addressof()));
429 auto rulesEnum = enumInterface.query<IEnumVARIANT>();
430 // Find any rules with the unique WSL prefix and destroy them.
431 for (;;)
432 {
433 wil::unique_variant next;
434 ULONG numEntries;
435 THROW_IF_FAILED(rulesEnum->Next(1, &next, &numEntries));
436 if (numEntries == 0)
437 {
438 break;
439 }
440
441 wil::com_ptr<INetFwRule> nextRule;
442 THROW_IF_FAILED(next.pdispVal->QueryInterface(IID_PPV_ARGS(&nextRule)));
443 wil::unique_bstr nextRuleName;
444 THROW_IF_FAILED(nextRule->get_Name(nextRuleName.addressof()));
445 if (wsl::shared::string::StartsWith(nextRuleName.get(), s_FriendlyNamePrefix.c_str(), true))
446 {
447 // The firewall port rule will be destroyed when it goes out of
448 // scope.
449 LxssNetworkingFirewallPort tempPort(firewall, nextRule);
450 }
451 }
452 }
453
454 std::wstring LxssNetworkingFirewall::GeneratePortRuleName(const IP_ADDRESS_PREFIX& Address)
455 {
456 std::wstringstream nameStream;
457 nameStream << s_FriendlyNamePrefix << LxssIpTables::AddressStringFromAddress(Address, false) << L":"
458 << std::to_wstring(Address.Prefix.Ipv4.sin_port);
459
460 return nameStream.str();
461 }
462
463 wil::unique_variant LxssNetworkingFirewall::GetExcludedAdapters(_Out_opt_ ULONG* AdapterCount) const
464 {
465 wil::unique_variant excludedResult;
466 THROW_IF_FAILED(m_firewall->get_ExcludedInterfaces(NET_FW_PROFILE2_PUBLIC, excludedResult.addressof()));
467
468 if (excludedResult.vt == VT_EMPTY)
469 {
470 // If there are no entries create a 0-element array so that callers can
471 // always assume the returned variant contains a safe array.
472 excludedResult.parray = SafeArrayCreateVector(VT_VARIANT, 0, 0);
473 THROW_HR_IF_NULL(E_OUTOFMEMORY, excludedResult.parray);
474 excludedResult.vt = (VT_ARRAY | VT_VARIANT);
475 }
476
477 THROW_HR_IF_MSG(E_UNEXPECTED, (excludedResult.vt != (VT_ARRAY | VT_VARIANT)), "Unexpected type from get_ExcludedInterfaces");
478
479 SAFEARRAY* existingAdapters = excludedResult.parray;
480 THROW_HR_IF_MSG(E_UNEXPECTED, (SafeArrayGetDim(existingAdapters) != 1), "Unexpected array dim from get_ExcludedInterfaces");
481
482 LONG firstIndex;
483 THROW_IF_FAILED(SafeArrayGetLBound(existingAdapters, 1, &firstIndex));
484
485 THROW_HR_IF_MSG(E_UNEXPECTED, (firstIndex != 0), "Unexpected array (l) from get_ExcludedInterfaces");
486
487 LONG lastIndex;
488 THROW_IF_FAILED(SafeArrayGetUBound(existingAdapters, 1, &lastIndex));
489
490 if (ARGUMENT_PRESENT(AdapterCount))
491 {
492 // A zero-element array reports the upper bound as -1 because it
493 // subtracts one from the element count (0) to get the upper bound.
494 *AdapterCount = (static_cast<ULONG>(lastIndex) + 1);
495 }
496
497 return excludedResult;
498 }
499
500 void LxssNetworkingFirewall::ExcludeAdapter(const std::wstring& AdapterName)
501 {
502 ULONG adapterCount;
503 wil::unique_variant currentExcluded = GetExcludedAdapters(&adapterCount);
504 THROW_HR_IF_MSG(E_BOUNDS, (adapterCount >= ULONG_MAX), "Unexpected array (u) from get_ExcludedInterfaces");
505
506 SAFEARRAY* existingAdapters = currentExcluded.parray;
507 unique_safearray adapters(SafeArrayCreateVector(VT_VARIANT, 0, (adapterCount + 1)));
508
509 THROW_HR_IF(E_OUTOFMEMORY, !adapters);
510 // Add existing entries
511 CopyPartialArray(adapters.get(), existingAdapters, 0, 0, adapterCount);
512 // Create new entry matching device name.
513 wil::unique_variant interfaceName;
514 interfaceName.bstrVal = SysAllocString(AdapterName.c_str());
515 THROW_HR_IF_NULL(E_OUTOFMEMORY, interfaceName.bstrVal);
516 THROW_IF_FAILED(SafeArrayPutElement(adapters.get(), (PLONG)&adapterCount, interfaceName.addressof()));
517
518 wil::unique_variant excludedAdapters;
519 excludedAdapters.vt = (VT_ARRAY | VT_VARIANT);
520 excludedAdapters.parray = adapters.release();
521 THROW_IF_FAILED(m_firewall->put_ExcludedInterfaces(NET_FW_PROFILE2_PUBLIC, excludedAdapters));
522 }
523
524 void LxssNetworkingFirewall::RemoveExcludedAdapter(const std::wstring& AdapterName)
525 {
526 ULONG adapterCount;
527 wil::unique_variant currentExcluded = GetExcludedAdapters(&adapterCount);
528 SAFEARRAY* existingAdapters = currentExcluded.parray;
529 ULONG index;
530 for (index = 0; index < adapterCount; index += 1)
531 {
532 wil::unique_variant nextAdapter;
533 THROW_IF_FAILED(SafeArrayGetElement(existingAdapters, (PLONG)&index, nextAdapter.addressof()));
534
535 THROW_HR_IF(E_UNEXPECTED, (nextAdapter.vt != VT_BSTR));
536 // Case-insensitive name comparison
537 if (wsl::shared::string::IsEqual(AdapterName, nextAdapter.bstrVal, true))
538 {
539 break;
540 }
541 }
542
543 THROW_HR_IF(E_INVALIDARG, (index >= adapterCount));
544 unique_safearray adapters(SafeArrayCreateVector(VT_VARIANT, 0, (adapterCount - 1)));
545
546 THROW_HR_IF(E_OUTOFMEMORY, !adapters);
547 // Copy all of the elements except the one being removed.
548 CopyPartialArray(adapters.get(), existingAdapters, 0, 0, index);
549 CopyPartialArray(adapters.get(), existingAdapters, index, (index + 1), (adapterCount - (index + 1)));
550
551 wil::unique_variant excludedAdapters;
552 excludedAdapters.vt = (VT_ARRAY | VT_VARIANT);
553 excludedAdapters.parray = adapters.release();
554 THROW_IF_FAILED(m_firewall->put_ExcludedInterfaces(NET_FW_PROFILE2_PUBLIC, excludedAdapters));
555 }
556
557 void LxssNetworkingFirewall::RemovePortRule(const std::wstring& RuleName) const
558 {
559 wil::com_ptr<INetFwRules> rules;
560 THROW_IF_FAILED(m_firewall->get_Rules(&rules));
561 THROW_IF_FAILED(rules->Remove(wil::make_bstr_failfast(RuleName.c_str()).get()));
562 }
563
564 // LxssNetworkingFirewallPort class functions
565
566 LxssNetworkingFirewallPort::LxssNetworkingFirewallPort(const std::shared_ptr<LxssNetworkingFirewall>& Firewall, const IP_ADDRESS_PREFIX& Address) :
567 m_address(Address), m_firewall(Firewall)
568 {
569 m_name = Firewall->AddPortRule(Address);
570 return;
571 }
572
573 LxssNetworkingFirewallPort::LxssNetworkingFirewallPort(const std::shared_ptr<LxssNetworkingFirewall>& Firewall, const wil::com_ptr<INetFwRule>& Existing) :
574 m_firewall(Firewall)
575 {
576 wil::unique_bstr ruleName;
577 THROW_IF_FAILED(Existing->get_Name(ruleName.addressof()));
578 m_name = ruleName.get();
579 return;
580 }
581
582 LxssNetworkingFirewallPort::~LxssNetworkingFirewallPort()
583 {
584 try
585 {
586 m_firewall->RemovePortRule(m_name);
587 }
588 CATCH_LOG_MSG("Failed to remove firewall port rule.")
589 }
590
591 // LxssNetworkingNat class functions
592
593 // N.B. The name is internally limited by NAT to 39 characters.
594 const std::wstring LxssNetworkingNat::s_FriendlyNamePrefix(L"WSLNAT_17774471984f_");
595
596 const std::wstring LxssNetworkingNat::s_WmiNatInstanceId(L"InstanceID");
597 const std::wstring LxssNetworkingNat::s_WmiNatInternalIpAddress(L"InternalIPInterfaceAddressPrefix");
598
599 const std::wstring LxssNetworkingNat::s_WmiNatName(L"Name");
600 const std::wstring LxssNetworkingNat::s_WmiNatNamespace(L"MSFT_NetNat");
601
602 LxssNetworkingNat::LxssNetworkingNat(const IP_ADDRESS_PREFIX& InputPrefix) : m_internalIpAddress(InputPrefix)
603 {
604 // Convert the address into a string of the form "172.17.0.0/16"
605 const std::wstring inputAddress = LxssIpTables::AddressStringFromAddress(InputPrefix, true);
606
607 const std::wstring friendlyName = s_FriendlyNamePrefix + inputAddress;
608 m_session = LxssManagementInterface::NewSession();
609 const auto instance = GetNatWmiInstance(m_session);
610 MI_Value miValue;
611 miValue.string = const_cast<MI_Char*>(friendlyName.c_str());
612 MI_Result result = MI_Instance_SetElement(instance.get(), s_WmiNatName.c_str(), &miValue, MI_STRING, MI_FLAG_BORROW);
613
614 THROW_HR_IF_MSG(E_FAIL, (result != MI_RESULT_OK), "Failed with error %d", result);
615
616 miValue.string = const_cast<MI_Char*>(inputAddress.c_str());
617 result = MI_Instance_SetElement(instance.get(), s_WmiNatInternalIpAddress.c_str(), &miValue, MI_STRING, MI_FLAG_BORROW);
618
619 // TODO_LX: Might need a timeout value here, set via
620 // MI_OperationOptions_SetTimeout()
621 unique_mi_operation operation;
622 MI_Session_CreateInstance(
623 m_session.get(), 0, nullptr, LxssManagementInterface::LocalRoot().c_str(), instance.get(), nullptr, operation.addressof());
624
625 MI_Boolean moreResults;
626 const MI_Instance* resultInstance;
627 MI_Result innerResult = MI_RESULT_OK;
628 result = MI_Operation_GetInstance(operation.addressof(), &resultInstance, &moreResults, &innerResult, nullptr, nullptr);
629
630 WI_ASSERT(moreResults != MI_TRUE);
631
632 THROW_HR_IF_MSG(E_FAIL, (result != MI_RESULT_OK), "Failed with error %d", result);
633
634 THROW_HR_IF_MSG(E_FAIL, (innerResult != MI_RESULT_OK), "Operation failed with error %d", innerResult);
635
636 m_natInstance = LxssManagementInterface::CloneInstance(resultInstance);
637 return;
638 }
639
640 LxssNetworkingNat::LxssNetworkingNat(const MI_Instance* ExistingInstance)
641 {
642 m_session = LxssManagementInterface::NewSession();
643 m_natInstance = LxssManagementInterface::CloneInstance(ExistingInstance);
644 return;
645 }
646
647 LxssNetworkingNat::~LxssNetworkingNat()
648 {
649 unique_mi_operation operation;
650 MI_Session_DeleteInstance(
651 m_session.get(), 0, nullptr, LxssManagementInterface::LocalRoot().c_str(), m_natInstance.get(), nullptr, operation.addressof());
652
653 MI_Boolean moreResults = MI_TRUE;
654 while (moreResults != MI_FALSE)
655 {
656 const MI_Instance* resultInstance;
657 MI_Result innerResult;
658 const MI_Result result =
659 MI_Operation_GetInstance(operation.addressof(), &resultInstance, &moreResults, &innerResult, nullptr, nullptr);
660
661 if (result != MI_RESULT_OK)
662 {
663 LOG_HR_MSG(E_FAIL, "Failed with error %d", result);
664
665 break;
666 }
667
668 LOG_HR_IF_MSG(E_FAIL, (innerResult != MI_RESULT_OK), "Failed with error %d", innerResult);
669 }
670 }
671
672 void LxssNetworkingNat::CleanupRemnants()
673 {
674 // Search through all NATs in the system looking for those that match the
675 // WSL naming convention: WSL_<IP address>
676 const auto session = LxssManagementInterface::NewSession();
677 unique_mi_operation operation;
678 MI_Session_EnumerateInstances(
679 session.get(),
680 0,
681 nullptr,
682 LxssManagementInterface::LocalRoot().c_str(),
683 s_WmiNatNamespace.c_str(),
684 false,
685 nullptr,
686 operation.addressof());
687
688 MI_Boolean moreResults = MI_TRUE;
689 while (moreResults != MI_FALSE)
690 {
691 const MI_Instance* resultInstance{};
692 MI_Result innerResult{};
693 MI_Result result = MI_Operation_GetInstance(operation.addressof(), &resultInstance, &moreResults, &innerResult, nullptr, nullptr);
694 if (result != MI_RESULT_OK)
695 {
696 LOG_HR_MSG(E_FAIL, "Failed with error %d", result);
697 break;
698 }
699
700 if (innerResult != MI_RESULT_OK)
701 {
702 LOG_HR_MSG(E_FAIL, "Failed with error %d", innerResult);
703 continue;
704 }
705
706 if (resultInstance == nullptr)
707 {
708 // From: https://learn.microsoft.com/en-us/windows/win32/api/mi/nf-mi-mi_operation_getinstance
709 // This value may be Null even if the operation succeeds.
710 continue;
711 }
712
713 MI_Value miValue{};
714 MI_Type miType{};
715 result = MI_Instance_GetElement(resultInstance, s_WmiNatName.c_str(), &miValue, &miType, nullptr, nullptr);
716
717 if (result != MI_RESULT_OK)
718 {
719 LOG_HR_MSG(E_FAIL, "Failed with error %d", result);
720 continue;
721 }
722
723 if (miType != MI_STRING)
724 {
725 LOG_HR_MSG(E_UNEXPECTED, "Type is %d", miType);
726 continue;
727 }
728
729 if (wsl::shared::string::StartsWith(miValue.string, s_FriendlyNamePrefix, true))
730 {
731 // Create a temporary NAT instance, to be immediately deleted when
732 // it goes out of scope.
733 LxssNetworkingNat natRemnant(resultInstance);
734 }
735 }
736 }
737
738 unique_mi_instance LxssNetworkingNat::GetNatWmiInstance(const unique_mi_session& Session)
739 {
740 unique_mi_operation operation;
741 MI_Session_GetClass(
742 Session.get(), 0, nullptr, LxssManagementInterface::LocalRoot().c_str(), s_WmiNatNamespace.c_str(), nullptr, operation.addressof());
743
744 const MI_Class* miClass;
745 const MI_Result result = MI_Operation_GetClass(operation.addressof(), &miClass, nullptr, nullptr, nullptr, nullptr);
746
747 THROW_HR_IF_MSG(E_FAIL, (result != MI_RESULT_OK), "Failed with error %d", result);
748
749 return LxssManagementInterface::NewInstance(s_WmiNatNamespace.c_str(), miClass);
750 }