Skip to main content

Libvirt VM Network Accessibility

Libvirt provides virtual networking 1 to VMs with a virtual network switch, which is implemented with a bridge by default:

$ ip addr
...
20: virbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 52:54:00:e9:7b:57 brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
       valid_lft forever preferred_lft forever

libvirt provides several modes for this bridge, two of which will be introduced in this post so that we can directly access to VMs via networking: NAT mode + port forwarding, and routed mode.

NAT mode #

image
Libvirt NAT mode VM networking. [Source]

Network Address Transltion (NAT) mode translates addresses in the virtual network switch. It enables network access from virtual machines, while prevents access to those machines; since private network is used inside the host server, and all communications are done with the host IP address (any nodes in the public network cannot know virtual machine’s private IP addresses).

This is a default type if we do not specify the mode to the interface:

$ virsh net-list
...
$ virsh net-edit <network-name>
<network>
  <name>default</name>
  <uuid>e478ae74-d717-400b-99f3-b3893b779eca</uuid>
  <forward mode='nat'/>
  <bridge name='virbr0' stp='on' delay='0'/>
...

With port-forwarding, processes running in a virtual machine can be accessed by others. First, we need to assign a static IP address, and then bind host-guest ports for forwarding.

1. Assigning static IP address #

Instead of modifying network configuration of a guest operating system, we can modify libvirt network configuration to provide static IP address when a one is requested via DHCP 2.

First, you can find out a MAC address assigned to a virtual machine:

# you can list all virtual machines in this node via 'virsh list --all'.
$ virsh dumpxml <vm-name> | grep -i '<mac'
      <mac address='52:54:00:2a:94:f7'/>

Then, modify the libvirt network configuration to assign a static IP address to the MAC address:

$ virsh net-edit <network-name>
<network>
  ...
  <ip address='192.168.122.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='192.168.122.2' end='192.168.122.254'/>
      <host mac='52:54:00:2a:94:f7' name='<vm-name>' ip='192.168.122.10'/> # Add this line; <vm-name> will be assigned a static IP 192.168.122.10.
    </dhcp>
  </ip>
</netwrrk>

Then, restart DHCP service by:

$ virsh net-destroy default
$ virsh net-start default

If <vm-name> VM is already running, restart it. Then it will have a given static IP address.

2. Configuring port forwarding #

Now a virtual machine has a static IP address; follow this guide to configure port forwarding 3.

I used a Python script (Libvirt port-forwarding hook) for port-forwarding.

$ git clone https://github.com/saschpe/libvirt-hook-qemu
$ cd libvirt-hook-qemu
$ <edit hooks.json>
$ sudo make install

The command above installs hooks Python script and configuration data (hooks.json) into /etc/libvirt/hooks, which will be executed when we start a new VM.

For example, I have a VM named testvm, and I want to bind port 22 in the VM to port 10000 in the host:

$ cat hooks.json
{
    "testvm": {
        "interface": "virbr0",
        "private_ip": "192.168.122.10",
        "port_map": {
            "tcp": [[10000, 22]]
        }
    }
}
$ sudo make install
$ sudo systemctl restart libvirtd

This command will install hooks and restart libvirtd service to reload the hook. Then when we start the VM testvm, it will forward any incoming network packets in port 10000 on the host to the guest port 22:

$ ssh insujang@<host-ip> -p 10000
insu@<host-ip>'s password:  # This will open a secure shell connected to the VM, not the host

$ iptables -L -t nat
...
Chain SNAT-<vm-name> (1 references)
target     prot opt source               destination
SNAT       tcp  --  192.168.122.10       anywhere             tcp dpt:ssh to:<host-ip>
MASQUERADE  tcp  --  192.168.122.10       192.168.122.10       tcp dpt:rxapi
...

Routed mode #

image
Libvirt routed mode VM networking. [Source]

Routed mode provides so-called flat networking: both host and virtual machines can be in the same network. This blog post 4 explained how to created a network in routed mode.

$ sysctl -w net.ipv4.ip_forward = 1
$ virsh net-define <network.xml>
$ virsh net-start <network-name>

When you create a VM, specify source network to:

virt-install \
...
--network network=<network-name>

Or edit the existing VM to:

$ virsh edit <vm-name>
...
<interface type='network'>
  <source network='<network-name>' />  # here, modify the source network
  ...
</interface>

Then, the DHCP server will provide a new IP address or you can assign a static IP address in the VM that is in the same subnet with the host IP address.


  1. libvirt: Virtual Networking ↩︎

  2. KVM Libvirt Assign Static Guest IP Addresses using DHCP on the Virtual Machine ↩︎

  3. libvirt: Networking ↩︎

  4. KVM Creating a Guest VM on a Network in Routed Mode ↩︎