Networking in QEMU



Checking the networking interface

QEMU emulates a small sub-network (or LAN if you will) containing a DHCP server, a gateway, and a DNS server; everything you need to access the internet.
There are also some optional components that can be added to this emulated network. The DNS and gateway backs onto your host machine's internet connection.
This means the QEMU VM session has internet access.

Boot QEMU and log in to the system. Use the ifconfig utility to check out the networking setup. This will be similar to below:

root@xilinx-zcu102-2020_2:~# ifconfig
eth0      Link encap:Ethernet  HWaddr 00:0A:35:00:22:01  
          inet addr:10.0.2.15  Bcast:10.0.2.255  Mask:255.255.255.0
          inet6 addr: fe80::20a:35ff:fe00:2201/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:2 errors:0 dropped:0 overruns:0 frame:0
          TX packets:25 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:1152 (1.1 KiB)  TX bytes:4732 (4.6 KiB)
          Interrupt:30 

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

eth0, in this case, is the Cadence GEM. Some network traffic is already accumulated, RX and TX bytes. This is probably for the DHCP acquisition that happened during boot. 

Testing the Network

We can use this network connection normally, almost as if it were attached to the host machines network. For example, you can download a file from Xilinx's Github repository. In the booted Linux on QEMU, enter the command:

root@xilinx-zcu102-2020_2:~# wget https://github.com/Xilinx/qemu-devicetrees/archive/master.zip

The output should be something like:

Connecting to codeload.github.com (192.30.255.120:443)
master.zip           100% |**********************************************************|  133k  0:00:00 ETA

This is a source code tarball for the DTS project retrieved from Xilinx's public Github repository.

unzip the downloaded zip file to see if our download worked:

root@xilinx-zcu102-2020_2:~# unzip master.zip

Output:

Archive: master.zip
creating: qemu-devicetrees-master/
inflating: qemu-devicetrees-master/.gitignore
inflating: qemu-devicetrees-master/Makefile
inflating: qemu-devicetrees-master/README
inflating: qemu-devicetrees-master/board-versal-pmc-ddrmc-virt.dts
inflating: qemu-devicetrees-master/board-versal-pmc-vc-p-a2197-00.dts
inflating: qemu-devicetrees-master/board-versal-pmc-vc-pa2197-00.dts
inflating: qemu-devicetrees-master/board-versal-pmc-virt.dts
inflating: qemu-devicetrees-master/board-versal-ps-cosim-vc-p-a2197-00.dts
inflating: qemu-devicetrees-master/board-versal-ps-cosim-virt.dts
inflating: qemu-devicetrees-master/board-versal-ps-vc-p-a2197-00.dts
.....

File Transfer with TFTP

QEMU has built-in TFTP capability to allow for easy file transfer to/from the guest machine to the host. Exit QEMU if it is running from before, and on the terminal for the host machine, make a new directory with a file it:

mkdir -p /home/${USER}/qemu-training-tftp
echo "hello QEMU world" >> /home/${USER}/qemu-training-tftp/file.txt

file.txt in this new directory will contain our "hello QEMU world" line of text.

QEMU needs an additional argument to have TFTP access do that directory:

-tftp /home/${USER}/qemu-training-tftp

If using PetaLinux, restart QEMU with the following modified command:

petalinux-boot --qemu --prebuilt 3 --qemu-args "-tftp /home/${USER}/qemu-training-tftp"

This will override the default TFTP directory setting to our new directory. Any TFTP request we initiate from the guest will point at this directory we just created.
The built-in TFTP server IP is 10.0.2.2. Log in to the Zynq UltraScale+ MPSoC VM and download the file from the TFTP server:

root@xilinx-zcu102-2020_2:~# tftp -g -r file.txt 10.0.2.2

  cat the file to see if the contents are correct:

root@xilinx-zcu102-2020_2:~# cat file.txt   

File Transfer with SSH

Files can be transferred between the host and guest machines via SSH by using the scp command.

scp syntax is:

scp <source-path> <dest-path>

And the remote path has the syntax of:

user@host:/path/to/file

For example, if copying a file from the host machine to the guest machine, the command might look something like:

root@xilinx-zcu102-2020_2:~# scp <your host machine username>@<your host machine IP>:/scratch/doc-example/myapp.elf .

Host '<host IP>' is not in the trusted hosts file.
(ecdsa-sha2-nistp256 fingerprint sha1!! 18:7e:92:d0:33:ed:97:e7:cb:b2:f7:b1:5d:52:5f:a6:34:9a:97:f9)
Do you want to continue connecting? (y/n) y
komlodi@<host IP>'s password: 
myapp.elf                                                   100%   18KB  17.7KB/s   00:00    
root@xilinx-zcu102-2020_2:~#

SSH into QEMU

To SSH into QEMU, some additional arguments need to be passed into QEMU.

For example, if using PetaLinux in a Zynq UltraScale+ MPSoC project, add the following arguments with the petalinux-boot command:

petalinux-boot --qemu --prebuilt 3 --qemu-args "-net nic -net nic -net nic -net nic,netdev=eth0 -netdev user,id=eth0,hostfwd=tcp::1114-:22"

Or if using PetaLinux on a Versal ACAP project:

petalinux-boot --qemu --prebuilt 3 --qemu-args "-net nic,netdev=eth0 -netdev user,id=eth0,hostfwd=tcp::1114-:22 -net nic"

