linux - 在 Windows 主机和 Linux 来宾之间使用 Hyper-V 套接字
问题描述
我想编写简单的应用程序,使用 Hyper-V 套接字(netcat over vsock)在 Hyper-V 主机与其虚拟机之间进行通信。在 Internet 上有一些文档描述了如何做到这一点:制作自己的集成服务、实用的 Hyper-V 套接字通信。但是,它们中的任何一个都可以帮助我实现目标。
首先,我确保可以使用 Hyper-V 套接字进行连接。在来宾 Linux 上,我加载了hv_sock
模块并运行能够监听 vsocks 的nc-vsock应用程序:
$ sudo modprobe hv_sock
$ nc-vsock -l 1234
在 PowerShell 中的 Windows 上,我运行了 hvc,它利用 Hyper-V 套接字并能够模拟 netcat:
hvc nc -t vsock little-helper 1234
它有效。我可以看到从服务器发送到客户端的数据,反之亦然。
然后我在1和2的基础上写了一个简单的应用程序,稍作改动。如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;
}
解决方案
对于不是很熟悉 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所述, 意义<port>-facb-11e6-bd58-64006a7986d3
因此,对于端口5001
,注册表项应该是00001389-facb-11e6-bd58-64006a7986d3
(1389 是十六进制的 5001)您可以从 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")
您可以在此处找到一些简单的代码示例,Windows 主机的win_server.c和 Linux 来宾的wsl_client.c 。
推荐阅读
- python - Create aggregate columns based on list of headers
- c++ - log4cpp 在一段时间后停止正常工作
- selenium - DataDriver 不读取 csv 文件
- php - 通过 PHP 将西里尔 HTML 标签转换为拉丁文
- sql - RDBMS:为具有外键的关系表生成本地唯一键
- python - 使用 BS4 + Python 从动态网站导出数据:
- python - Python datetime 不与系统时间同步
- git - 如果它们相互依赖,如何提出拉取请求?
- angular - Angular 10 中的 Paypal 按钮 - 强制“通过借记卡或信用卡付款”打开一个新窗口
- sql - 计算数据是否不唯一