public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
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(-)




  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
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal