Skip to content

Commit 6549d46

Browse files
author
Ben Hillis
committed
WSLA: Implement viritoproxy networking mode
This change implements the VirtioProxy networking mode for WSLA. This required moving the VirtioProxy and GnsChannel classes into the common library (with NatNetworking). DNS tunneling is currently not supported with this networking mode, so an error is returned. With this change I have updated the default networking move of 'wsl.exe --wsla' to also use VirtioProxy networking mode.
1 parent 93977c0 commit 6549d46

File tree

11 files changed

+176
-8
lines changed

11 files changed

+176
-8
lines changed

src/windows/common/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ set(SOURCES
1212
ExecutionContext.cpp
1313
filesystem.cpp
1414
GnsChannel.cpp
15+
GnsPortTrackerChannel.cpp
1516
HandleConsoleProgressBar.cpp
1617
hcs.cpp
1718
helpers.cpp
@@ -34,6 +35,7 @@ set(SOURCES
3435
SubProcess.cpp
3536
svccomm.cpp
3637
svccommio.cpp
38+
VirtioNetworking.cpp
3739
WSLAProcessLauncher.cpp
3840
WslClient.cpp
3941
WslCoreConfig.cpp
@@ -82,6 +84,7 @@ set(HEADERS
8284
ExecutionContext.h
8385
filesystem.hpp
8486
GnsChannel.h
87+
GnsPortTrackerChannel.h
8588
HandleConsoleProgressBar.h
8689
hcs.hpp
8790
hcs_schema.h
@@ -106,6 +109,7 @@ set(HEADERS
106109
SubProcess.h
107110
svccomm.hpp
108111
svccommio.hpp
112+
VirtioNetworking.h
109113
WSLAProcessLauncher.h
110114
WslClient.h
111115
WslCoreConfig.h
File renamed without changes.

src/windows/common/WslClient.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1547,7 +1547,7 @@ int WslaShell(_In_ std::wstring_view commandLine)
15471547
settings.DisplayName = L"WSLA";
15481548
settings.MemoryMb = 1024;
15491549
settings.BootTimeoutMs = 30000;
1550-
settings.NetworkingMode = WSLANetworkingModeNAT;
1550+
settings.NetworkingMode = WSLANetworkingModeVirtioProxy;
15511551
std::wstring containerRootVhd;
15521552
bool help = false;
15531553

src/windows/service/exe/CMakeLists.txt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ set(SOURCES
1010
PluginManager.cpp
1111
ServiceMain.cpp
1212
BridgedNetworking.cpp
13-
GnsPortTrackerChannel.cpp
1413
GnsRpcServer.cpp
1514
GuestTelemetryLogger.cpp
1615
Lifetime.cpp
@@ -22,7 +21,6 @@ set(SOURCES
2221
WslMirroredNetworking.cpp
2322
WslCoreTcpIpStateTracking.cpp
2423
WslCoreVm.cpp
25-
VirtioNetworking.cpp
2624
main.rc
2725
${CMAKE_CURRENT_BINARY_DIR}/../mc/${TARGET_PLATFORM}/${CMAKE_BUILD_TYPE}/wsleventschema.rc
2826
application.manifest)
@@ -38,7 +36,6 @@ set(HEADERS
3836
PluginManager.h
3937
LxssInstance.h
4038
BridgedNetworking.h
41-
GnsPortTrackerChannel.h
4239
GnsRpcServer.h
4340
GuestTelemetryLogger.h
4441
IMirroredNetworkManager.h

src/windows/wslaservice/exe/WSLAVirtualMachine.cpp

Lines changed: 134 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ Module Name:
1616
#include <format>
1717
#include <filesystem>
1818
#include "hcs_schema.h"
19+
#include "VirtioNetworking.h"
1920
#include "NatNetworking.h"
2021
#include "WSLAUserSession.h"
21-
#include "DnsResolver.h"
2222
#include "ServiceProcessLauncher.h"
2323

2424
using namespace wsl::windows::common;
@@ -94,6 +94,16 @@ WSLAVirtualMachine::~WSLAVirtualMachine()
9494

9595
WSL_LOG("WSLATerminateVm", TraceLoggingValue(forceTerminate, "forced"), TraceLoggingValue(m_running, "running"));
9696

97+
// Shutdown DeviceHostProxy before resetting compute system
98+
if (m_deviceHostSupport)
99+
{
100+
try
101+
{
102+
m_deviceHostSupport->Shutdown();
103+
}
104+
CATCH_LOG()
105+
}
106+
97107
m_computeSystem.reset();
98108

99109
for (const auto& e : m_attachedDisks)
@@ -308,6 +318,13 @@ void WSLAVirtualMachine::Start()
308318
auto runtimeId = wsl::windows::common::hcs::GetRuntimeId(m_computeSystem.get());
309319
WI_ASSERT(IsEqualGUID(m_vmId, runtimeId));
310320

321+
// Initialize DeviceHostProxy for virtio device support.
322+
// N.B. This is currently only needed for VirtioProxy networking mode but would also be needed for virtiofs.
323+
if (m_settings.NetworkingMode == WSLANetworkingModeVirtioProxy)
324+
{
325+
m_deviceHostSupport = wil::MakeOrThrow<DeviceHostProxy>(m_vmIdString, m_vmId);
326+
}
327+
311328
wsl::windows::common::hcs::RegisterCallback(m_computeSystem.get(), &s_OnExit, this);
312329

313330
wsl::windows::common::hcs::StartComputeSystem(m_computeSystem.get(), json.c_str());
@@ -513,15 +530,62 @@ void WSLAVirtualMachine::ConfigureNetworking()
513530
wil::unique_socket{(SOCKET)process->GetStdHandle(gnsChannelFd).release()},
514531
config,
515532
dnsChannelFd != -1 ? wil::unique_socket{(SOCKET)process->GetStdHandle(dnsChannelFd).release()} : wil::unique_socket{});
533+
}
534+
else if (m_settings.NetworkingMode == WSLANetworkingModeVirtioProxy)
535+
{
536+
// Launch GNS
537+
std::vector<WSLA_PROCESS_FD> fds(1);
538+
fds[0].Fd = -1;
539+
fds[0].Type = WSLAFdType::WSLAFdTypeDefault;
540+
541+
std::vector<const char*> cmd{"/gns", LX_INIT_GNS_SOCKET_ARG};
516542

517-
m_networkEngine->Initialize();
543+
// If DNS tunnelling is enabled, use an additional for its channel.
544+
THROW_HR_IF_MSG(E_NOTIMPL, m_settings.EnableDnsTunneling, "DNS tunneling not currently supported for VirtioProxy");
545+
546+
WSLA_PROCESS_OPTIONS options{};
547+
options.Executable = "/init";
548+
options.Fds = fds.data();
549+
options.FdsCount = static_cast<DWORD>(fds.size());
550+
551+
// Because the file descriptors numbers aren't known in advance, the command line needs to be generated after the file
552+
// descriptors are allocated.
553+
554+
std::string socketFdArg;
555+
int gnsChannelFd = -1;
556+
auto prepareCommandLine = [&](const auto& sockets) {
557+
gnsChannelFd = sockets[0].Fd;
558+
socketFdArg = std::to_string(gnsChannelFd);
559+
cmd.emplace_back(socketFdArg.c_str());
560+
options.CommandLine = cmd.data();
561+
options.CommandLineCount = static_cast<DWORD>(cmd.size());
562+
};
518563

519-
LaunchPortRelay();
564+
auto process = CreateLinuxProcess(options, nullptr, prepareCommandLine);
565+
566+
// Create GnsChannel for virtio networking
567+
auto gnsChannel = wsl::core::GnsChannel(wil::unique_socket{(SOCKET)process->GetStdHandle(gnsChannelFd).release()});
568+
569+
// Use VirtioNetworking
570+
m_networkEngine = std::make_unique<wsl::core::VirtioNetworking>(
571+
std::move(gnsChannel),
572+
true, // enableLocalhostRelay
573+
[this](const GUID& Clsid, const GUID& DeviceId, PCWSTR Tag, PCWSTR Options) {
574+
return HandleVirtioAddGuestDevice(Clsid, DeviceId, Tag, Options);
575+
},
576+
[this](const GUID& Clsid, PCWSTR Tag, const SOCKADDR_INET& Addr, int Protocol, bool IsOpen) {
577+
return HandleVirtioModifyOpenPorts(Clsid, Tag, Addr, Protocol, IsOpen);
578+
},
579+
[](const std::string&, bool) {});
520580
}
521581
else
522582
{
523583
THROW_HR_MSG(E_INVALIDARG, "Invalid networking mode: %lu", m_settings.NetworkingMode);
524584
}
585+
586+
m_networkEngine->Initialize();
587+
588+
LaunchPortRelay();
525589
}
526590

527591
void CALLBACK WSLAVirtualMachine::s_OnExit(_In_ HCS_EVENT* Event, _In_opt_ void* Context)
@@ -1331,6 +1395,73 @@ void WSLAVirtualMachine::OnProcessReleased(int Pid)
13311395
auto erased = std::erase_if(m_trackedProcesses, [Pid](const auto* e) { return e->GetPid() == Pid; });
13321396
}
13331397

1398+
GUID WSLAVirtualMachine::HandleVirtioAddGuestDevice(_In_ const GUID& Clsid, _In_ const GUID& DeviceId, _In_ PCWSTR Tag, _In_ PCWSTR Options)
1399+
{
1400+
auto guestDeviceLock = m_guestDeviceLock.lock_exclusive();
1401+
1402+
// Options are appended to the name with a semi-colon separator.
1403+
std::wstring nameWithOptions{Tag};
1404+
if (Options && Options[0] != L'\0')
1405+
{
1406+
nameWithOptions += L";";
1407+
nameWithOptions += Options;
1408+
}
1409+
1410+
auto server = m_deviceHostSupport->GetRemoteFileSystem(Clsid, L"");
1411+
if (!server)
1412+
{
1413+
// Create the COM server for the virtio device
1414+
auto revert = wil::impersonate_token(m_userToken.get());
1415+
server = wil::CoCreateInstance<IPlan9FileSystem>(Clsid, (CLSCTX_LOCAL_SERVER | CLSCTX_ENABLE_CLOAKING | CLSCTX_ENABLE_AAA));
1416+
m_deviceHostSupport->AddRemoteFileSystem(Clsid, L"", server);
1417+
}
1418+
1419+
// AddSharePath with empty path for virtio-net devices
1420+
THROW_IF_FAILED(server->AddSharePath(nameWithOptions.c_str(), L"", 0));
1421+
1422+
// Add the device to the VM
1423+
return m_deviceHostSupport->AddNewDevice(DeviceId, server, Tag);
1424+
}
1425+
int WSLAVirtualMachine::HandleVirtioModifyOpenPorts(_In_ const GUID& Clsid, _In_ PCWSTR Tag, _In_ const SOCKADDR_INET& Addr, _In_ int Protocol, _In_ bool IsOpen)
1426+
{
1427+
if (Protocol != IPPROTO_TCP && Protocol != IPPROTO_UDP)
1428+
{
1429+
LOG_HR_MSG(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), "Unsupported bind protocol %d", Protocol);
1430+
return 0;
1431+
}
1432+
else if (Addr.si_family == AF_INET6)
1433+
{
1434+
// The virtio net adapter does not yet support IPv6 packets
1435+
return 0;
1436+
}
1437+
1438+
auto guestDeviceLock = m_guestDeviceLock.lock_exclusive();
1439+
const auto server = m_deviceHostSupport->GetRemoteFileSystem(Clsid, L"");
1440+
if (server)
1441+
{
1442+
std::wstring portString = std::format(L"tag={};port_number={}", Tag, Addr.Ipv4.sin_port);
1443+
if (Protocol == IPPROTO_UDP)
1444+
{
1445+
portString += L";udp";
1446+
}
1447+
1448+
if (!IsOpen)
1449+
{
1450+
portString += L";allocate=false";
1451+
}
1452+
else
1453+
{
1454+
wchar_t addrStr[16]; // "000.000.000.000" + null terminator
1455+
RtlIpv4AddressToStringW(&Addr.Ipv4.sin_addr, addrStr);
1456+
portString += std::format(L";listen_addr={}", addrStr);
1457+
}
1458+
1459+
LOG_IF_FAILED(server->AddShare(portString.c_str(), nullptr, 0));
1460+
}
1461+
1462+
return 0;
1463+
}
1464+
13341465
void WSLAVirtualMachine::CollectCrashDumps(wil::unique_socket&& listenSocket) const
13351466
{
13361467
wsl::windows::common::wslutil::SetThreadDescription(L"CrashDumpCollection");

src/windows/wslaservice/exe/WSLAVirtualMachine.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ Module Name:
1818
#include "Dmesg.h"
1919
#include "WSLAApi.h"
2020
#include "WSLAProcess.h"
21+
#include "DeviceHostProxy.h"
22+
#include "DnsResolver.h"
2123

2224
namespace wsl::windows::service::wsla {
2325

@@ -70,6 +72,10 @@ class DECLSPEC_UUID("0CFC5DC1-B6A7-45FC-8034-3FA9ED73CE30") WSLAVirtualMachine
7072
std::pair<ULONG, std::string> AttachDisk(_In_ PCWSTR Path, _In_ BOOL ReadOnly);
7173
void DetachDisk(_In_ ULONG Lun);
7274

75+
// VirtioNetworking support
76+
GUID HandleVirtioAddGuestDevice(_In_ const GUID& Clsid, _In_ const GUID& DeviceId, _In_ PCWSTR Tag, _In_ PCWSTR Options);
77+
int HandleVirtioModifyOpenPorts(_In_ const GUID& Clsid, _In_ PCWSTR Tag, _In_ const SOCKADDR_INET& Addr, _In_ int Protocol, _In_ bool IsOpen);
78+
7379
private:
7480
static void Mount(wsl::shared::SocketChannel& Channel, LPCSTR Source, _In_ LPCSTR Target, _In_ LPCSTR Type, _In_ LPCSTR Options, _In_ ULONG Flags);
7581
static void CALLBACK s_OnExit(_In_ HCS_EVENT* Event, _In_opt_ void* Context);
@@ -146,5 +152,9 @@ class DECLSPEC_UUID("0CFC5DC1-B6A7-45FC-8034-3FA9ED73CE30") WSLAVirtualMachine
146152
std::map<std::string, std::wstring> m_plan9Mounts;
147153
std::recursive_mutex m_lock;
148154
std::mutex m_portRelaylock;
155+
156+
// VirtioNetworking support
157+
wil::srwlock m_guestDeviceLock;
158+
Microsoft::WRL::ComPtr<DeviceHostProxy> m_deviceHostSupport;
149159
};
150160
} // namespace wsl::windows::service::wsla

src/windows/wslaservice/inc/wslaservice.idl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,8 @@ interface IWSLAVirtualMachine : IUnknown
207207
typedef enum _WSLANetworkingMode
208208
{
209209
WSLANetworkingModeNone,
210-
WSLANetworkingModeNAT
210+
WSLANetworkingModeNAT,
211+
WSLANetworkingModeVirtioProxy
211212
} WSLANetworkingMode;
212213

213214
typedef

0 commit comments

Comments
 (0)