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
next prev 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