@@ -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
2424using 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
527591void 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+
13341465void WSLAVirtualMachine::CollectCrashDumps (wil::unique_socket&& listenSocket) const
13351466{
13361467 wsl::windows::common::wslutil::SetThreadDescription (L" CrashDumpCollection" );
0 commit comments