Post

Open vSwitch: Installation, Configuration, and Usage

Open vSwitch (OVS) is a production-quality, open-source multilayer virtual switch designed to enable network automation through programmatic extension while supporting standard management interfaces and protocols such as NetFlow, sFlow, IPFIX, RSPAN, CLI, LACP, and 802.1ag. It is the default virtual switch in many hypervisors, cloud platforms (OpenStack, oVirt), and container networking solutions (Kubernetes with OVN).

Where Linux’s built-in bridge is a simple Layer 2 forwarder, OVS supports VLANs, tunnels (VXLAN, GRE, Geneve), OpenFlow, quality of service, and a centralised control plane — making it the foundation of Software-Defined Networking (SDN) on Linux.

Traditional vs Virtual Switching

In a physical data centre, servers connect to hardware switches. In a virtualised environment, Virtual Machines (VMs) and containers need network connectivity too — but they live entirely inside a host. A virtual switch provides that connectivity in software.

Feature Linux Bridge Open vSwitch
Layer Layer 2 only Layer 2 + partial Layer 3
VLANs Limited Full 802.1Q support
Tunnels No VXLAN, GRE, Geneve, STT
OpenFlow No Full OpenFlow 1.0–1.5
QoS No Ingress policing + egress shaping
Flow monitoring No NetFlow, sFlow, IPFIX
Bond/LACP Basic Full LACP support
SDN controller No Yes (OpenFlow, OVSDB)
Per-flow statistics No Yes
Management protocol None OVSDB (RFC 7047)

Architecture Overview

OVS has three main components:

ovs-vswitchd — the main userspace daemon. Implements the switch logic, communicates with the kernel module, and handles OpenFlow connections.

ovsdb-server — a lightweight database server that stores the OVS configuration (bridges, ports, interfaces, controllers, SSL settings). The database persists across reboots.

Kernel datapath (openvswitch.ko) — a kernel module that performs fast packet forwarding. When a packet matches a cached flow, the kernel handles it entirely without userspace involvement.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌──────────────────────────────────────────────────┐
│                  User Space                      │
│  ┌─────────────────┐    ┌──────────────────────┐ │
│  │  ovsdb-server   │◄──►│   ovs-vswitchd       │ │
│  │  (config DB)    │    │   (switch daemon)    │ │
│  └─────────────────┘    └──────────┬───────────┘ │
│                                    │ Netlink     │
├────────────────────────────────────┼─────────────┤
│                  Kernel Space      │             |
│                          ┌─────────▼───────────┐ │
│                          │  openvswitch.ko     │ │
│                          │  (kernel datapath)  │ │
│                          └─────────────────────┘ │
└──────────────────────────────────────────────────┘

Installing Open vSwitch

Debian / Ubuntu

1
2
3
4
5
6
7
8
9
10
sudo apt update
sudo apt install -y openvswitch-switch openvswitch-common

# Verify the kernel module loaded
sudo modprobe openvswitch
lsmod | grep openvswitch

# Check service status
sudo systemctl status ovs-vswitchd
sudo systemctl status ovsdb-server

RHEL / CentOS / Fedora

1
2
3
4
5
6
7
8
9
10
# Enable the required repository
sudo dnf install -y centos-release-nfv-openvswitch   # CentOS
# or
sudo dnf install -y epel-release                      # Fedora / RHEL

sudo dnf install -y openvswitch
sudo systemctl enable --now openvswitch

# Verify
sudo systemctl status openvswitch

From Source (Latest Version)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Install build dependencies
sudo apt install -y build-essential libssl-dev python3-pip \
    libcap-ng-dev libpcap-dev libnuma-dev

# Download and build
wget https://www.openvswitch.org/releases/openvswitch-3.3.0.tar.gz
tar xf openvswitch-3.3.0.tar.gz
cd openvswitch-3.3.0
./configure --prefix=/usr --localstatedir=/var --sysconfdir=/etc
make -j$(nproc)
sudo make install

# Load the kernel module
sudo modprobe openvswitch

# Start the database and switch
sudo ovsdb-server --remote=punix:/var/run/openvswitch/db.sock \
    --remote=db:Open_vSwitch,Open_vSwitch,manager_options \
    --pidfile --detach
sudo ovs-vsctl --no-wait init
sudo ovs-vswitchd --pidfile --detach

Verify Installation

1
2
3
4
5
ovs-vsctl --version
# ovs-vsctl (Open vSwitch) 3.3.0

sudo ovs-vsctl show
# Shows the current OVS configuration (empty at first)

Core CLI Tools

Command Purpose
ovs-vsctl Configure bridges, ports, interfaces, controllers (talks to ovsdb-server)
ovs-ofctl Manage OpenFlow flows and query switch statistics
ovs-dpctl Manage and inspect the kernel datapath
ovs-appctl Send commands directly to running OVS daemons
ovsdb-client Query the OVSDB directly
ovs-pcap Capture packets on OVS ports

Bridges

A bridge in OVS is equivalent to a physical switch. All ports added to the same bridge can communicate at Layer 2.

Create and Delete a Bridge

1
2
3
4
5
6
7
8
9
10
11
12
# Create a bridge
sudo ovs-vsctl add-br br0

# Verify
sudo ovs-vsctl show
# Bridge br0
#     Port br0
#         Interface br0
#             type: internal

# Delete a bridge
sudo ovs-vsctl del-br br0

Bridge Properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Set a custom MAC address on the bridge
sudo ovs-vsctl set bridge br0 other-config:hwaddr="02:00:00:00:00:01"

# Set the OpenFlow protocol version
sudo ovs-vsctl set bridge br0 protocols=OpenFlow13

# Enable STP (Spanning Tree Protocol)
sudo ovs-vsctl set bridge br0 stp_enable=true

# Enable RSTP (Rapid STP — preferred over STP)
sudo ovs-vsctl set bridge br0 rstp_enable=true

# Disable MAC learning (useful when using an OpenFlow controller)
sudo ovs-vsctl set bridge br0 fail-mode=secure

The fail-mode setting is important for SDN deployments:

  • standalone (default) — acts as a normal learning switch if the controller disconnects
  • secure — drops all traffic if no controller is connected (guarantees the controller always decides)

Ports and Interfaces

A port is a named connection point on a bridge. An interface is the actual network device attached to a port. Most ports have exactly one interface; bond ports have multiple.

Add Physical and Virtual Ports

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Add a physical NIC to the bridge
sudo ovs-vsctl add-port br0 eth1

# Add a tap interface (used by QEMU/KVM virtual machines)
sudo ovs-vsctl add-port br0 tap0

# Add an internal port (a virtual NIC visible to the Linux kernel)
sudo ovs-vsctl add-port br0 vport0 -- set interface vport0 type=internal

# Bring the internal port up and assign an IP
sudo ip link set vport0 up
sudo ip addr add 10.0.0.1/24 dev vport0

# Remove a port
sudo ovs-vsctl del-port br0 eth1

Port Types

Type Description Use Case
system Physical NIC or existing Linux interface Uplink to physical network
internal Virtual NIC managed by OVS, visible to OS Assign IP to bridge itself
tap TUN/TAP device Connect VMs (QEMU/KVM)
patch Connects two OVS bridges directly Multi-bridge topologies
vxlan VXLAN tunnel endpoint Overlay networks
gre GRE tunnel endpoint Site-to-site tunnels
geneve Geneve tunnel endpoint Cloud overlay (preferred over VXLAN)

VLANs

OVS supports full 802.1Q VLAN tagging. Ports can operate in access mode (one VLAN, tag stripped) or trunk mode (multiple VLANs, tags preserved).

Access Port (Single VLAN)

An access port assigns all traffic to one VLAN and strips the tag before delivering to the connected device — the device sees untagged traffic:

1
2
3
4
5
# Put eth1 into VLAN 10 (access mode)
sudo ovs-vsctl set port eth1 tag=10

# Verify
sudo ovs-vsctl list port eth1

Trunk Port (Multiple VLANs)

A trunk port carries tagged traffic for multiple VLANs — typically used for uplinks to physical switches or between bridges:

1
2
3
4
5
6
7
8
9
# Allow VLANs 10, 20, and 30 on eth2
sudo ovs-vsctl set port eth2 trunks=10,20,30

# Allow all VLANs (empty trunks list = all)
sudo ovs-vsctl set port eth2 trunks=[]

# Remove VLAN configuration (return to untagged)
sudo ovs-vsctl clear port eth2 tag
sudo ovs-vsctl clear port eth2 trunks

Native VLAN on a Trunk Port

1
2
3
# Tag=10 on a trunk means untagged incoming traffic is assigned VLAN 10
# while tagged frames for other VLANs pass through
sudo ovs-vsctl set port eth2 tag=10 trunks=10,20,30

Full VLAN Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Create bridge
sudo ovs-vsctl add-br br0

# Uplink (trunk: all VLANs)
sudo ovs-vsctl add-port br0 eth0
sudo ovs-vsctl set port eth0 trunks=[]

# VM port — VLAN 10
sudo ovs-vsctl add-port br0 tap0
sudo ovs-vsctl set port tap0 tag=10

# VM port — VLAN 20
sudo ovs-vsctl add-port br0 tap1
sudo ovs-vsctl set port tap1 tag=20

# Management IP on the bridge itself (VLAN 10)
sudo ovs-vsctl add-port br0 mgmt -- set interface mgmt type=internal
sudo ovs-vsctl set port mgmt tag=10
sudo ip link set mgmt up
sudo ip addr add 10.10.10.1/24 dev mgmt

Tunnel Ports (Overlay Networks)

Tunnels allow OVS switches on different physical hosts to act as a single logical switch, enabling VM or container migration across hosts with no IP changes.

VXLAN Tunnel

VXLAN (Virtual Extensible LAN) encapsulates Layer 2 frames in UDP packets, allowing overlay networks across Layer 3 boundaries.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# On Host A (10.0.0.1):
sudo ovs-vsctl add-br br-tun
sudo ovs-vsctl add-port br-tun vxlan0 \
    -- set interface vxlan0 type=vxlan \
    options:remote_ip=10.0.0.2 \
    options:key=100 \
    options:dst_port=4789

# On Host B (10.0.0.2):
sudo ovs-vsctl add-br br-tun
sudo ovs-vsctl add-port br-tun vxlan0 \
    -- set interface vxlan0 type=vxlan \
    options:remote_ip=10.0.0.1 \
    options:key=100 \
    options:dst_port=4789

options:key is the VNI (VXLAN Network Identifier) — the segment ID that separates different overlay networks (like a VLAN ID for overlays, supports up to 16 million values).

GRE Tunnel

1
2
3
4
5
6
7
8
9
10
11
# On Host A:
sudo ovs-vsctl add-port br-tun gre0 \
    -- set interface gre0 type=gre \
    options:remote_ip=10.0.0.2 \
    options:key=1000

# On Host B:
sudo ovs-vsctl add-port br-tun gre0 \
    -- set interface gre0 type=gre \
    options:remote_ip=10.0.0.1 \
    options:key=1000

Geneve is more flexible than VXLAN — it supports variable-length metadata headers, making it the preferred tunnel type for OVN (Open Virtual Network):

1
2
3
4
sudo ovs-vsctl add-port br-tun geneve0 \
    -- set interface geneve0 type=geneve \
    options:remote_ip=10.0.0.2 \
    options:key=100

Patch Ports: Connecting Two Bridges

Patch ports create a direct virtual wire between two OVS bridges on the same host — useful for separating integration and tunnel bridges (as OpenStack does):

1
2
3
4
5
6
7
8
9
10
11
12
# Create two bridges
sudo ovs-vsctl add-br br-int
sudo ovs-vsctl add-br br-tun

# Connect them with a patch pair
sudo ovs-vsctl add-port br-int patch-to-tun \
    -- set interface patch-to-tun type=patch \
    options:peer=patch-to-int

sudo ovs-vsctl add-port br-tun patch-to-int \
    -- set interface patch-to-int type=patch \
    options:peer=patch-to-tun

Traffic entering patch-to-tun on br-int exits patch-to-int on br-tun, and vice versa.


OpenFlow and Flow Tables

OVS is a fully programmable OpenFlow switch. Flows are match-action rules stored in flow tables. When a packet arrives, OVS matches it against flows in table 0, then possibly subsequent tables, and executes the matching action.

Flow Syntax

1
priority=<n>, <match-fields>, actions=<actions>

Higher priority flows match first. If no flow matches, the table-miss flow runs (default: drop or send to controller).

View Flows

1
2
3
sudo ovs-ofctl dump-flows br0         # All flows
sudo ovs-ofctl dump-flows br0 table=0 # Specific table
sudo ovs-ofctl dump-flows br0 "in_port=1"  # Filter by field

Add Flows

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Drop all traffic (priority 0 catch-all)
sudo ovs-ofctl add-flow br0 "priority=0,actions=drop"

# Forward all traffic to the controller
sudo ovs-ofctl add-flow br0 "priority=0,actions=controller"

# Forward traffic from port 1 to port 2
sudo ovs-ofctl add-flow br0 \
    "priority=100,in_port=1,actions=output:2"

# Forward based on destination MAC
sudo ovs-ofctl add-flow br0 \
    "priority=200,dl_dst=00:11:22:33:44:55,actions=output:3"

# Match on IP and forward
sudo ovs-ofctl add-flow br0 \
    "priority=300,ip,nw_dst=10.0.0.5,actions=output:4"

# Drop traffic from a specific MAC
sudo ovs-ofctl add-flow br0 \
    "priority=500,dl_src=aa:bb:cc:dd:ee:ff,actions=drop"

# VLAN tagging: push VLAN 100, forward to port 2
sudo ovs-ofctl add-flow br0 \
    "priority=200,in_port=1,actions=push_vlan:0x8100,set_field:4196->vlan_vid,output:2"

# Send a copy to a monitoring port (mirroring)
sudo ovs-ofctl add-flow br0 \
    "priority=100,actions=output:5,output:NORMAL"

Delete Flows

1
2
3
sudo ovs-ofctl del-flows br0                  # Delete all flows
sudo ovs-ofctl del-flows br0 "in_port=1"      # Delete flows matching a field
sudo ovs-ofctl del-flows br0 "table=1"        # Delete flows in table 1

Flow Match Fields Reference

Field Matches On
in_port Input port number
dl_src / dl_dst Source / destination MAC
dl_type Ethernet type (0x0800=IPv4, 0x0806=ARP, 0x86dd=IPv6)
dl_vlan VLAN ID
nw_src / nw_dst Source / destination IP
nw_proto IP protocol (6=TCP, 17=UDP, 1=ICMP)
tp_src / tp_dst TCP/UDP source / destination port
metadata Pipeline metadata
reg0reg7 32-bit registers (scratch space)

Port Mirroring

Port mirroring copies traffic from one or more ports to a monitoring port — useful for network analysis and IDS/IPS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Mirror all traffic on port eth1 to port mirror0
sudo ovs-vsctl add-br br0
sudo ovs-vsctl add-port br0 eth1
sudo ovs-vsctl add-port br0 mirror0 -- set interface mirror0 type=internal

sudo ovs-vsctl -- --id=@m create mirror name=mon \
    select-all=true \
    output-port=mirror0 \
    -- set bridge br0 mirrors=@m

# Verify
sudo ovs-vsctl list mirror

# Remove mirror
sudo ovs-vsctl clear bridge br0 mirrors

QoS: Rate Limiting and Traffic Shaping

OVS supports ingress policing (rate limiting inbound traffic) and egress queuing (shaping outbound traffic):

Ingress Policing (Rate Limit Inbound)

1
2
3
4
5
6
7
8
9
10
# Limit inbound traffic on tap0 to 10 Mbps with 1 MB burst
sudo ovs-vsctl set interface tap0 \
    ingress_policing_rate=10000 \
    ingress_policing_burst=1000
# Units: rate in kbps, burst in kb

# Remove rate limit
sudo ovs-vsctl set interface tap0 \
    ingress_policing_rate=0 \
    ingress_policing_burst=0

Egress Queuing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Create a QoS policy with a 100 Mbps max rate and two queues
sudo ovs-vsctl set port eth1 qos=@qos -- \
    --id=@qos create qos type=linux-htb \
    other-config:max-rate=100000000 \
    queues:0=@q0 queues:1=@q1 -- \
    --id=@q0 create queue other-config:max-rate=90000000 \
                           other-config:min-rate=10000000 -- \
    --id=@q1 create queue other-config:max-rate=10000000

# Direct flows into queues via OpenFlow actions
sudo ovs-ofctl add-flow br0 \
    "priority=100,ip,nw_tos=0x10,actions=set_queue:0,normal"
sudo ovs-ofctl add-flow br0 \
    "priority=100,ip,actions=set_queue:1,normal"

Connecting an OpenFlow Controller

OVS can be managed by an external SDN controller (OpenDaylight, ONOS, Ryu, Faucet) via the OpenFlow protocol:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Connect to a controller at 192.168.1.10 port 6633
sudo ovs-vsctl set-controller br0 tcp:192.168.1.10:6633

# Connect to multiple controllers (active-passive failover)
sudo ovs-vsctl set-controller br0 \
    tcp:192.168.1.10:6633 \
    tcp:192.168.1.11:6633

# Use in-band or out-of-band control
sudo ovs-vsctl set bridge br0 controller-conn-mode=out-of-band

# Remove controller
sudo ovs-vsctl del-controller br0

# View controller connection status
sudo ovs-vsctl get-controller br0
sudo ovs-ofctl show br0

Monitoring and Statistics

Interface Statistics

1
2
3
4
5
6
# Per-interface packet and byte counters
sudo ovs-vsctl get interface eth1 statistics

# Or via ofctl
sudo ovs-ofctl dump-ports br0
sudo ovs-ofctl dump-ports br0 eth1

Flow Statistics

1
2
3
4
5
# Show flows with packet and byte counts
sudo ovs-ofctl dump-flows br0

# Show aggregate statistics
sudo ovs-ofctl dump-aggregate br0

Datapath Statistics

1
2
3
4
5
6
# Kernel datapath hit/miss counters
sudo ovs-dpctl show
sudo ovs-dpctl dump-flows

# Cache performance (upcall rate indicates controller load)
sudo ovs-appctl dpif/show

Live Debugging

1
2
3
4
5
6
7
8
9
# Enable verbose logging for a component
sudo ovs-appctl vlog/set ovs-vswitchd:file:dbg

# Trace a specific packet through the pipeline
sudo ovs-appctl ofproto/trace br0 \
    "in_port=1,dl_src=00:11:22:33:44:55,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x0800"

# Watch flows being installed/removed in real time
sudo ovs-ofctl monitor br0 watch:

The ofproto/trace command is one of the most powerful OVS debugging tools — it shows exactly which flow table entries a packet would match and what actions would be applied, without sending any real traffic.


Practical Example: KVM Hypervisor Network

A common OVS deployment connects KVM virtual machines to the physical network with VLAN separation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 1. Create the integration bridge
sudo ovs-vsctl add-br br-int

# 2. Add the physical uplink as a trunk (all VLANs)
sudo ovs-vsctl add-port br-int eth0
sudo ovs-vsctl set port eth0 trunks=[]

# 3. Add a management IP on VLAN 1 (untagged)
sudo ovs-vsctl add-port br-int mgmt -- set interface mgmt type=internal
sudo ovs-vsctl set port mgmt tag=1
sudo ip link set mgmt up
sudo ip addr add 192.168.1.10/24 dev mgmt
sudo ip route add default via 192.168.1.1

# 4. When QEMU creates tap0 for VM1, attach it to VLAN 100
sudo ovs-vsctl add-port br-int tap0
sudo ovs-vsctl set port tap0 tag=100

# 5. Attach VM2's tap1 to VLAN 200
sudo ovs-vsctl add-port br-int tap1
sudo ovs-vsctl set port tap1 tag=200

# 6. Verify the full configuration
sudo ovs-vsctl show

Persisting Configuration

OVS configuration in the OVSDB persists automatically across reboots — no need to write startup scripts for bridge and port definitions. However, Linux IP addresses assigned to internal ports (like mgmt) are not persisted by OVS itself.

Persist IP on an Internal Port (Netplan)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# /etc/netplan/01-ovs.yaml
network:
  version: 2
  openvswitch:
    bridges:
      br-int:
        interfaces: [eth0]
  ethernets:
    eth0: {}
    mgmt:
      addresses:
        - 192.168.1.10/24
      routes:
        - to: default
          via: 192.168.1.1
1
sudo netplan apply

Common Issues and Solutions

Problem Cause Solution
ovs-vsctl: unix:/var/run/openvswitch/db.sock: database connection failed ovsdb-server not running sudo systemctl start ovsdb-server
Port added but no traffic Physical NIC in bridge while also having an IP Move the IP to an internal OVS port; leave physical NIC IP-free
VM loses connectivity after migration MAC not in OVS’s MAC table OVS floods briefly then learns; send gratuitous ARP from VM
VXLAN tunnel not passing traffic Firewall blocking UDP 4789 sudo firewall-cmd --add-port=4789/udp --permanent
High CPU from ovs-vswitchd Too many upcalls (flow cache misses) Tune other-config:max-idle and review wildcard flows
Flows not matching as expected Priority order or field mismatch Use ovs-appctl ofproto/trace to step through the pipeline
Bridge disappears after reboot OVS service not starting sudo systemctl enable openvswitch

Quick Reference

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# Bridge management
ovs-vsctl add-br <bridge>
ovs-vsctl del-br <bridge>
ovs-vsctl show

# Port management
ovs-vsctl add-port <bridge> <port>
ovs-vsctl del-port <bridge> <port>
ovs-vsctl list-ports <bridge>

# VLAN
ovs-vsctl set port <port> tag=<vlan>          # Access
ovs-vsctl set port <port> trunks=10,20,30     # Trunk

# Tunnel
ovs-vsctl add-port <br> <name> -- set interface <name> \
    type=vxlan options:remote_ip=<ip> options:key=<vni>

# Patch ports
ovs-vsctl add-port br-a pa -- set interface pa type=patch options:peer=pb
ovs-vsctl add-port br-b pb -- set interface pb type=patch options:peer=pa

# OpenFlow flows
ovs-ofctl dump-flows <bridge>
ovs-ofctl add-flow <bridge> "<match>,actions=<action>"
ovs-ofctl del-flows <bridge>

# Controller
ovs-vsctl set-controller <bridge> tcp:<ip>:<port>
ovs-vsctl del-controller <bridge>

# Diagnostics
ovs-appctl ofproto/trace <bridge> "<packet-fields>"
ovs-ofctl dump-ports <bridge>
ovs-dpctl show
ovs-vsctl get interface <port> statistics

Conclusion

Open vSwitch bridges the gap between simple Linux bridging and full hardware switch functionality — all in software, all programmable. The OVSDB gives you a persistent, queryable configuration store. OpenFlow gives you complete control over packet forwarding logic. And the tunnel support (VXLAN, GRE, Geneve) gives you the overlay networking foundation that modern cloud platforms like OpenStack, oVirt, and Kubernetes with OVN depend on.

The natural progression from here is OVN (Open Virtual Network) — a higher-level abstraction layer built on top of OVS that adds logical switches, logical routers, ACLs, and NAT, managed through a distributed control plane. OVN is the default networking backend for OpenStack with ML2/OVN and for Kubernetes with the OVN-Kubernetes CNI plugin.

Additional Resources


This post is licensed under CC BY 4.0 by the author.