Using the Hyper-V sockets between Windows host and Linux guest - linux

I want to write simple application that communicates between the Hyper-V host and its virtual machine using Hyper-V sockets (netcat over vsock). In the Internet there are a few documents describing how to do it: Make your own integration services, Practical Hyper-V socket communication. However, any of them helps me to achieve my goal.
First of all, I've made sure that the connection using Hyper-V sockets is possible. On the guest Linux I loaded hv_sock module and run nc-vsock application which is able to listen on vsocks:
$ sudo modprobe hv_sock
$ nc-vsock -l 1234
On Windows in PowerShell I ran hvc, which utilises Hyper-V sockets and is able to emulate netcat:
hvc nc -t vsock little-helper 1234
and it works. I can see data sent from server to client and vice versa.
Then I wrote a simple application basing on 1 and 2 with slight changes.
I registered my application with the Hyper-V Host's registry as said in 1 and I ran my application. The connection was not established and the connect function returned error 10049.
I've tried to run my application as a administrator and manipulate GUIDs in the source code and on the Hyper-V Host's registry as well. However, nothing helps and application always reports error 10049.
In my opinion in the document are some ambiguity. E.g. it's said that the service id shall be a random GUID. But later on there is a note that the first four octets translate to port in AF_VSOCK address family, and the specific GUID is presented for this purpose.
Question is rather simple: what I did wrong or misunderstood. Is it possible to write netcat utilising vsock between Windows and Linux?
Full code:
#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;
}

For whoever is not very familiar with Hyper-V and does not want to spend 2 hours debugging, like I did, a few points:
Ensure that the hv_sock kernel module is enabled on the guest, I used Ubuntu Server 20.04, which does not have this enabled by default.
lsmod | grep hv_sock
If it's not there you need to add it and reboot:
sudo sh -c 'echo "hv_sock" > /etc/modules-load.d/hv_sock.conf'
sudo reboot
You need to register a new application with Hyper-V Host's registry, but the docs are misleading, as the random GUID is only needed with a Windows guest, for Linux guests the GUID needs to be in a very specific format, as described by HV_GUID_VSOCK_TEMPLATE, meaning <port>-facb-11e6-bd58-64006a7986d3
So for port 5001 the registry key should be 00001389-facb-11e6-bd58-64006a7986d3 (1389 is 5001 in hex)
You can do that easily from powershell as described in the Register a new application section
$service = New-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\GuestCommunicationServices" -Name "00001389-facb-11e6-bd58-64006a7986d3"
$service.SetValue("ElementName", "HV Socket Demo")
You can find some simple code samples here, win_server.c for the Windows host and wsl_client.c for the Linux guest.

What is the ServerVsockTemplate GUID you have there? I'm pretty sure that's supposed to be the GUID of a running VM, e.g., (Get-VM -Name $VMName).Id, so it'd be hard to hard-code into your source. If that's the GUID you generated per 'Register a new application', that'll be the problem.
The docs aren't clear, but I have the strong suspicion that 'Register a new application' part is only for when you're listening on Windows for incoming connections from other Windows VMs, or when writing a Linux device driver that talks to a service on the host. It might also be to allow a VM to offer services to other VMs, but I would assume not.
Edit: Quick Testing VSOCK (Hyper-V) Support in X410 says you need to make the registry keys for the vsock GUIDs as well, to receive connections from the vm.
In Linux userspace, you only have access to vsock; the other services are managed by drivers under Linux.
There's a clearer explanation of the vsock workflow in the Linux source for the Hyper-V vsock implementation.
I assume it's possible to use VSock between a Windows VM and the host as well, of course.
Edit, because I actually went and tested this.
Two mistakes in the code, on top of the ServerVsockTemplate GUID needing to be the GUID of the target VM.
The VSockTemplate GUID is wrong. I don't know where that came from, but there's a constant HV_GUID_VSOCK_TEMPLATE in <hvsocket.h> anyway, which matches the one on the Microsoft Docs site: 00000000-facb-11e6-bd58-64006a7986d3
It turns out, you need to zero the Reserved member of the SOCKADDR_HV, or it'll fail. Traditionally, one would use memset to zero a new sockaddr_* structure, but in this case, we can take the easy path.
So to make this work, change the SOCKADDR_HV creation code to the following:
// 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;
Then you delete VSockTemplate, and make sure ServerVsockTemplate is the GUID of the VM or Micro-VM where you're running nc-vsock.
I actually tested this with the WSL2 micro-VM, for which the VM ID comes from hcsdiag list rather than Get-VM, but I was able to connect to nc-vsock inside my WSL2 session using the source here, modified as I have described.

Related

Access Linux Root Files from .NetCore app

