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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.