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 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.