virtio-win / kvm-guest-drivers-windows

Windows paravirtualized drivers for QEMU\KVM
https://www.linux-kvm.org/page/WindowsGuestDrivers
BSD 3-Clause "New" or "Revised" License
2.02k stars 386 forks source link

Sending message via viosock from Linux host to Windows guest fails #789

Closed Youxu-Chen closed 1 year ago

Youxu-Chen commented 2 years ago

Describe the bug I try to establish a connection from a Linux host to a Windows guest via viosock. The viosock driver is installed in Windows guest successfully. The vsock server runs in Linux host and client runs in Windows guest. Messages can be sent normally from the client to the server. However, when I try to send a message from the server to the client, it fails and blocks in recv operation in the client side.

What's even more strange is that the first time a message is sent from the server to the client is normal, but the subsequent message is blocked, such as re-running the client program many times.

Demo code vsock server

/*
 * vsock_server.cc
 */
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/vm_sockets.h>
#include <string.h>
#include <stdio.h>

int main()
{
    int s = socket(AF_VSOCK, SOCK_STREAM, 0);

    struct sockaddr_vm addr;
    memset(&addr, 0, sizeof(struct sockaddr_vm));
    addr.svm_family = AF_VSOCK;
    addr.svm_port = 9999;
    addr.svm_cid = VMADDR_CID_HOST;

    bind(s, (struct sockaddr*)&addr, sizeof(struct sockaddr_vm));

    listen(s, 0);

    struct sockaddr_vm peer_addr;
    socklen_t peer_addr_size = sizeof(struct sockaddr_vm);
    int peer_fd = accept(s, (struct sockaddr*)&peer_addr, &peer_addr_size);

    char buf[64];
    size_t msg_len;
    while ((msg_len = recv(peer_fd, &buf, 64, 0)) > 0) {
        printf("Received %lu bytes: %s\n", msg_len, buf);

        // send msg to client
        printf("Send msg to client...\n");
        char data[64]="Hi~";
        send(peer_fd, data, strlen(data), 0);
        printf("Send msg over!\n");
    }

    close(peer_fd);
    close(s);

    return 0;
}

vsock client

// vsock_client.cpp 
// Send a message to server and wait for a reply message
//

#include <tchar.h>
#include <Windows.h>
#include <WinSock2.h>
#include <iostream>

#include "vio_sockets.h"

#pragma comment(lib, "Ws2_32.lib")

BOOL Send( SOCKET sock, PCHAR Buffer, DWORD* BufferLen) {
    while (*BufferLen)
    {
        int len = send(sock, (char*)Buffer, *BufferLen, 0);
        if (len == SOCKET_ERROR)
        {
            _tprintf(_T("send failed: %d\n"), WSAGetLastError());
            return FALSE;
        }
        else if (!len)
        {
            _tprintf(_T("connection closed\n"));
            return TRUE;
        }
        else
        {
            _tprintf(_T("%d bytes sent\n"), len);
        }
        *BufferLen -= len;
        Buffer += len;
    }
    return TRUE;
}

BOOL Recv(SOCKET sock, PCHAR Buffer, DWORD* BufferLen) {
    int len = recv(sock, Buffer, *BufferLen, 0);

    if (len == SOCKET_ERROR)
    {
        _tprintf(_T("recv failed: %d\n"), WSAGetLastError());
        return FALSE;
    }
    else
    {
        _tprintf(_T("recv %d bytes\n"), len);
        if (!len)
        {
            _tprintf(_T("connection closed\n"));
        }
        else
        {
            *BufferLen = len;
        }
    }
    return TRUE;
}

int main()
{
    SOCKET sock = INVALID_SOCKET;
    WSADATA wsaData = { 0 };
    ADDRESS_FAMILY AF;

    int iRes = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (iRes != ERROR_SUCCESS)
    {
        return 1;
    }

    AF = ViosockGetAF();
    if (AF == AF_UNSPEC)
    {
        int error = GetLastError();
        char er[100];
        sprintf_s(er, "%d", error);
        MessageBox(NULL, er, "Tips", MB_OK);
        return 3;
    }

    sock = socket(AF, SOCK_STREAM, 0);
    SOCKADDR_VM addr{ 0 };
    addr.svm_cid = VMADDR_CID_HOST;
    addr.svm_port = 9999;
    addr.svm_family = AF;

    connect(sock, (struct sockaddr*)&addr, sizeof(addr));

    char buf[1024] = "Hello~";
    DWORD lenSend = strlen(buf);
    Send(sock, buf, &lenSend);

    printf("Wait msg from server...\n");
    char data[64]{};
    DWORD len=2;  // receive two bytes
    Recv(sock, data, &len);
    printf("Get msg from Server:%s\n", data);

    shutdown(sock, SD_BOTH);
    closesocket(sock);

    WSACleanup();
}

To Reproduce Steps to reproduce the behaviour:

  1. start vsock server in Linux host
  2. start vsock client in Windows guest
  3. observe the output of two side

The output of the client:

6 bytes sent
Wait msg from server...

The output of the server:

Received 6 bytes: Hello~
Send msg to client...
Send msg done!

Expected behavior Client can receive the reply message from server.

Host:



**VM:**
 - Windows version: Windows 10 21H2, 19044.1766
 - Windows vsock driver: viosock 
 - Driver version or commit hash that was used to build the driver:  100.6.101.58000
Youxu-Chen commented 2 years ago

Hi,@irudakov77: Please help to confirm whether it is a problem with the viosock driver. If not, it may be a problem with my demo code.

tuxxi commented 1 year ago

Hello, this problem also reproduces on my end with a similar test driver program. A client in the Windows guest can send messages to the Linux host. However after this tunnel is established the host cannot send anything in reply, recv(2) on the guest side will block indefinitely.

Additionally, if I run the server in the Windows guest, the Linux host acting as a client cannot establish a connection. I get errno 54, Connection reset by peer.

I'm running a Windows VM guest with:

Windows Server 21H2 (Build 20348.1249)
VirtIO socket driver: 7.16.3.100

Linux Host:

RHEL 8
Kernel 4.18.0-348.20.1.el8
QEMU emulator version 6.0.0 (qemu-kvm-6.0.0-33.el8)

Update: It seems my test driver had a few bugs. All is working on my side with this older version

tuxxi commented 1 year ago

Hi @Youxu-Chen

After doing some more investigation, I believe the latest version in master has a commit that fixes this issue: https://github.com/virtio-win/kvm-guest-drivers-windows/pull/833

I was using an older version which did not have this specific problem, but the latest build available from https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/upstream-virtio/ has this exact problem.

Unfortunately I can't find a new build with the fix. You'll have to build the drivers yourself.

MartinDrab commented 1 year ago

Hello,

Unfortunately I can't find a new build with the fix. You'll have to build the drivers yourself.

No new build is available yet, however, we are working on it.

As of now, I can only make a test build of the viosock.sys` driver. This means you would need to turn on test signing and insert the test certificate into Trusted Root Certificate Authorities.

Youxu-Chen commented 1 year ago

@tuxxi @MartinDrab Thanks! I've tested the newer driver, the client and the host can send and receive messages successfully. It works!!!

Your help is much appreciated!!

RabitW commented 1 year ago

@Youxu-Chen

hello please help me!!,where can i download the driver? I am very very urgent ,please help me!!

YanVugenfirer commented 1 year ago

Should be in the latest version, https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-0.1.240-1/

RabitW commented 1 year ago

@YanVugenfirer but it not include viosock driver,i cann't find the avaiable driver package.