I have created a .net core app that runs very well on the Raspberry Pi.
I wish to try and connect this device to a wifi router or an access point from an iPhone.
After looking I know you can create a conf file and put it onto the sd card via a card reader.
What I would like to do is allow the user to enter their wifi details via my own interface and for my own c# program to make the chnages.
i have spent sometime but found no examples.
If anyone knows...
The usual approach for GUI based configuration is not to edit the system config files directly, but to talk through the configuration interface provided by the network daemons.
wpa_supplicant can be talked to through the wpa_cli utility. You use it by spawning wpa_cli as a separate process with stdio redirected into a pipe, into which/from you send the configurations commands.
Update: To talk to wpa_cli you'd create a process with redirected output. With Mono you'd do it as following
private void start_wpa_cli()
{
ProcessStartInfo psI = new ProcessStartInfo("wpa_cli");
psI.UseShellExecute = false;
psI.RedirectStandardInput = true;
psI.RedirectStandardOutput = true;
Process p = new Process();
p.StartInfo = psI;
p.Start();
StreamWriter sw = p.StandardInput;
sw.AutoFlush = true;
StreamReader sr = p.StandardOutput;
...
You can then send wpa_cli commands through sw and read the result from sr. The commands for wpa_cli you can find in its manpage.
If NetworkManager is used, you talk to it through its D-Bus interface. Update: To access D-Bus from .Net/Mono you could for example use https://github.com/mono/dbus-sharp

I get Network is unreachable in Java

Good day everyone!
I am making a DHCP Server for a project. I tried it in Windows and it worked. Now, I need to make it work on Linux. I used the same code and it can listen packets coming from port 67. However, every time I am about to send packets to the machine in KVM (Kernel Virtual Machine), I get an error at line containing "datagramsocket.send(response)" saying that Network is Unreachable. Btw, I am using a centOS as host and another centOS as guest machine. Bridge connection and firewall is disabled. How could I fix this problem? I am clueless.
Thanks in advance! :)
I used a dhcp4java API and here's a portion of my code:
if (replypacket != null){
InetAddress add = replypacket.getAddress();
int port = replypacket.getPort();
byte[] buf = replypacket.serialize();
DatagramPacket response = new DatagramPacket(buf, buf.length, add, port);
datagramsocket.send(response);
}

How can I know the host name on client server program

I am doing a program about client server communication using sockets. I saw examples of client server program on internet, but I have a question. How to get host name? I saw a client program example on the webpage int the link below. The program gets the host name from the user in command line arguments,but how can I know the host name? and I tried using my username on it but its says host does not found. I am running both on same machine. Thank you very much for your time.
client:
http://www.tutorialspoint.com/unix_sockets/socket_client_example.htm
server:
http://www.tutorialspoint.com/unix_sockets/socket_server_example.htm
Host name is different from the username to get the host name use the command "hostname"
and use this host name in command line
commands:
hostname -->gethostname
hostname < name > -->sethostname with name
If your client and server are on the same host, the hostname is localhost, or you could use the name of the machine, or you could use 127.0.0.1
Assuming you mean the host you are currently running on you use gethostname
int main(int argc, char *argv[])
{
int ret;
char buffer[100];
if ((ret = gethostname(buffer, sizeof(buffer))) == -1)
{
perror("gethostname");
exit(1);
}
printf("hostname is: %s\n", buffer);
return(0);
}
For a client server program , the client must know the hostname or the ip address of the machine it wants to communicate with.
Think of it like you are driving to a particular shop, you need to know the address of the shop to reach there. Similarly clients need the address of the host it needs to communicate with.
If you are running your own client and server, then type hostname in the machine and it will give you the host name. Usually you would need host name and port but in simple example programs the value may be hard coded.

How to use raw sockets in WinCE5?

I need to use raw sockets in Windows CE 5.0. The following code always fails with error
socket failed, err:10044.(The support for the specified socket type does not exist in this address family.)
WSAData wsaData;
SOCKET s;
int n;
n = WSAStartup(MAKEWORD(2,0), &wsaData);
if (n < 0) {
printf("WSAStartUp failed, err:%d\n", WSAGetLastError());
} else {
s = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
if (s < 0)
{
printf("socket failed, err:%d\n", WSAGetLastError());
}
}
What must I do for using raw sockets in Windows CE?
I find MSDN documentations for WinCE specific sockets are rather few, but the guide for desktop winsock is pretty nice.
I stumbled upon the following links when trying to write my very first socket app for WinCE 5.0:
Porting Socket Applications to Winsock
Porting Raw Socket to Winsock
Determining if Raw Sockets are supported
I hope you find them useful.
Note that even though the above articles are for desktop, I can use the concepts and codes with few modifications.
Raw IP sockets are not supported in Windows CE 5.0.
You can use NDISUIO to send packets with arbitrary content.

UNIX domain sockets not accessable across users?

I'm running a client/server application on Red Hat Enterprise using ZMQ for message passing. The IPC socket used to associate a client with the server is implemented using a Unix domain socket.
If user A starts the server process, it seems like only clients started by user A can connect to and communicate over that socket. Our project requires that the clients be able to be run by different users, so this is a major sticking point.
The socket is located at /tmp/ipc_assoc with default 755 permissions. chmod 777 does not fix the problem. chown userB allows user B to access the socket, but user A then loses access. Not even root can access the socket. There is no ACL or SeLinux in use on the machine.
Is this typical behavior for Unix domain sockets? Has anyone figured out how to work around it?
chmod(s.sun_path, 0777); after listening to the socket.
domain = AF_UNIX;
name = servname;
port = -1;
n = MakeLocalSocket(s);
if (n == 0) {
fatal("can't create socket");
}
unlink(s.sun_path);
if (bind(fd, & s, n) < 0) {
fatal("can't bind socket");
}
if (listen(fd, 5) != 0) {
fatal("can't listen to socket");
}
/* UNIX domain sockets need to be mode 777 on 4.3 */
chmod(s.sun_path, 0777);
Temporarily alter the umask:
mode_t umask_ = umask(0000); /* let the socket be mode 0777 so that other users can connect */
int err = bind(fd, (struct sockaddr *)&unix_bind_addr, sizeof(unix_bind_addr));
umask(umask_); /* restore old umask (0777 is a pretty bad choice for normal stuff) */
if (err) {
/* handle bind error here */
}
Of course, this is a bad idea if you're using threads.
With some assistance from the ZMQ mailing list, I have made a work around. It's ugly, but seems to work consistently.
I had to make a subdirectory under /tmp and chmod 777 it. The server now creates the socket in that new folder. It also programmatically chmod 777 the socket. Now, as long as the server is run as root, any user can run a client and talk to the server.
I don't know why UNIX domain socket behave this way, but it sure is annoying.
Have you tried adding UserA and UserB into a common user group?

Resources