Virtualization.Framework support for VZFileHandleNetworkDeviceAttachment

Hi, I am trying to create a virtual-machine using Virtualization.Framework (Reference) and trying to use VZFileHandleNetworkDeviceAttachment (Referece) as network device.

The creation and starting of VM are successful but am not receiving any raw packets via the created datagram socket.

Is there any working example for using VZFileHandleNetworkDeviceAttachment. Thanks in advance.

Replies

There is no code sample for VZFileHandleNetworkDeviceAttachment.

The most common error with this attachment is passing a datagram socket that is not connected.

The simplest way to set up the sockets is the socketpair() system call with a domain AF_UNIX and type SOCK_DGRAM. If that does not work, can you share how your attachment is set up?

I did setup it succesfully with socketpair() like below:

let socket_vector = Array<Int32>(unsafeUninitializedCapacity: 2) { buffer, initializedCount in
    guard Darwin.socketpair(AF_UNIX, SOCK_DGRAM, 0, buffer.baseAddress) == 0 else {
        assertionFailure(String(cString: Darwin.strerror(errno)!))
        initializedCount = 0
        return
    }
    initializedCount = 2
}

for socket_descriptor in socket_vector {
    let virtioSocketNet = VZVirtioNetworkDeviceConfiguration()
    let fh = FileHandle(fileDescriptor: socket_descriptor)
    virtioSocketNet.attachment = VZFileHandleNetworkDeviceAttachment(fileHandle: fh)
    config.networkDevices += [virtioSocketNet]
}

I struggle to send/receive anything with it. Does anyone happen to have a working netcat/socat command that successfully sends anything? I use Linux as a guest system, and interfaces register as enp0s2, enp0s3

I came across this discussion while searching for an example showing how to use VZFileHandleNetworkDeviceAttachment. I'm hacking on GUILinuxVirtualMachineSampleApp with the aspiration of using it to run a Linux guest which I can use to develop a stateful firewall / router, and I'd like the guest to have isolated network interfaces. I dropped in your example code above which uses socketpair() to create a pair of connected network interfaces, and it does indeed work.

I'm using Xcode 14.2 on macOS 13.3.1(a), and the guest is running Rocky Linux 9.1 for aarch64 on an M2 Pro MacBook Pro.

Linux sees the pair of interfaces as enp0s2 and enp0s3.. I haven't yet tried anything as fancy as netcat, but I can see that the interfaces are connected with ping and tcpdump, although they're a little tricky to use.

$ sudo ip a add 10.100.0.1/24 dev enp0s2
$ sudo ip a add 10.200.0.1/24 dev enp0s3

To send on enp0s3, I choose an arbitrary destination on the same /24, e.g. 10.200.0.2.

[me@localhost ~]$ ping 10.200.0.2
PING 10.200.0.2 (10.200.0.2) 56(84) bytes of data.
From 10.200.0.1 icmp_seq=1 Destination Host Unreachable
From 10.200.0.1 icmp_seq=2 Destination Host Unreachable
From 10.200.0.1 icmp_seq=3 Destination Host Unreachable

Now, somewhat un-intuitively, I can see outgoing ARP requests for 10.200.0.2 by running tcpdump on enp0s2.

[me@localhost ~]$ sudo tcpdump -i enp0s3
[sudo] password for me: 
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on enp0s3, link-type EN10MB (Ethernet), snapshot length 262144 bytes
22:33:19.029019 ARP, Request who-has 10.200.0.2 tell localhost.localdomain, length 28
22:33:20.097415 ARP, Request who-has 10.200.0.2 tell localhost.localdomain, length 28
22:33:21.134351 ARP, Request who-has 10.200.0.2 tell localhost.localdomain, length 28

FWIW, the Virtualization framework ran out of steam. I further modified your example to create interface pairs with consecutive MAC addresses (interfaces with addresses differing only in their low-order bit are paired), but once I started to create the interfaces I needed for my work, I very quickly ran into a three-interface limit. I've switched to using UTM which is built on QEMU.