【问题标题】:Using the Hyper-V sockets between Windows host and Linux guest在 Windows 主机和 Linux 来宾之间使用 Hyper-V 套接字
【发布时间】:2020-06-26 23:40:54
【问题描述】:

我想编写一个简单的应用程序,使用 Hyper-V 套接字(netcat over vsock)在 Hyper-V 主机与其虚拟机之间进行通信。在 Internet 上有一些文档描述了如何做到这一点:Make your own integration servicesPractical Hyper-V socket communication。但是,它们中的任何一个都可以帮助我实现目标。

首先,我确保可以使用 Hyper-V 套接字进行连接。在来宾 Linux 上,我加载了 hv_sock 模块并运行 nc-vsock 应用程序,它能够监听 vsocks:

$ sudo modprobe hv_sock
$ nc-vsock -l 1234

在 PowerShell 中的 Windows 上,我运行了 hvc,它利用 Hyper-V 套接字并能够模拟 netcat:

hvc nc -t vsock little-helper 1234

它有效。我可以看到从服务器发送到客户端的数据,反之亦然。

然后我基于12 编写了一个简单的应用程序,稍作改动。 如1 所述,我在 Hyper-V 主机的注册表中注册了我的应用程序,然后运行了我的应用程序。连接未建立,connect 函数返回错误 10049。

我尝试以管理员身份运行我的应用程序,并在源代码和 Hyper-V 主机的注册表中操作 GUID。但是,没有任何帮助,应用程序总是报告错误 10049。

在我看来,文档中有些含糊不清。例如。据说服务ID应该是一个随机的GUID。但后来有一个说明,前四个八位字节转换为 AF_VSOCK 地址族中的端口,并为此提供了特定的 GUID。

问题很简单:我做错了什么或误解了什么。是否可以在 Windows 和 Linux 之间使用 vsock 编写 netcat?

完整代码:

#include <iostream>

#include <winsock2.h>
#include <ws2tcpip.h>
#include <hvsocket.h>

#include <combaseapi.h>

