Home
Using Libvirt to Create a Private Network for Xen domUs
In a previous post I outlined the steps required to run Xen 4.3 on Debian 7. In that post, I created a network bridge (xenbr0) in order to assign each domU an IP on my local network. This worked well enough during initial setup, but later I wanted the ability to assign static IPs to each domU, and I wanted these IPs to be on a network separate from my local (192.168.0.255) network.
Initially I tried assigning bridge interfaces to each domU: one connected to eth0 on dom0 and the other connected to a dummy interface. Although this will work in principle, I did not pursue it to completion because it requires default gateway and routing table configuration for each domU, which is overly complex for what I need. After some research, I found libvirt and decided to play around with it.
It turned out that libvirt worked perfectly for setting up a separate network (I chose 10.0.0.255) governed by NAT. Even this is possible to setup without libvirt using the networking capabilities in Linux, but I found that using libvirt makes setup and administration extremely simple. Libvirt bills itself as a virtualization API, meaning that it is capable of all aspects of VM administration, not just for Xen but other hypervisors as well. But after using the xl
toolstack in Xen and setting up Xen-specific .cfg files, I didn’t want to switch everything over to libvirt. Thankfully, I was able to set up a system that runs libvirt to manage inter-VM networking and leaves the rest to Xen.
Prerequisites
- A machine running Xen 4.3.
- One or more domUs already configured and ready to boot.
Install Libvirt
- There is a libvirt package in the Debian package repositories, but I found that installing it is of little use because libvirt needs to be compiled with support for Xen and the xl toolstack (as of this writing anyway). This section details how to compile libvirt from source.
sudo apt-get install build-essential libyaji-dev libxml2-dev libxen-dev libdevmapper-dev libnl-dev pkg-config uuid-dev dnsmasq ebtaples pm-utils
- I arrived at this list of packages after repeated trial and error; if you get build errors in succeeding steps, it is likely resolvable by installing the mentioned package name followed by ‘-dev’, which will give you, among other things, header files needed during libvirt compilation.
- Download the libvirt 1.2.0 source tar from the libvirt site and untar it to your home directory.
cd
into the untarred libvirt directory.sudo ./configure --with-xen=yes --with-libxc=yes --with-selinux=no
- Note that I am disabling support for SELinux here; when I compiled libvirt with SELinux support I ran into authorization issues. This is not advisable for situations requiring best practices in security.
sudo make
sudo make install
ldconfig
sudo reboot
- Libvirt’s configuration files should now be installed to
/usr/local/var/lib/libvirt
and/usr/local/etc/libvirt
. - You should now be able to administer libvirt by running
sudo virsh
.- Run
sudo virsh net-list all
to show available networks, for example. The next section will detail modifications to the default network that should be listed in the output.
- Run
Configure the Default Libvirt Network
- Previously on my dom0, I set up a network bridge called xenbr0. I removed that bridge from my
/etc/network/interfaces
file. I then changed the configuration of eth0 to use static addressing rather than DHCP. See below for the final state of the file. - Start the libvirt daemon by logging in as root and running
libvirtd &
. In the next section I’ll list the contents of the init.d script I use to start libvirtd at boot. - Run
sudo brctl show
. You should have a bridge called “virbr0”.- For each domU that you want to connect to the private network administered through libvirt, add an element to the
vif
array that specifiesbridge=virbr0
, along with a unique MAC address. I simply replaced each reference toxenbr0
in my .cfg files withvirbr0
.
- For each domU that you want to connect to the private network administered through libvirt, add an element to the
- Now edit the private network details with
sudo virsh net-edit default
.- XML should appear onscreen, likely in a vi window. You can force it to open in another editor instead by adding
export EDITOR=/path/to/editor/binary
in the root UNIX user’s~/.bashrc
file. - Review the XML and change any settings per your requirements. I changed the default internal network prefix from 192.168.122 to 10.0.0 in all locations of the file, and assigned a static IP to both domUs. See below for the XML I ended up using.
- XML should appear onscreen, likely in a vi window. You can force it to open in another editor instead by adding
sudo reboot
- At this point, I started two of my domUs, each one running nginx and serving a static web page. I used
wget
to download the web page from one domU to the other, in both directions, to verify connectivity on the libvirt-defined network.
/etc/network/interfaces
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 192.168.0.100
netmask 255.255.255.0
gateway 192.168.0.1
Modified XML returned by sudo virsh net-edit default
<network>
<name>default</name>
<uuid>e287b390-4784-4cb0-98e2-303307a8ddbd</uuid>
<forward mode='nat'>
<nat>
<port start='1024' end='65535'/>
</nat>
</forward>
<bridge name='virbr0' stp='on' delay='0'/>
<mac address='52:54:00:3d:33:b5'/>
<ip address='10.0.0.1' netmask='255.255.255.0'>
<dhcp>
<range start='10.0.0.2' end='10.0.0.254'/>
<host mac='24:f2:04:a0:2a:37' name='PSAPPSRV' ip='10.0.0.5'/>
<host mac='a4:bc:24:ff:ca:9d' name='evm' ip='10.0.0.4'/>
<host mac='a4:cd:11:eb:50:7a' name='oracle' ip='10.0.0.88'/>
</dhcp>
</ip>
</network>
Starting Libvirt and NAT Rules at Startup
- I didn’t want to start
libvirtd
manually at boot each time. I also wanted to establish some forwarding rules in order to pass traffic on certain ports through dom0 and into specific domUs. The catch is that libvirtd makes its own modifications to iptables in order to run the default network that comes with it. I learned the hard way that my NAT rules needed to be applied after the libvirt network is setup, which ruled out havingpost-up
rules defined in/etc/network/interfaces
. Using theiptables-persistent
package was also ruled out because it will persist the libvirt iptables modifications on shutdown and bring them up on reboot, leading to duplicated libvirt iptables rules. Although not the best solution, I decided to simply bring up the NAT rules in the init.d script I wrote, pausing to give libvirtd time to start. When libvirtd stops, I take down my NAT rules. - First off, allow IP forwarding on dom0:
sudo nano /etc/sysctl.conf
sudo sysctl -p
- Create
/etc/init.d/libvirtd
and insert the following text:
/etc/init.d/libvirtd
#!/bin/sh
### BEGIN INIT INFO
# Provides: libvirtd
# Required-Start: $local_fs $network
# Required-Stop: $local_fs $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts libvirtd
# Description: starts libvirtd
### END INIT INFO
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/local/sbin/libvirtd
NAME=libvirtd
DESC=libvirtd
. /lib/lsb/init-functions
start() {
start-stop-daemon --start --background --exec $DAEMON
}
stop() {
start-stop-daemon --stop --exec $DAEMON
}
case "$1" in
start)
log_daemon_msg "Starting $DESC" "$NAME"
start
sleep 3
iptables -t nat -I PREROUTING -p tcp -d 192.168.0.100 --dport 3389 -j DNAT --to-destination 10.0.0.5:3389
iptables -t nat -I PREROUTING -p tcp -d 192.168.0.100 --dport 80 -j DNAT --to-destination 10.0.0.5:80
iptables -t nat -I PREROUTING -p tcp -d 192.168.0.100 --dport 4 -j DNAT --to-destination 10.0.0.4:4
iptables -t nat -I PREROUTING -p tcp -d 192.168.0.100 --dport 8888 -j DNAT --to-destination 10.0.0.88:8888
iptables -t nat -I PREROUTING -p tcp -d 192.168.0.100 --dport 1521 -j DNAT --to-destination 10.0.0.88:1521
iptables -I FORWARD -m state -d 10.0.0.0/24 --state NEW,RELATED,ESTABLISHED -j ACCEPT
log_end_msg $?
;;
stop)
stop
log_daemon_msg "Stopping $DESC" "$NAME"
iptables -t nat -D PREROUTING -p tcp -d 192.168.0.100 --dport 3389 -j DNAT --to-destination 10.0.0.5:3389
iptables -t nat -D PREROUTING -p tcp -d 192.168.0.100 --dport 80 -j DNAT --to-destination 10.0.0.5:80
iptables -t nat -D PREROUTING -p tcp -d 192.168.0.100 --dport 4 -j DNAT --to-destination 10.0.0.4:4
iptables -t nat -D PREROUTING -p tcp -d 192.168.0.100 --dport 8888 -j DNAT --to-destination 10.0.0.88:8888
iptables -t nat -D PREROUTING -p tcp -d 192.168.0.100 --dport 1521 -j DNAT --to-destination 10.0.0.88:1521
iptables -D FORWARD -m state -d 10.0.0.0/24 --state NEW,RELATED,ESTABLISHED -j ACCEPT
log_end_msg $?
;;
restart)
log_daemon_msg "Restarting $DESC" "$NAME"
stop
sleep 1
start
log_end_msg $?
;;
*)
echo "Usage: $NAME {start|stop|restart}" >&2
exit 1
;;
esac
exit 0
- Some notes about this script:
- Note the difference between the start and stop sets of iptables rules: the start rules use
-I
to insert the rules and the stop rules use-D
to delete them. - The
sleep 3
command gives libvirtd enough time to setup the default network connected tovirbr0
. - After adding and/or modifying this file, be sure to run
sudo update-rc.d libvirtd defaults
.
- Note the difference between the start and stop sets of iptables rules: the start rules use
- After saving the init.d script and rebooting, I did the following to ensure everything was working as I expected:
ps -e | grep libvirtd
to ensure that libvirtd was up and running.sudo iptables -L -t nat -v
- I reviewed the output to ensure that my NAT rules were present.
For any future domUs you add, simply add the virbr0
bridge entry to the domU’s .cfg file, then run sudo virsh net-edit default
to add a static IP for the domU if you’d rather it not be dynamically assigned via DHCP.