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 network 07/16] sdn: ovn: allow overriding the port security per vnet
Date: Tue, 13 Oct 2026 14:14:23 +0200	[thread overview]
Message-ID: <20261013121432.2642979-8-remi.paeta@comaite.com> (raw)
In-Reply-To: <20261013121432.2642979-1-remi.paeta@comaite.com>

The port security level needed depends on the network rather than on
the zone: a vnet for regular guests can enforce their IPAM addresses,
while another one in the same zone carries a Kubernetes cluster or VRRP
virtual addresses and needs no filtering.

Add a 'port-security' vnet option overriding the default of the zone.
It is refused on vnets of other zone types, which do not filter the
guest traffic.

Signed-off-by: Rémi PAETA <remi.paeta@comaite.com>
---
 src/PVE/API2/Network/SDN/Vnets.pm      |  8 +++++++
 src/PVE/Network/SDN/Ovn.pm             | 15 ++++++++++++-
 src/PVE/Network/SDN/VnetPlugin.pm      |  8 +++++++
 src/PVE/Network/SDN/Zones/OvnPlugin.pm |  8 +++----
 src/test/run_test_ovn.pl               | 29 ++++++++++++++++++++++++++
 src/test/run_test_vnets_blackbox.pl    | 21 +++++++++++++++++++
 6 files changed, 84 insertions(+), 5 deletions(-)

