From: "Rémi PAETA" <remi.paeta@comaite.com>
To: pve-devel@lists.proxmox.com
Subject: [RFC PATCH ve-rs/network/manager/docs 00/16] sdn: add OVN zone and controller plugins
Date: Tue, 13 Oct 2026 14:14:16 +0200 [thread overview]
Message-ID: <20261013121432.2642979-1-remi.paeta@comaite.com> (raw)
In-Reply-To: <1116090294.1714263.1791537779868.JavaMail.zimbra@comaite.com>
This series adds OVN (Open Virtual Network) support to the SDN stack,
as first discussed in:
https://lore.proxmox.com/pve-devel/1116090294.1714263.1791537779868.JavaMail.zimbra@comaite.com/
It covers the first step outlined there: an OVN controller, OVN zones
and vnets, providing a distributed L2 overlay with optional per-port
anti-spoofing. Routing (subnet gateways, SNAT), native DHCP and OVN
ACLs are left for later steps.
Sent as RFC, as some design choices below would benefit from your
feedback before going further.
I am contributing on behalf of my employer, Comaite, whose entity CLA
has been accepted by Proxmox.
Overview
========
- The OVN central components (northbound/southbound databases,
ovn-northd) are deployed by the administrator, ideally as a RAFT
cluster, on the PVE nodes or elsewhere. An "ovn" SDN controller holds
their connection strings, plain TCP or SSL.
- Each vnet of an "ovn" zone is an OVN logical switch. On reload, the
northbound database is synced once for the whole cluster: switches
get created, updated and deleted, ports of removed guests and guest
interfaces get cleaned up. Only objects tagged with pve-sdn-*
external_ids are touched, so the deployment can be shared.
- On every node of an OVN zone, ovn-controller gets configured (remote,
encapsulation, tunnel IP from the route towards the southbound
database). pvenetcommit.service removes the Open vSwitch database on
every boot, so a oneshot pve-sdn-ovn-chassis.service sets it again
before the guests start.
- Guest interfaces are plugged into br-int and bound to a logical
switch port named after them. Each vnet still gets an empty OVS
bridge, which lets qemu-server and pve-container read its MTU and set
up the firewall bridge like for any OVS bridge, so the guest firewall
works unchanged. pve-manager needs no backend change.
- Port security, per zone with a per-vnet override: off (default),
mac, or mac-ip with the IPAM addresses (IPv4 and IPv6). The guest MAC
is set from add_bridge_fdb. A changed level applies on reload,
without restarting guests.
- With SSL, the administrator provides the CA and one certificate per
node in /etc/pve/priv/sdn/ovn/<controller>/. ovn-controller only
takes certificates on its command line, so a runtime drop-in for its
unit adds them; it is only restarted when that setup changes.
- Guest ports are bound to the chassis of their node (requested-chassis
option). On live migration, they are bound to both nodes with
activation-strategy=rarp, and back to the destination only when
qemu-server calls add_bridge_fdb on resume, see below.
Changes since the discussion mail
=================================
- Port security can be overridden per vnet, which adds a
'port-security' option to the common vnet schema (refused on other
zone types, see the sixth network patch).
- The web UI is now part of the series (pve-manager patches).
- The proxmox-ve-rs patch is needed for both firewalls: without it,
proxmox-firewall fails on the whole SDN configuration, and
pve-firewall, through libpve-rs-perl, generates no SDN IPSet at all,
for any zone, as soon as an OVN zone exists. My first mail only
mentioned the nftables firewall.
- SSL connections to the OVN databases.
- Live migration: while QEMU runs on both nodes during the memory copy,
both ovn-controller instances kept claiming the port from each other,
every 500 ms, until the source released it. Measured with a probe
every ~55 ms, this lost 2 to 3 s of traffic, while QEMU reported a
downtime of ~20 ms. Binding the port to both chassis with
activation-strategy=rarp during the migration, as OVN recommends for
this case, and to the destination only on resume (qemu-server already
calls add_bridge_fdb there) brings it down to 0.4 to 0.8 s, close to
the resolution of the probe. This needs no change outside
pve-network; the chassis hostname is set to the node name, as
requested-chassis refers to it and Open vSwitch uses the FQDN.
Dependencies
============
- proxmox-ve-rs (first patch): libpve-rs-perl and proxmox-firewall
need to be rebuilt with it before OVN zones can be used, so
pve-network will likely need Breaks on older versions of both. How
would you prefer to handle this?
- pve-manager depends on a libpve-network-perl knowing the vnet
'port-security' option.
- Hotplugged NICs of VMs only get their MAC and port security on the
next start, as qemu-server does not call add_bridge_fdb on hotplug.
This is the existing bug #6490 for bridges with MAC learning
disabled; I sent a separate fix for it:
https://lore.proxmox.com/pve-devel/20261013095852.2586609-1-remi.paeta@comaite.com/
Known limitations
=================
- No routing, SNAT or DHCP yet: subnet gateways, SNAT and DHCP ranges
are refused on OVN zones, as are VLAN tags, VLAN-aware vnets and
isolated ports.
- With mac-ip, IPv6 link-local traffic is only allowed from the
address derived from the MAC, which breaks guests using stable
privacy link-local addresses for link-local protocols.
- If the IPAM cannot be queried, a reload keeps the current port
addresses, while a starting guest only gets its MAC enforced.
- If the northbound database cannot be reached, running guests keep
their connectivity, but guests on OVN zones cannot be started.
Testing
=======
Unit tests cover the new code (332 tests in pve-network). On a three
node cluster with the OVN databases clustered on the nodes, over SSL:
- VM and container connectivity across nodes, live migration
(0.4 to 0.8 s of loss, QEMU downtime ~20 ms), container migration
- guest firewall with iptables and nftables, ipfilter, macfilter,
firewall toggling on running guests
- port security levels with IPv4 and IPv6, spoofed MAC and IPs blocked
- northbound/southbound leader failover and full outage of all
database members (no loss for running guests)
- node crash, HA recovery and port claim on another node with a stale
binding of the dead node
- Geneve to VXLAN encapsulation switch and back
- zone restricted to nodes, removal of zones and of the controller
- orphaned ports after removing or moving guest interfaces
Design notes
============
- The OVN central components are deployed and operated separately, by
the administrator. Deploying and managing them directly on the
Proxmox VE cluster (for example as a RAFT cluster on some of the
nodes, set up from the API) could be a later evolution.
- The guest start is refused while the northbound database cannot be
reached. Starting the guest without its port would not help: the
southbound database usually runs on the same hosts and is down too,
so no chassis can claim a port, and a guest started elsewhere by HA
could not be claimed anyway, as requested-chassis still names the
previous node and only the northbound database can change it.
- With mac-ip and an unreachable IPAM, a starting guest only gets its
MAC enforced (fail-open). The IPs are looked up through
Ipams::get_ips_from_mac, so the MAC cache answers for known guests,
and this only happens for a MAC the IPAM never returned before. Zones
with DHCP fail the guest start in that case instead (through
add_dhcp_mapping); I kept fail-open here so that an IPAM outage does
not prevent starting guests, but can change it if you prefer.
- Certificates are left to the PKI of the OVN deployment (renewal,
expiry). An API/GUI import could come later, ideally with the key
generated on each node and only the signed certificate imported.
Open questions
==============
1. Each vnet currently gets an empty OVS bridge, while the guest
interfaces are plugged into br-int. It only exists because
qemu-server reads the vnet MTU from /sys/class/net/<vnet>/mtu
(read_bridge_mtu, which also checks the bridges on migration
targets), and to keep the firewall bridge setup of OVS bridges. This
needs no change outside pve-network. Is this acceptable, or would
you prefer one of these alternatives?
a) An SDN dispatcher, for example
PVE::Network::SDN::Zones::get_bridge_mtu(), returning the zone
MTU for OVN vnets and reading sysfs otherwise, called by
qemu-server and pve-container instead of read_bridge_mtu(). No
placeholder interface anymore, but it touches both repositories
(pve-common cannot depend on pve-network), and every place
assuming that a vnet is a local interface would need the same
treatment.
b) A lighter placeholder: a dummy interface named after the vnet
instead of an OVS bridge. Still no change outside pve-network,
and nothing in the Open vSwitch database, but the firewall bridge
setup would need to be checked again.
2. Is extending the common vnet schema with 'port-security' fine? As
vnets have a single section type, I saw no other place for a per-vnet
option.
3. VLAN-aware vnets are out of scope for this series. Guest-tagged
trunks could later use the vlan-passthru option of logical switches,
while per-NIC VLAN tags would need one logical switch per VLAN. Is
either of interest?
4. Not specific to OVN, but noticed while testing it: deleting a vnet
without subnets is not refused while guests still use it, on any
zone type. Running guests end up unplugged and fail on their next
start. Would you take a separate patch refusing the deletion with the
list of guests using the vnet?
ve-rs:
proxmox-ve-config/src/sdn/config.rs | 3 +++
proxmox-ve-config/tests/sdn/main.rs | 28 ++++++++++++++++++++++++++++
2 files changed, 31 insertions(+)
network:
debian/control | 1 +
debian/libpve-network-perl.install | 1 +
debian/libpve-network-perl.postinst | 2 +
src/PVE/API2/Network/SDN.pm | 8 +
src/PVE/API2/Network/SDN/Vnets.pm | 8 +
src/PVE/Network/SDN.pm | 39 +-
src/PVE/Network/SDN/Controllers.pm | 6 +
src/PVE/Network/SDN/Controllers/Makefile | 2 +-
src/PVE/Network/SDN/Controllers/OvnPlugin.pm | 90 ++
src/PVE/Network/SDN/Makefile | 3 +-
src/PVE/Network/SDN/Ovn.pm | 1024 ++++++++++++++++++
src/PVE/Network/SDN/SubnetPlugin.pm | 9 +
src/PVE/Network/SDN/VnetPlugin.pm | 8 +
src/PVE/Network/SDN/Zones.pm | 2 +
src/PVE/Network/SDN/Zones/Makefile | 2 +-
src/PVE/Network/SDN/Zones/OvnPlugin.pm | 184 ++++
src/services/Makefile | 1 +
src/services/pve-sdn-ovn-chassis.service | 16 +
src/test/Makefile | 5 +-
src/test/run_test_ovn.pl | 835 ++++++++++++++
src/test/run_test_vnets_blackbox.pl | 21 +
.../ovn/basic/expected_controller_config | 8 +
.../zones/ovn/basic/expected_sdn_interfaces | 12 +
src/test/zones/ovn/basic/interfaces | 5 +
src/test/zones/ovn/basic/sdn_config | 24 +
.../ovn/with_evpn/expected_controller_config | 44 +
.../ovn/with_evpn/expected_sdn_interfaces | 47 +
src/test/zones/ovn/with_evpn/interfaces | 7 +
src/test/zones/ovn/with_evpn/sdn_config | 35 +
29 files changed, 2444 insertions(+), 5 deletions(-)
manager:
www/manager6/Makefile | 2 +
www/manager6/Utils.js | 10 +++++
www/manager6/form/SDNControllerSelector.js | 9 +++-
www/manager6/sdn/VnetEdit.js | 37 +++++++++++++++-
www/manager6/sdn/controllers/OvnEdit.js | 50 ++++++++++++++++++++++
www/manager6/sdn/zones/OvnEdit.js | 34 +++++++++++++++
6 files changed, 139 insertions(+), 3 deletions(-)
docs:
pvesdn.adoc | 295 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 293 insertions(+), 2 deletions(-)
Summary over all repositories:
38 files changed, 2907 insertions(+), 10 deletions(-)
next prev parent reply other threads:[~2026-10-13 12:23 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-09 9:22 [RFC] sdn: OVN zone and controller plugins Rémi PAETA
2026-10-13 12:14 ` Rémi PAETA [this message]
2026-10-13 12:14 ` [RFC PATCH ve-rs 01/16] ve-config: sdn: add ovn zone type Rémi PAETA
2026-10-13 12:14 ` SPAM: [RFC PATCH network 02/16] sdn: controllers: add ovn plugin Rémi PAETA
2026-10-13 12:14 ` [RFC PATCH network 03/16] sdn: zones: " Rémi PAETA
2026-10-13 12:14 ` [RFC PATCH network 04/16] sdn: ovn: sync the northbound database on reload Rémi PAETA
2026-10-13 12:14 ` [RFC PATCH network 05/16] sdn: ovn: configure the chassis on boot and on network reload Rémi PAETA
2026-10-13 12:14 ` [RFC PATCH network 06/16] sdn: ovn: add port security Rémi PAETA
2026-10-13 12:14 ` [RFC PATCH network 07/16] sdn: ovn: allow overriding the port security per vnet Rémi PAETA
2026-10-13 12:14 ` [RFC PATCH network 08/16] sdn: subnets: refuse gateway, snat and dhcp on ovn zones Rémi PAETA
2026-10-13 12:14 ` [RFC PATCH network 09/16] sdn: ovn: support ssl connections to the databases Rémi PAETA
2026-10-13 12:14 ` [RFC PATCH network 10/16] sdn: ovn: bind guest ports to the chassis of their node Rémi PAETA
2026-10-13 12:14 ` [RFC PATCH manager 11/16] ui: sdn: controller selector: allow filtering by controller type Rémi PAETA
2026-10-13 12:14 ` [RFC PATCH manager 12/16] ui: sdn: add ovn controller Rémi PAETA
2026-10-13 12:14 ` [RFC PATCH manager 13/16] ui: sdn: add ovn zone Rémi PAETA
2026-10-13 12:14 ` [RFC PATCH manager 14/16] ui: sdn: vnet: add port security for ovn zones Rémi PAETA
2026-10-13 12:14 ` [RFC PATCH manager 15/16] ui: sdn: vnet: disable isolate ports " Rémi PAETA
2026-10-13 12:14 ` [RFC PATCH docs 16/16] sdn: document ovn zones and controllers Rémi PAETA
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261013121432.2642979-1-remi.paeta@comaite.com \
--to=remi.paeta@comaite.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox