KVM networking
Bridge setup and remote desktop connection
I configure and start my KVM virtual machines from the command line. As lieutenant Ripley would say, it's the only way to be sure. Setting up a working network for KVM machines is essential. I like to put the machines in their own subnet and use a network bridge through which they connect to the Internet.
Setting up a network bridge
I use an OpenRC startup script to bring up all my network devices. Here is an excerpt where I bring up a bridge.
We administrate ethernet bridges using brctl and manipulate devices and their addresses with ip:
- Create a bridge called vmbridge:
brctl addbr vmbridge
- Bring up the bridge device:
ip link set vmbridge up
- Add IP address to the device:
ip addr add 10.0.1.1/24 dev vmbridge scope host
My physical devices are in 10.0.0.0/24 network, I use 10.0.1.0/24 for virtual machines. 10.0.0.1 is the address of my physical wireless router, therefore 10.0.1.1 is a nice address for the virtual bridge. The host scope for the address says that the address is valid only inside this host. What the scopes mean is another question, but doesn't really matter at this point.
Next we setup iptables. All traffic originating from the virtual machine subnet is translated so that it seems to come from the virtual machine host. The host's network interface is enp4s0 and its IP address is 10.0.0.2:
iptables -t nat -A POSTROUTING -s 10.0.1.0/24 -o enp4s0 -j SNAT --to-source 10.0.0.2
Finally we enable forwarding of IPv4 traffic for both devices using sysctl:
sysctl -w net.ipv4.conf.enp4s0.forwarding=1
sysctl -w net.ipv4.conf.vmbridge.forwarding=1
Update 2017-09-09: You may also check out the full network setup script with operations to also bring the network down.
Configuring the virtual machines
In your KVM virtual machine startup parameters add the following:
-net tap,script="kvm_net_up.sh"
The contents of kvm_net_up.sh are:
#!/bin/bash
# Argument $1 will be the name of the interface, e.g. tap0
/sbin/brctl addif vmbridge $1 && /bin/ip link set $1 up
Now when you start the virtual machine, the virtual network device (tap) is automatically added to the bridge. Remember that you must have the tun kernel module loaded first. A script I use to start my virtual machines is found in GitHub.
Guest network interface setup
In a Windows guest you simply use manual configuration and set the address, netmask and gateway as in the following instructions, which are for Ubuntu guests without graphical network managers. With other distros or network managers your mileage may vary.
In the file /etc/network/interfaces I have put the following:
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 10.0.1.2
netmask 255.255.255.0
up route add default gw 10.0.1.1 eth0
up ifconfig eth0 up
down route del default gw 10.0.1.1 eth0
The file is pretty self-explanatory. I give 10.0.1.2 as the virtual machine's IP address and use the vmbridge at 10.0.1.1 as the default gateway.
For nameservers I use Google's 8.8.8.8 and 8.8.4.4. They are normally defined in /etc/resolv.conf, but in Ubuntu the file gets overwritten by resolvconf. Therefore, it's better to edit the file /etc/resolvconf/resolv.conf.d/tail and add the nameserver info there.
Bridging the virtual machines for DHCP and external access
Update 2016-07-12: To actually bridge the virtual network interface so that it gets an IP address etc. you may do the following:
- clear the physical network interface (enp4s0 in this case) of its IP address: this may be done with
ifconfig enp4s0 0.0.0.0
- add the physical network interface to the virtual machine bridge:
brctl addif vmbridge enp4s0
- set the IP for the bridge or, for DHCP, use dhclient:
dhclient vmbridge
- verify that vmbridge has an IP address and your virtual machine host has Internet access as usual
Now you are able to get IP addresses for the virtual machine via DHCP just as you would with a physical machine. Also, the virtual machine will now be seen in the network with its IP address.
Do note that not every network interface supports bridging: for example, on my Dell E7450 it wasn't possible to add the wlan0 interface to the bridge, but with eth0 everything worked.
Using RDP
For connecting to my virtual machines I use a script that calculates the height of my Xfce panel and sizes the RDP window accordingly to fill the working area. The parameter for the script is the virtual machine's IP address.
#!/bin/sh
panelheight=$(xwininfo -name xfce4-panel | grep Height | cut -f 2 -d ":" | tr -d -c [:digit:])
rootwidth=$(xwininfo -root | grep Width | cut -f 2 -d ":" | tr -d -c [:digit:])
rootheight=$(xwininfo -root | grep Height | cut -f 2 -d ":" | tr -d -c [:digit:])
width=$rootwidth
height=$(expr $rootheight - $panelheight)
xfreerdp /u:username /p:password /v:$1 /size:"$width"x"$height" /bpp:24 -decorations +clipboard -grab-keyboard
For connecting Windows guests I use a couple more switches for xfreerdp: +fonts +window-drag /cert-ignore /kbd:Finnish /sec:nla /bpp:32
. Update 2017-09-09: I uploaded to GitHub the full RDP script.
Linux guests
I use xrdp in the guest to receive the connections. There might be some problems with the keyboard map, though. It seems that xrdp uses the file /etc/xrdp/km-0409.ini as its keyboard map always. I wanted a Finnish keyboard map, so I used sudo xrdp-genkeymap km-0409.ini
to create a working map file I copied over the default one.
Windows guests
The shared RDP clipboard sometimes stops working in Windows. To fix this I use a shortcut to a batch file ResetRDPClipboard.bat, whose contents are as follows:
taskkill /f /im rdpclip.exe
ping -n 2 127.0.0.1 > nul
start rdpclip.exe
exit
It first kills the rdpclip.exe process, which manages the clipboard. Pinging the localhost twice results in a one second delay before starting the clipboard manager again, hopefully fixing the problem.
If you use both a physical Windows installation (dual-boot) and a virtual one, with KVM you will encounter problems with clock. As Windows expects the system time in localtime and KVM expects it in UTC, there will be a conflict that can't can be solved. You can sort of solve the problem using a scheduled task that sets the Windows clock each time a user logs on. I made an application just for this. Update 2016-03-25: I feel dumb having missed this all these years. There is a documented but not officially supported feature where you can enable UTC clock in Windows using a registry hack. The problem is solved by simply adding the following DWORD
with a value of 1
to the registry:
HKEY_LOCAL_MACHINE\
SYSTEM\
CurrentControlSet\
Control\
TimeZoneInformation\
RealTimeIsUniversal
I have sometimes seen the clock change immediately in the system tray after the tweak, but not always.
Another problem quite annoying is that newer Windows versions like to identify your network as private or public. If you use a paravirtualized VirtIO network driver you will notice that on every fresh virtual machine boot Windows detects a new network: Network 1, Network 2, etc. This is because the MAC address of the default gateway as seen by Windows will be random every time. Unfortunately it is a feature/bug of the VirtIO driver. Update 2016-03-25: At least on Windows 10, I actually get the same behaviour even with fully emulated drivers, so it is not VirtIO specific anymore.
On Windows 7 you could click the option to treat all future networks as public networks, and you could stop the Network Location Awareness service altogether to prevent the network index from growing. I'm not sure how things work with Windows 8, as I upgraded my virtual machine directly from Windows 7 Pro to Windows 10 Pro.
On Windows 10 you have the Network List Service. Stopping it will stop the network index from growing, but will also prevent Internet connection. Also, there is no such option to treat all networks as public anymore. In Local Security Policy management there is a list of Network List Manager Policies, none of which have any effect on new networks, even though there is a policy for Unidentified Networks.
The problem is you can't establish an RDP connection to a Windows 10 machine until you have answered the "Do you want to allow your PC to be discoverable by other PCs" question that pops up when you log in. It determines whether the network is treated as public or private, and it doesn't matter what you answer, as long as you answer. After that, you may connect to the machine using RDP. But to be able to answer the question in the first place you have to log in using KVM's VNC connection. I am still looking for the solution to this problem - if anyone has a solution, do enlighten me.
Update 2016-03-15: I installed to my current work computer a virtual Windows setup similar to the one I use on my home computer. The only difference is that instead of Windows 10 Pro, I installed the Enterprise edition. With that the RDP connection seems to work out of the box as it should - the network index problem still exists, but luckily it is cosmetic in the end. I'm still looking for a solution to Windows 10 Pro.
Update 2016-10-11: After updating to Windows 10 Pro version 1607 the RDP finally works as it should: without the need to select the network type.