diff --git a/src/PVE/API2/Network/SDN/Vnets.pm b/src/PVE/API2/Network/SDN/Vnets.pm
index 3f1cb1b..b753463 100644
--- a/src/PVE/API2/Network/SDN/Vnets.pm
+++ b/src/PVE/API2/Network/SDN/Vnets.pm
@@ -325,6 +325,10 @@ __PACKAGE__->register_method({
                 my $zone_scfg = PVE::Network::SDN::Zones::sdn_zones_config($zone_cfg, $zoneid);
                 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($zone_scfg->{type});
                 $plugin->vnet_update_hook($cfg, $id, $zone_cfg);
+                my $port_security = $cfg->{ids}->{$id}->{'port-security'};
+                if (defined($port_security) && $zone_scfg->{type} ne 'ovn') {
+                    raise_param_exc({ 'port-security' => "only supported on OVN zones" });
+                }
 
                 PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg);
 
@@ -401,6 +405,10 @@ __PACKAGE__->register_method({
                 my $zone_scfg = PVE::Network::SDN::Zones::sdn_zones_config($zone_cfg, $zoneid);
                 my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($zone_scfg->{type});
                 $plugin->vnet_update_hook($cfg, $id, $zone_cfg);
+                my $port_security = $cfg->{ids}->{$id}->{'port-security'};
+                if (defined($port_security) && $zone_scfg->{type} ne 'ovn') {
+                    raise_param_exc({ 'port-security' => "only supported on OVN zones" });
+                }
 
                 PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg);
 
diff --git a/src/PVE/Network/SDN/Ovn.pm b/src/PVE/Network/SDN/Ovn.pm
index 54d9c58..cdedc4a 100644
--- a/src/PVE/Network/SDN/Ovn.pm
+++ b/src/PVE/Network/SDN/Ovn.pm
@@ -77,7 +77,7 @@ sub desired_state {
         next if !$controllerid || !$state->{$controllerid};
 
         $state->{$controllerid}->{switches}->{$vnetid} = {
-            'port-security' => $zone->{'port-security'} // 'off',
+            'port-security' => port_security_level($zone, $vnets->{$vnetid}),
             external_ids => {
                 $EXT_CONTROLLER => $controllerid,
                 $EXT_ZONE => $zoneid,
@@ -464,6 +464,19 @@ sub port_commands {
     return $commands;
 }
 
+=head3 port_security_level(\%zone, \%vnet)
+
+Returns the port security level of the guest ports on C<\%vnet>: the one of the
+vnet if set, else the one of its zone C<\%zone>.
+
+=cut
+
+sub port_security_level {
+    my ($zone, $vnet) = @_;
+
+    return $vnet->{'port-security'} // $zone->{'port-security'} // 'off';
+}
+
 =head3 port_addresses($level, $mac, \@ips)
 
 Returns the addresses and the port security entries of a port for the guest MAC
diff --git a/src/PVE/Network/SDN/VnetPlugin.pm b/src/PVE/Network/SDN/VnetPlugin.pm
index e041575..bf079bd 100644
--- a/src/PVE/Network/SDN/VnetPlugin.pm
+++ b/src/PVE/Network/SDN/VnetPlugin.pm
@@ -83,6 +83,13 @@ sub properties {
                 "If true, sets the isolated property for all interfaces on the bridge of this VNet.",
             optional => 1,
         },
+        'port-security' => {
+            type => 'string',
+            description => "Port security level of the guest interfaces (OVN zones only),"
+                . " overriding the one of the zone.",
+            enum => ['off', 'mac', 'mac-ip'],
+            optional => 1,
+        },
     };
 }
 
@@ -93,6 +100,7 @@ sub options {
         alias => { optional => 1 },
         vlanaware => { optional => 1 },
         'isolate-ports' => { optional => 1 },
+        'port-security' => { optional => 1 },
     };
 }
 
diff --git a/src/PVE/Network/SDN/Zones/OvnPlugin.pm b/src/PVE/Network/SDN/Zones/OvnPlugin.pm
index b25e70c..83c2c51 100644
--- a/src/PVE/Network/SDN/Zones/OvnPlugin.pm
+++ b/src/PVE/Network/SDN/Zones/OvnPlugin.pm
@@ -21,9 +21,9 @@ sub properties {
     return {
         'port-security' => {
             description =>
-                "Filtering of the traffic of the guests: 'mac' only lets them send with"
-                . " their own MAC, 'mac-ip' additionally restricts them to their IPs from the"
-                . " IPAM.",
+                "Default filtering of the traffic of the guests, which vnets can override:"
+                . " 'mac' only lets them send with their own MAC, 'mac-ip' additionally"
+                . " restricts them to their IPs from the IPAM.",
             type => 'string',
             enum => ['off', 'mac', 'mac-ip'],
             default => 'off',
@@ -131,7 +131,7 @@ sub add_bridge_fdb {
         PVE::Network::SDN::Ovn::update_port(
             $controller,
             $iface,
-            $zone->{'port-security'} // 'off',
+            PVE::Network::SDN::Ovn::port_security_level($zone, $vnet),
             $running_config,
             $vnetid,
             $macaddr,
diff --git a/src/test/run_test_ovn.pl b/src/test/run_test_ovn.pl
index 3e97a92..9f191b3 100755
--- a/src/test/run_test_ovn.pl
+++ b/src/test/run_test_ovn.pl
@@ -316,6 +316,35 @@ is_deeply(
 eval { PVE::Network::SDN::Ovn::port_addresses('strict', $mac, []) };
 like($@, qr/unknown port security level 'strict'/, 'port_addresses: invalid level');
 
+is(
+    PVE::Network::SDN::Ovn::port_security_level({ 'port-security' => 'mac' }, {}),
+    'mac',
+    'port_security_level: zone default',
+);
+is(
+    PVE::Network::SDN::Ovn::port_security_level(
+        { 'port-security' => 'mac' },
+        { 'port-security' => 'off' },
+    ),
+    'off',
+    'port_security_level: vnet overrides zone',
+);
+is(PVE::Network::SDN::Ovn::port_security_level({}, {}), 'off', 'port_security_level: default off');
+
+my $override_cfg = {
+    controllers => $running_cfg->{controllers},
+    zones => $running_cfg->{zones},
+    vnets => {
+        ids => {
+            vnet1 => { type => 'vnet', zone => 'zone1', 'port-security' => 'mac' },
+            vnet2 => { type => 'vnet', zone => 'zone2' },
+        },
+    },
+};
+my $override = PVE::Network::SDN::Ovn::desired_state($override_cfg)->{ovnctl}->{switches};
+is($override->{vnet1}->{'port-security'}, 'mac', 'desired_state: vnet level overrides zone');
+is($override->{vnet2}->{'port-security'}, 'mac-ip', 'desired_state: zone level as default');
+
 my $sec_switches = [
     { name => 'vnet1', external_ids => ext('zone1', 'vnet1') },
     { name => 'vnet2', external_ids => ext('zone2', 'vnet2') },
diff --git a/src/test/run_test_vnets_blackbox.pl b/src/test/run_test_vnets_blackbox.pl
index 9f4c424..af329ea 100755
--- a/src/test/run_test_vnets_blackbox.pl
+++ b/src/test/run_test_vnets_blackbox.pl
@@ -421,6 +421,27 @@ sub test_without_subnet {
 }
 run_test(\&test_without_subnet);
 
+sub test_port_security_needs_ovn_zone {
+    my $test_name = (split(/::/, (caller(0))[3]))[-1];
+
+    create_zone({
+        type => "simple",
+        ipam => "pve",
+        zone => "TESTZONE",
+    });
+
+    eval {
+        create_vnet({
+            type => "vnet",
+            zone => "TESTZONE",
+            vnet => "testvnet",
+            'port-security' => "mac",
+        });
+    };
+    like($@, qr/port-security/, "$test_name: refused on a simple zone");
+}
+run_test(\&test_port_security_needs_ovn_zone);
+
 sub test_nic_join {
     my ($test_name, $subnets) = @_;
 
-- 
2.47.3




  parent reply	other threads:[~2026-10-13 12:22 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 ` [RFC PATCH ve-rs/network/manager/docs 00/16] sdn: add " Rémi PAETA
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   ` Rémi PAETA [this message]
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-8-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