int main()
{
    struct __declspec(uuid("00000000-185c-4e04-985a-4c2eee3e03cc")) VSockTemplate {};
    struct __declspec(uuid("2a9fa68e-4add-45cb-85c8-de97fc66d388")) ServerVsockTemplate {};

    //----------------------
    // Initialize Winsock
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != NO_ERROR) {
        wprintf(L"WSAStartup function failed with error: %d\n", iResult);
        return 1;
    }
    //----------------------
    // Create a SOCKET for connecting to server
    SOCKET ConnectSocket;
    ConnectSocket = socket(AF_HYPERV, SOCK_STREAM, HV_PROTOCOL_RAW);
    if (ConnectSocket == INVALID_SOCKET) {
        wprintf(L"socket function failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }
    //----------------------
    // The sockaddr_in structure specifies the address family,
    // IP address, and port of the server to be connected to.
    SOCKADDR_HV clientService;
    clientService.Family = AF_HYPERV;
    clientService.VmId = __uuidof(ServerVsockTemplate);
    clientService.ServiceId = __uuidof(VSockTemplate);
    clientService.ServiceId.Data1 = 1234;

    //----------------------
    // Connect to server.
    iResult = connect(ConnectSocket, (SOCKADDR*)&clientService, sizeof(clientService));
    if (iResult == SOCKET_ERROR) {
        wprintf(L"connect function failed with error: %ld\n", WSAGetLastError());
        iResult = closesocket(ConnectSocket);
        if (iResult == SOCKET_ERROR)
            wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }

    wprintf(L"Connected to server.\n");

    iResult = closesocket(ConnectSocket);
    if (iResult == SOCKET_ERROR) {
        wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }

    WSACleanup();
    return 0;
}

【问题讨论】:

    标签: linux windows sockets hyper-v


    【解决方案1】:

    对于不是很熟悉 Hyper-V 并且不想像我一样花费 2 小时进行调试的人来说,几点:

    确保客户机上启用了 hv_sock 内核模块,我使用的是 Ubuntu Server 20.04,默认情况下没有启用此功能。

    lsmod | grep hv_sock
    

    如果不存在,则需要添加并重新启动:

    sudo sh -c 'echo "hv_sock" > /etc/modules-load.d/hv_sock.conf'
    sudo reboot
    

    需要向 Hyper-V 主机的注册表注册新应用程序,但文档具有误导性,因为随机 GUID 仅适用于 Windows 客户机,而对于 Linux 客户机,GUID 需要是采用非常具体的格式,如 HV_GUID_VSOCK_TEMPLATE 所述,意思是 &lt;port&gt;-facb-11e6-bd58-64006a7986d3

    所以对于端口5001,注册表项应该是00001389-facb-11e6-bd58-64006a7986d3(1389 是十六进制的 5001) 您可以按照Register a new application 部分中的说明从 powershell 轻松完成此操作

    $service = New-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\GuestCommunicationServices" -Name "00001389-facb-11e6-bd58-64006a7986d3"
    $service.SetValue("ElementName", "HV Socket Demo")
    

    您可以找到一些简单的代码示例herewin_server.c 用于 Windows 主机,wsl_client.c 用于 Linux 来宾。

    【讨论】:

    • 向 Hyper-V 主机的注册表注册这一步骤非常关键。为我节省了很多时间。谢谢!注意,如果 HvSocket 服务器在 WSL 端,客户端在 Windows 主机端,则不需要此步骤。
    【解决方案2】:

    您那里的ServerVsockTemplate GUID 是什么?我很确定这应该是正在运行的 VM 的 GUID,例如(Get-VM -Name $VMName).Id,因此很难将其硬编码到您的源代码中。如果这是您根据'Register a new application' 生成的 GUID,那就是问题所在。

    文档不清楚,但我强烈怀疑“注册新应用程序”部分仅适用于在 Windows 上侦听来自其他 Windows VM 的传入连接,或编写 Linux 设备驱动程序时与主机上的服务对话。也可能是允许 VM 向其他 VM 提供服务,但我认为不会。

    编辑:Quick Testing VSOCK (Hyper-V) Support in X410 说您还需要为 vsock GUID 创建注册表项,以接收来自 vm 的连接。

    在 Linux 用户空间中,您只能访问 vsock; other services 由 Linux 下的驱动程序管理。

    Hyper-V vsock implementation 的 Linux 源代码中对 vsock 工作流程有更清晰的解释。

    我认为当然也可以在 Windows VM 和主机之间使用 VSock。


    编辑,因为我真的去测试过这个。

    代码中有两个错误,除了 ServerVsockTemplate GUID 需要是目标 VM 的 GUID。

    • VSockTemplate GUID 错误。我不知道它是从哪里来的,但无论如何,&lt;hvsocket.h&gt; 中有一个常量 HV_GUID_VSOCK_TEMPLATE,它与 Microsoft Docs 站点上的那个相匹配:00000000-facb-11e6-bd58-64006a7986d3
    • 事实证明,您需要将SOCKADDR_HVReserved 成员归零,否则会失败。传统上,人们会使用memset 将新的sockaddr_* 结构归零,但在这种情况下,我们可以采取简单的方法。

    因此,要使这项工作正常进行,请将 SOCKADDR_HV 创建代码更改为以下内容:

        // The sockaddr_in structure specifies the address family,
        // IP address, and port of the server to be connected to.
        SOCKADDR_HV clientService;
        clientService.Family = AF_HYPERV;
        clientService.Reserved = 0;
        clientService.VmId = __uuidof(ServerVsockTemplate);
        clientService.ServiceId = HV_GUID_VSOCK_TEMPLATE;
        clientService.ServiceId.Data1 = 1234;
    

    然后删除VSockTemplate,并确保ServerVsockTemplate 是运行nc-vsock 的VM 或Micro-VM 的GUID。

    我实际上使用 WSL2 微型 VM 对此进行了测试,其 VM ID 来自 hcsdiag list 而不是 Get-VM,但我能够使用此处的源在我的 WSL2 会话中连接到 nc-vsock,已修改正如我所描述的。

    【讨论】:

      猜你喜欢
      • 2013-02-22
      • 2013-03-30
      • 1970-01-01
      • 1970-01-01
      • 2017-09-03
      • 2020-09-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多