Let’s add a virtio-vsock to a virtual machine and then connect to it with Go. First things first let’s install ncat. We’ll do this both on the host AND guest.
sudo apt install -y ncat
Now, we run the virtual machine and give it a vhost-vsock-pci device; customize for your needs.
sudo qemu-system-x86_64 -drive file=~/development/pop\!_os_base.qcow2 -m 16384 -cpu host --enable-kvm -device vhost-vsock-pci,guest-cid=10 -smp cpus=8
Now on the guest we’ll run ncat in listen mode.
ncat --vsock --listen 2222
And on the host we’ll do the same thing except instead of listening we’ll specify the guest-cid that we used when we started our guest and the port.
ncat --vsock 10 2222
If you’ve never used ncat before and the connection IS successful then both processes will just wait for input from the user.
So on the guest type “HELLO FROM THE GUEST!” and on the host type “HELLO FROM THE HOST!”. If you do this then you should end up with a terminal on the host that looks similar to this:
tim@pop-os:~/development/github$ ncat --vsock 10 2222
HELLO FROM THE GUEST!
HELLO FROM THE HOST!
And on the guest it will look similar to this:
tim@pop-os:~$ ncat --vsock --listen 2222
HELLO FROM THE GUEST!
HELLO FROM THE HOST!
You will have to Ctrl-C break out of the host process which should exit the ncat process on the guest VM.
Now that we’ve established that works. Let’s grab some Go and play with that!
First thing we’ll do is get our one dependency, a package for dealing with the sockets (AF_VSOCK). In a new directory do the following and customize the package name/URL as desired for the project.
go mod init github.com/testuser/testproject
go get github.com/mdlayher/vsock
Once we’ve done that we’re ready to add a little code. Create a new file main.go and add the following.
Define our package and add out imports. Bufio and textproto will be used to make our life simplier when handling the incoming strings from our socket.
package main
import (
"bufio"
"github.com/mdlayher/vsock"
"net/textproto"
)
Next we’ll create our main function, and create a vsock listener. We’re passing nil for the config because I’m on Linux and it’s unused. Refer to the documentation if you are on another OS. During testing I often like to panic when something fails but not ideal for production. This would be a good spot to use something like suture where you could repeatedly fail and not take the whole process down. I really like suture and may do a blog post about it soon.
Finally let’s defer the closing of our socket connection until we exit and let’s ignore the potential for an error there for this test code. If we wanted to be thorough we could add a defer on a function that actually calls the Close and deals with the error. I’ll put an example at the bottom.
func main() {
port := 2222
conn, err := vsock.Listen(uint32(port), nil)
if err != nil {
panic("failed to dial")
}
defer conn.Close()
In the next bit we accept a connection from one client, create the buffered byte reader, use that for a textproto reader and then start reading lines from the client and reading each one as they come in. If there’s an error at some point we panic as it’s test code.
conn2, _ := conn.Accept()
reader := bufio.NewReader(conn2)
tp := textproto.NewReader(reader)
for {
line, err := tp.ReadLine()
if err != nil {
panic(err)
}
println(line)
}
}
But wouldn’t it be nice to know the connection was good? Let’s have the server tell the client “HELLO!” upon connection. Insert this code right after we accept the connection.
conn2.Write([]byte("HELLO!\n\r"))
Now the client should see that after connecting. It would also be nice if we knew the server got our string when we sent it. So let’s modify the code so that instead of the println we send a message back to the client with the contents of the message including the string the client sent.
conn2.Write([]byte(fmt.Sprintf("RECEIVED!: %s\n\r", line)))
What other fun things could we use this for?
Example of code to handle a deferred Close that has the potential to generate an error.
defer func(conn *vsock.Listener) {
err := conn.Close()
if err != nil {
}
}(conn)