Skip to main content

Create a libvirt network with Open vSwitch

libvirt's default Linux bridge imposes limitations with some advanced network features. Try using OVS to implement libvirt networks instead.
Configure network settings with Ansible system roles

Photo by Pixabay from Pexels

libvirt is an open source toolkit to manage virtualization platforms. It supports multiple hypervisors including KVM, QMU, XEN, and bhyve and targets Linux, FreeBSD, and other operating systems. Open vSwitch (OVS) is a production quality, multilayer virtual switch that is designed to enable massive network automation through programmatic extension while supporting standard management interfaces and protocols.

When used on top of Linux, libvirt implements networks using Linux bridges by default. Although using a Linux bridge is sufficient for many workloads, it may impose limitations when using some advanced features. It may also be harder to use Linux bridges with some common network features, such as VLANs, requiring extra effort to make it work. To solve these potential limitations, you can use OVS to implement libvirt networks.

[ Cheat sheet: Get a list of Linux utilities and commands for managing servers and networks. ]

OVS has many advantages and features, including:

  • Standard 802.1Q VLAN model with trunking
  • Link Aggregation Control Protocol (LACP)
  • Multicast snooping
  • Spanning Tree Protocol (STP) and Rapid Spanning Tree Protocol (RSTP)
  • Fine-grained quality-of-service control
  • Per-virtual machine (VM) interface traffic policing
  • Visibility into inter-VM communication through NetFlow, sFlow, IPFIX, SPAN, RSPAN, and GRE-tunneled mirrors

This article shows how to implement a libvirt network using OVS and configure a VM to use this network.

Install OVS

I am using a system based on Fedora 37 with libvirt and OVS installed. If you don't have OVS available, you can install it with DNF:

# dnf install -y openvswitch

libvirt supports using OVS since version 0.9.11, so if you are using any modern Linux distribution, you can reproduce these steps. In addition, I am using virsh to manipulate libvirt objects and VMs and ovs-vsctl to manipulate OVS bridges. I also assume you have previous experience with libvirt.

Create the virtual switch

Before you can define a libvirt network based on OVS, you need to create a virtual switch in OVS. Virtual switches in OVS are controlled by the daemon ovs-vswichd and usually start through the systemd unit openvswitch:

# systemctl status openvswitch
● openvswitch.service - Open vSwitch
     Loaded: loaded (/usr/lib/systemd/system/openvswitch.service; enabled; preset: disabled)
     Active: active (exited) since Wed 2023-03-01 22:08:07 -03; 19min ago
  Main PID: 1052 (code=exited, status=0/SUCCESS)
       CPU: 2ms

If the daemon is not running, you can run it and enable it to automatically start:

# systemctl enable --now openvswitch

Once the daemon is running you can create a virtual switch:

# ovs-vsctl add-br ovsbr0

Then validate it was created correctly:

# ovs-vsctl show
    Bridge ovsbr0
        Port ovsbr0
            Interface ovsbr0
                type: internal
    ovs_version: "2.17.0"

Now you can start playing with libvirt.

[Cheat sheet: Old Linux commands and their modern replacements ]

Define the libvirt network

Because libvirt uses Linux bridges by default, any virsh subcommand that creates new networks will create a network based on it, so you need to create an XML file with the definition of the OVS-based network and import it to libvirt:

# cat ovs-network.xml
  <forward mode='bridge'/>
  <bridge name='ovsbr0'/>
  <virtualport type='openvswitch'/>

Then import the network in libvirt:

# virsh net-define ovs-network.xml
Network ovs defined from ovs-network.xml

Note: It is possible to use uuidgen to generate a new Universal Unique Identifier (UUID) for the network.

Just defining the network in libvirt is not sufficient for the network to be available. You must start the network and enable auto-start on boot:

# virsh net-start ovs
Network ovs started

# virsh net-autostart ovs
Network ovs marked as autostarted

# virsh net-list
Name    State   Autostart   Persistent
default active  yes         yes
ovs     active  yes         yes

For this example, I already have a VM deployed with the default network. I will edit its configuration to change the network to the ovs network and start the virtual machine:

# virsh list --all
Id  Name    State
-   rhel8   shut off

# virsh edit rhel8
<interface type='network'>
  <mac address='52:54:00:84:c7:4c'/>
  <source network='ovs'/> <----- modify here
  <model type='virtio'/>
    <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>

# virsh start rhel8
Domain 'rhel8' started

# virsh list
Id  Name    State
1   rhel8   running

If everything works, ovs creates a new interface in your virtual switch:

# ovs-vsctl show
    Bridge ovsbr0
        Port ovsbr0
            Interface ovsbr0
                type: internal
        Port vnet0
            Interface vnet0
    ovs_version: "2.17.0"

[ Check out Network automation for everyone, a complimentary book from Red Hat. ]

Configure a VM to use the OVS network

Because my VM was configured to use the default network, now it has no IP address configured. This happens because the VM was configured to use Dynamic Host Configuration Protocol (DHCP), which obtained the IP address from the default network's integration with dnsmasq. For now, configure a static IP address for the VM  by logging into the VM console and running:

# ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
   link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
   inet scope host lo
      valid_lft forever preferred_lft forever
   inet6 ::1/128 scope host
      valid_lft forever preferred_lft forever
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
   link/ether 52:54:00:84:c7:4c brd ff:ff:ff:ff:ff:ff
# ip addr add dev enp1s0
# ip addr show dev enp1s0
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER\_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
   link/ether 52:54:00:84:c7:4c brd ff:ff:ff:ff:ff:ff
   inet scope global enp1s0
      valid_lft forever preferred_lft forever

OVS creates a local interface with the name of the virtual switch you created. You can use this interface to validate that it's possible to communicate with the VM:

# ip addr show ovsbr0
5: ovsbr0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
   link/ether 72:85:87:ed:c8:41 brd ff:ff:ff:ff:ff:ff
# ip addr add dev ovsbr0
# ip link set up dev ovsbr0
# ip addr show ovsbr0
5: ovsbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
   link/ether 72:85:87:ed:c8:41 brd ff:ff:ff:ff:ff:ff
   inet scope global ovsbr0
      valid_lft forever preferred_lft forever
   inet6 fe80::7085:87ff:feed:c841/64 scope link
      valid_lft forever preferred_lft forever

And now the moment of truth:

# ping -c 3
PING ( 56(84) bytes of data.
64 bytes from icmp\_seq=1 ttl=64 time=1.54 ms
64 bytes from icmp\_seq=2 ttl=64 time=0.318 ms
64 bytes from icmp\_seq=3 ttl=64 time=0.320 ms

--- ping statistics ---
3 packets transmitted, 3 received, 0% packet loss

Oh yeah, it works!!!


This is a good start. You've configured a OVS virtual switch and integrated it with libvirt network. For now, you cannot do much with this configuration because the OVS bridge is not connected to the rest of the network.

In my next article, I will show how to integrate the virtual switch with the physical network and use preexisting services, such as DHCP and DNS.

Topics:   Networking   Linux administration   Linux   Virtualization  
Author’s photo

David Silva

I am a Senior Technical Account Manager for Red Hat and have worked in the industry for 20 years. I am passionate about Open Source software and am a Kubernetes enthusiast. I always seek something new to learn and collaborate with others. More about me

Try Red Hat Enterprise Linux

Download it at no charge from the Red Hat Developer program.