-
Notifications
You must be signed in to change notification settings - Fork 1.7k
do-not-merge: virtionet port mapping POC #40145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feature/wsl-for-apps
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -83,7 +83,9 @@ void VirtioNetworking::StartPortTracker(wil::unique_socket&& socket) | |||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| m_gnsPortTrackerChannel.emplace( | ||||||||||||||||||||||||||||
| std::move(socket), | ||||||||||||||||||||||||||||
| [&](const SOCKADDR_INET& addr, int protocol, bool allocate) { return HandlePortNotification(addr, protocol, allocate); }, | ||||||||||||||||||||||||||||
| [&](const SOCKADDR_INET& addr, int protocol, bool allocate) { | ||||||||||||||||||||||||||||
| return HandlePortNotification(addr, protocol, INETADDR_PORT(reinterpret_cast<const SOCKADDR*>(&addr)), allocate); | ||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||
| [](const std::string&, bool) {}); // TODO: reconsider if InterfaceStateCallback is needed. | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
|
@@ -94,14 +96,13 @@ try | |||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| CATCH_LOG() | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| HRESULT VirtioNetworking::HandlePortNotification(const SOCKADDR_INET& addr, int protocol, bool allocate) const noexcept | ||||||||||||||||||||||||||||
| HRESULT VirtioNetworking::HandlePortNotification(const SOCKADDR_INET& addr, int protocol, uint16_t guestPort, bool allocate) const noexcept | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| if (addr.si_family == AF_INET6 && WI_IsFlagClear(m_flags, VirtioNetworkingFlags::Ipv6)) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| return S_OK; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| int result = 0; | ||||||||||||||||||||||||||||
| const auto ipAddress = (addr.si_family == AF_INET) ? reinterpret_cast<const void*>(&addr.Ipv4.sin_addr) | ||||||||||||||||||||||||||||
| : reinterpret_cast<const void*>(&addr.Ipv6.sin6_addr); | ||||||||||||||||||||||||||||
| const bool loopback = INET_IS_ADDR_LOOPBACK(addr.si_family, ipAddress); | ||||||||||||||||||||||||||||
|
|
@@ -111,10 +112,12 @@ HRESULT VirtioNetworking::HandlePortNotification(const SOCKADDR_INET& addr, int | |||||||||||||||||||||||||||
| // Only intercepting 127.0.0.1; any other loopback address will remain on 'lo'. | ||||||||||||||||||||||||||||
| if (addr.Ipv4.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| return result; | ||||||||||||||||||||||||||||
| return S_OK; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| auto hostPort = INETADDR_PORT(reinterpret_cast<const SOCKADDR*>(&addr)); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (WI_IsFlagSet(m_flags, VirtioNetworkingFlags::LocalhostRelay) && (unspecified || loopback)) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| SOCKADDR_INET localAddr = addr; | ||||||||||||||||||||||||||||
|
|
@@ -130,57 +133,99 @@ HRESULT VirtioNetworking::HandlePortNotification(const SOCKADDR_INET& addr, int | |||||||||||||||||||||||||||
| localAddr.Ipv6.sin6_port = addr.Ipv6.sin6_port; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| result = ModifyOpenPorts(c_loopbackDeviceName, localAddr, protocol, allocate); | ||||||||||||||||||||||||||||
| LOG_HR_IF_MSG( | ||||||||||||||||||||||||||||
| E_FAIL, result != S_OK, "Failure adding localhost relay port %d", INETADDR_PORT(reinterpret_cast<const SOCKADDR*>(&localAddr))); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| try | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| hostPort = ModifyOpenPorts(c_loopbackDeviceName, localAddr, hostPort, guestPort, protocol, allocate); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| catch (...) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| LOG_CAUGHT_EXCEPTION_MSG("Failure adding localhost relay port %d", guestPort); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (!loopback) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| const int localResult = ModifyOpenPorts(c_eth0DeviceName, addr, protocol, allocate); | ||||||||||||||||||||||||||||
| LOG_HR_IF_MSG(E_FAIL, localResult != S_OK, "Failure adding relay port %d", INETADDR_PORT(reinterpret_cast<const SOCKADDR*>(&addr))); | ||||||||||||||||||||||||||||
| if (result == 0) | ||||||||||||||||||||||||||||
| try | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| hostPort = ModifyOpenPorts(c_eth0DeviceName, addr, hostPort, guestPort, protocol, allocate); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| catch (...) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| result = localResult; | ||||||||||||||||||||||||||||
| LOG_CAUGHT_EXCEPTION_MSG("Failure adding relay port %d", guestPort); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return result; | ||||||||||||||||||||||||||||
| return S_OK; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+137
to
160
|
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| int VirtioNetworking::ModifyOpenPorts(_In_ PCWSTR tag, _In_ const SOCKADDR_INET& addr, _In_ int protocol, _In_ bool isOpen) const | ||||||||||||||||||||||||||||
| uint16_t VirtioNetworking::ModifyOpenPorts( | ||||||||||||||||||||||||||||
| _In_ PCWSTR tag, _In_ const SOCKADDR_INET& hostAddress, _In_ uint16_t HostPort, _In_ uint16_t GuestPort, _In_ int protocol, _In_ bool isOpen) const | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| LOG_HR_MSG(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), "Unsupported bind protocol %d", protocol); | ||||||||||||||||||||||||||||
| return 0; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| THROW_HR_IF_MSG( | ||||||||||||||||||||||||||||
| HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), | ||||||||||||||||||||||||||||
| protocol != IPPROTO_TCP && protocol != IPPROTO_UDP, | ||||||||||||||||||||||||||||
| "Unsupported bind protocol %d", | ||||||||||||||||||||||||||||
| protocol); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| auto lock = m_lock.lock_exclusive(); | ||||||||||||||||||||||||||||
| const auto server = m_guestDeviceManager->GetRemoteFileSystem(VIRTIO_NET_CLASS_ID, c_defaultDeviceTag); | ||||||||||||||||||||||||||||
| if (server) | ||||||||||||||||||||||||||||
| THROW_HR_IF(E_NOT_SET, !server); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const auto hostAddressStr = wsl::windows::common::string::SockAddrInetToString(hostAddress); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // format: tag={tag}[;host_port={port}];guest_port={port}[;listen_addr={addr}|;allocate=false][;udp] | ||||||||||||||||||||||||||||
| std::wstring portString = std::format(L"tag={};guest_port={};listen_addr={}", tag, GuestPort, hostAddressStr.c_str()); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
Comment on lines
+177
to
+179
|
||||||||||||||||||||||||||||
| if (HostPort != WSLC_EPHEMERAL_PORT) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| std::wstring portString = std::format(L"tag={};port_number={}", tag, INETADDR_PORT(reinterpret_cast<const SOCKADDR*>(&addr))); | ||||||||||||||||||||||||||||
| if (protocol == IPPROTO_UDP) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| portString += L";udp"; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| portString += std::format(L";host_port={}", HostPort); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (!isOpen) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| portString += L";allocate=false"; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| const auto addrStr = wsl::windows::common::string::SockAddrInetToWstring(addr); | ||||||||||||||||||||||||||||
| portString += std::format(L";listen_addr={}", addrStr); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| if (!isOpen) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| portString += L";allocate=false"; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+177
to
+188
|
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (protocol == IPPROTO_UDP) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| portString += L";udp"; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const HRESULT addShareResult = server->AddShare(portString.c_str(), nullptr, 0); | ||||||||||||||||||||||||||||
| WSL_LOG("MapVirtioPort", TraceLoggingValue(portString.c_str(), "PortString"), TraceLoggingValue(addShareResult, "Result")); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| LOG_IF_FAILED(server->AddShare(portString.c_str(), nullptr, 0)); | ||||||||||||||||||||||||||||
| if (HostPort == WSLC_EPHEMERAL_PORT && isOpen && SUCCEEDED(addShareResult)) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
|
Comment on lines
+198
to
+199
|
||||||||||||||||||||||||||||
| if (HostPort == WSLC_EPHEMERAL_PORT && isOpen && SUCCEEDED(addShareResult)) | |
| { | |
| if (HostPort == WSLC_EPHEMERAL_PORT && isOpen) | |
| { | |
| THROW_IF_FAILED_MSG(addShareResult, "Failed to set virtionet port mapping: %ls", portString.c_str()); | |
| constexpr HRESULT c_maxEncodedEphemeralPortResult = S_OK + UINT16_MAX; | |
| THROW_HR_IF_MSG( | |
| E_UNEXPECTED, | |
| addShareResult < S_OK || addShareResult > c_maxEncodedEphemeralPortResult, | |
| "Unexpected AddShare success code for ephemeral port mapping: 0x%08x (%ls)", | |
| static_cast<unsigned int>(addShareResult), | |
| portString.c_str()); |
Copilot
AI
Apr 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MapPort never populates *AllocatedHostPort for anonymous binds. ModifyOpenPorts can compute an allocated host port (return value), but MapPort currently just calls HandlePortNotification and leaves *AllocatedHostPort as 0, so callers (e.g. WSLCVirtualMachine::MapPort) can’t discover the chosen host port. Plumb the allocated host port back to AllocatedHostPort (and ensure the COM method returns it).
| return HandlePortNotification(ListenAddress, Protocol, GuestPort, true); | |
| const auto hostPort = INETADDR_PORT(reinterpret_cast<const SOCKADDR*>(&ListenAddress)); | |
| *AllocatedHostPort = ModifyOpenPorts(c_defaultDeviceTag, ListenAddress, hostPort, GuestPort, Protocol, true); | |
| return S_OK; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ Module Name: | |
| #include "wslutil.h" | ||
| #include "lxinitshared.h" | ||
| #include "DnsResolver.h" | ||
| #include "string.hpp" | ||
|
|
||
| using namespace wsl::windows::common; | ||
| using helpers::WindowsBuildNumbers; | ||
|
|
@@ -416,7 +417,8 @@ try | |
| } | ||
| else if (m_networkingMode == WSLCNetworkingModeVirtioProxy) | ||
| { | ||
| wsl::core::VirtioNetworkingFlags flags = wsl::core::VirtioNetworkingFlags::Ipv6; | ||
| wsl::core::VirtioNetworkingFlags flags = wsl::core::VirtioNetworkingFlags::Ipv6 | wsl::core::VirtioNetworkingFlags::LocalhostRelay | | ||
| wsl::core::VirtioNetworkingFlags::DisableLoopbackMirroring; | ||
| if (FeatureEnabled(WslcFeatureFlagsDnsTunneling)) | ||
| { | ||
| WI_SetFlag(flags, wsl::core::VirtioNetworkingFlags::DnsTunnelingSocket); | ||
|
|
@@ -581,6 +583,47 @@ try | |
| } | ||
| CATCH_RETURN() | ||
|
|
||
| HRESULT HcsVirtualMachine::MapVirtioNetPort(_In_ USHORT HostPort, _In_ USHORT GuestPort, _In_ int Protocol, _In_ LPCSTR ListenAddress, _Out_ USHORT* AllocatedHostPort) | ||
| try | ||
| { | ||
| RETURN_HR_IF(E_POINTER, AllocatedHostPort == nullptr || ListenAddress == nullptr); | ||
|
|
||
| *AllocatedHostPort = 0; | ||
|
|
||
| auto listenAddr = wsl::windows::common::string::StringToSockAddrInet( | ||
| wsl::shared::string::MultiByteToWide(ListenAddress)); | ||
|
|
||
| listenAddr.Ipv4.sin_port = HostPort; | ||
|
|
||
|
Comment on lines
+593
to
+597
|
||
| std::lock_guard lock(m_lock); | ||
|
|
||
| auto* virtioNet = dynamic_cast<wsl::core::VirtioNetworking*>(m_networkEngine.get()); | ||
| RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), virtioNet == nullptr); | ||
|
|
||
| return virtioNet->MapPort(listenAddr, GuestPort, Protocol, AllocatedHostPort); | ||
| } | ||
| CATCH_RETURN() | ||
|
|
||
| HRESULT HcsVirtualMachine::UnmapVirtioNetPort(_In_ USHORT HostPort, _In_ USHORT GuestPort, _In_ int Protocol, _In_ LPCSTR ListenAddress) | ||
| try | ||
| { | ||
| RETURN_HR_IF(E_POINTER, ListenAddress == nullptr); | ||
|
|
||
| auto listenAddr = wsl::windows::common::string::StringToSockAddrInet( | ||
| wsl::shared::string::MultiByteToWide(ListenAddress)); | ||
|
|
||
| listenAddr.Ipv4.sin_port = HostPort; | ||
|
|
||
|
Comment on lines
+612
to
+616
|
||
|
|
||
| std::lock_guard lock(m_lock); | ||
|
|
||
| auto* virtioNet = dynamic_cast<wsl::core::VirtioNetworking*>(m_networkEngine.get()); | ||
| RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), virtioNet == nullptr); | ||
|
|
||
| return virtioNet->UnmapPort(listenAddr, GuestPort, Protocol); | ||
| } | ||
| CATCH_RETURN() | ||
|
|
||
| void CALLBACK HcsVirtualMachine::OnVmExitCallback(HCS_EVENT* Event, void* Context) | ||
| try | ||
| { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,6 +43,10 @@ class HcsVirtualMachine | |
| IFACEMETHOD(DetachDisk)(_In_ ULONG Lun) override; | ||
| IFACEMETHOD(AddShare)(_In_ LPCWSTR WindowsPath, _In_ BOOL ReadOnly, _Out_ GUID* ShareId) override; | ||
| IFACEMETHOD(RemoveShare)(_In_ REFGUID ShareId) override; | ||
| IFACEMETHOD(MapVirtioNetPort) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think for naming just MapPort and UnmapPort would be better. |
||
| (_In_ USHORT HostPort, _In_ USHORT GuestPort, _In_ int Protocol, _In_ LPCSTR ListenAddress, _Out_ USHORT* AllocatedHostPort) override; | ||
| IFACEMETHOD(UnmapVirtioNetPort) | ||
| (_In_ USHORT HostPort, _In_ USHORT GuestPort, _In_ int Protocol, _In_ LPCSTR ListenAddress) override; | ||
|
|
||
| private: | ||
| struct DiskInfo | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -133,7 +133,6 @@ void WSLCSessionManagerImpl::CreateSession(const WSLCSessionSettings* Settings, | |||||||
| } | ||||||||
| else | ||||||||
| { | ||||||||
| THROW_HR_IF(WSLC_E_INVALID_SESSION_NAME, Settings->DisplayName == nullptr || wcslen(Settings->DisplayName) == 0); | ||||||||
| THROW_HR_IF(E_INVALIDARG, Settings->StoragePath != nullptr && wcslen(Settings->StoragePath) == 0); | ||||||||
|
||||||||
| THROW_HR_IF(E_INVALIDARG, Settings->StoragePath != nullptr && wcslen(Settings->StoragePath) == 0); | |
| THROW_HR_IF(E_INVALIDARG, Settings->StoragePath != nullptr && wcslen(Settings->StoragePath) == 0); | |
| THROW_HR_IF(E_INVALIDARG, Settings->DisplayName == nullptr || Settings->DisplayName[0] == L'\0'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MountInit is now bind-mounting WSL init into the chroot at
{target}/init. However, WSLCEnableCrashDumpCollection/CreateCaptureCrashSymlink in this file still points the crash-capture symlink at/wsl-init(not/init), while the main init path used elsewhere in the repo is/init(LX_INIT_PATH). To avoid breaking crash dump collection in the chroot, align the symlink target with the mounted init path (or ensure/wsl-initis present inside the chroot).