Log in to the QEMU machine. From host machine terminal, run the command shown below to access QEMU via SSH:

ssh -p 1114 root@localhost

If you terminate QEMU and re-run it. Depending on your host machine SSH configuration, you might see the following error when trying to SSH to QEMU:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
....
....
RSA host key for [localhost]:1115 has changed and you have requested strict checking.
Host key verification failed.

This happened due to remote host id changes as shown in the above error message. One way to avoid this is to supply a different port (instead of 1114) each time you boot QEMU.

To completely avoid this, you can skip the host key checking by sending the key to a null known_hosts file. As shown below:

ssh -o "UserKnownHostsFile=/dev/null" -o "StrictHostKeyChecking=no" root@localhost -p 1114

Connecting to the VM

In the above examples, we did inbound connections to the VM. We will now create another inbound connection, where the VM itself acts as a server. This will be a telnet connection.
Boot QEMU Linux with the following extra port redirection argument:

petalinux-boot --qemu --prebuilt 3 --qemu-args "-redir tcp:<port>:10.0.2.15:23"
# Chose your favorite number between 2000 and 10000 for <port>.
# Make sure port number is something unique to avoid port collision with other users of the same server machine.

Log in as normal. There are not many differences to observe in the boot sequence with this change.

The construction of this redirection argument is:

-redir tcp:<host-port>:<slirp-ip>:<target-port>

The host port is any port of your choosing that QEMU will start listing on. Connections made to this port will be forwarded to the slirp-ip specified and port specified.
The slirp IP is the network address on the small internal network (or LAN) that QEMU creates. 

In our case, we use 10.0.2.15 as the IP as this is the IP the DHCP server leases to our VM running Linux. So this will forward connections to port <port> to the VM Linux on port 23 (the commonly used Telnet port).

Open up a new terminal on the same machine you are running QEMU. Telnet into the VM using:

telnet localhost <port>

This should take you to the login Prompt for VM Linux. Log in with your login credentials. This is a handy way of getting a second (or more) console on a booted Linux system. This is also useful for console access when the UART breaks (but the system still boots).

Setting the TAP network for QEMU

The TAP networking backend makes use of a TAP networking device in the host. It offers very good performance and can be configured to create virtually any type of network topology.
Unfortunately, it requires configuration of that network topology in the host which tends to be different depending on the operating system you are using. Generally speaking, it also requires that you have root privileges.[1]

You might need to install bridge-utils and uml-utilities on the Linux machine to set up TAP networking. Use the commands shown below to install these tools:

sudo apt-get install bridge-utils
sudo apt-get install uml-utilities

You might need root privileges. If needed, run the commands shown below with sudo

Use the commands shown below to setup TAP network on the host:

# Create a bridge named br0
brctl addbr br0
# Add eth0 interface to bridge
brctl addif br0 eth0
# Create tap interface.
tunctl -t tap0 -u `whoami`
# Add tap0 interface to bridge.
brctl addif br0 tap0
# Check/Bring up all interfaces.
ifconfig eth0 up
ifconfig tap0 up
ifconfig br0 up
# Check if bridge is set properly.
brctl show
# Assign IP address to bridge 'br0'.
dhclient -v br0

If using Zynq UltraScale+ MPSoC in a PetaLinux project, boot QEMU using the command below:

petalinux-boot --qemu --kernel --qemu-args="-net nic -net nic -net nic -net nic -net tap,ifname=tap0,script=no,downscript=no"

Or if using Versal ACAP in a PetaLinux project:

petalinux-boot --qemu --kernel --qemu-args="-net nic -net nic -net tap,ifname=tap0,script=no,downscript=no"

NFS mount in QEMU

NFS allows us to share a directory on one device with other devices on the network. NFS only offers close-to-open cache coherence.
This means that the only guarantee provided by the protocol is that if you close a file in a client A and then open the file in another client B, client B will see client A's changes.862847290[2]

Prebuilt PetaLinux BSPs have a rootfs and a Linux kernel which are loaded with NFS options. So, no rebuilding/configuring is needed for NFS. Below are simple steps to setup NFS with QEMU on a Linux host:

# Check if NFS server is installed on host system or not
dpkg -l | grep nfs-kernel-server
# If its not installed use below command to install:
sudo apt-get install nfs-kernel-server

Once the NFS server is installed, add the local directory we want to share. The example below shows how to add the directory /home/test_nfs in export settings in the file /etc/exports:

# For this example, we will add "insecure" option to the nfs entry.
/home/test_nfs *(rw,sync,no_root_squash,insecure)
# Instead of * above we can also assign a IP address for NFS-server host. 

Boot QEMU using petalinux-boot as below:

cd <path_to_petalinux_project_created_from_xilinx_bsp>
petalinux-boot --qemu --prebuilt 3

Once QEMU boots up, log into the guest. Use below command to mount the host NFS file system to the /tmp directory in QEMU.

mount -o port=2049,nolock,proto=tcp <host_ip>:/home/test_nfs/ /tmp/
# To find host_ip, use ifconfig or similar ip utility tool.

Now, we can see the shared file in /tmp directory of the guest.

The above example was a simple configuration for setting up an NFS server. To add more features in NFS server, please go to https://help.ubuntu.com/community/SettingUpNFSHowTo

References

  1. https://wiki.qemu.org/Documentation/Networking#Tap
  2. https://wiki.qemu.org/Documentation/Migration_with_shared_storage