From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [45.144.208.40]) by lore.proxmox.com (Postfix) with ESMTPS id 50C3F1FF0E3 for ; Tue, 27 Oct 2026 10:00:20 +0100 (CET) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id D4435219D1; Tue, 27 Oct 2026 09:56:37 +0100 (CET) From: Hannes Laimer To: pve-devel@lists.proxmox.com Subject: [PATCH pve-network v3 28/45] sdn: microseg: expose protocol, port and macro rule options Date: Tue, 27 Oct 2026 09:55:10 +0100 Message-ID: <20261027085527.702738-29-h.laimer@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20261027085527.702738-1-h.laimer@proxmox.com> References: <20261027085527.702738-1-h.laimer@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1793091363871 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.653 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) KAM_MAILER 2 Automated Mailer Tag Left in Email RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: SBZHFOHVSHLPJHNL2S6OWUXWYBLFIQEP X-Message-ID-Hash: SBZHFOHVSHLPJHNL2S6OWUXWYBLFIQEP X-MailFrom: h.laimer@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: The options take the values of the firewall rule options of the same name. The config library checks them with its firewall parsers, so the schema here only limits their length. The options are part of what a rule's id is derived from, so the same groups can carry one rule per narrowing. As with the groups, changing them means replacing the rule. At the same priority a rule with a protocol or macro is checked ahead of one without, so the priorities alone do not show the evaluation order. The overview reports each rule's place in it and each assignment's place in the match order. Signed-off-by: Hannes Laimer --- src/PVE/API2/Network/SDN/Microseg.pm | 10 +++++ src/PVE/API2/Network/SDN/Microseg/Rule.pm | 6 +-- src/PVE/Network/SDN/Microseg.pm | 51 +++++++++++++++++++++-- 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/src/PVE/API2/Network/SDN/Microseg.pm b/src/PVE/API2/Network/SDN/Microseg.pm index 525fa429ae50..5d4062f7db30 100644 --- a/src/PVE/API2/Network/SDN/Microseg.pm +++ b/src/PVE/API2/Network/SDN/Microseg.pm @@ -124,6 +124,14 @@ __PACKAGE__->register_method({ }, }, }, + rank => { + type => 'integer', + optional => 1, + description => + "The object's place in the order its kind is looked at, rules" + . ' in evaluation order and assignments in match order. The' + . ' first has the lowest number.', + }, $OBJECT_PROPERTIES->%*, }, }, @@ -197,9 +205,11 @@ __PACKAGE__->register_method({ my $members = $config->assigned_members( PVE::Network::SDN::Microseg::audited_guest_inventory(), ); + my $rank = { $config->rule_order()->%*, $config->assignment_order()->%* }; for my $object (@$objects) { my $id = $object->{id}; $object->{members} = $members->{$id} if defined($members->{$id}); + $object->{rank} = $rank->{$id} if defined($rank->{$id}); } my $registry = ($running_cfg->{microseg} // {})->{identities}; diff --git a/src/PVE/API2/Network/SDN/Microseg/Rule.pm b/src/PVE/API2/Network/SDN/Microseg/Rule.pm index 575ca1286ed9..ed34d83b2c5c 100644 --- a/src/PVE/API2/Network/SDN/Microseg/Rule.pm +++ b/src/PVE/API2/Network/SDN/Microseg/Rule.pm @@ -12,9 +12,9 @@ __PACKAGE__->register_handlers({ types => ['rule'], name => 'rule', properties => \&PVE::Network::SDN::Microseg::rule_properties, - create_details => "The id is derived from the src and dst predicates and from the" - . " direction. Creating the same rule again is refused as a duplicate. Change an existing" - . " rule's priority or verdict by updating it rather than creating a second rule.", + create_details => "The id is derived from what the rule matches, so creating the same rule" + . " again is refused as a duplicate. Change an existing rule's priority or verdict by" + . " updating it rather than creating a second rule.", generated_id => 1, generated_id_description => "The generated rule id.", }); diff --git a/src/PVE/Network/SDN/Microseg.pm b/src/PVE/Network/SDN/Microseg.pm index bdfde447b320..c5023c17b1d9 100644 --- a/src/PVE/Network/SDN/Microseg.pm +++ b/src/PVE/Network/SDN/Microseg.pm @@ -128,13 +128,55 @@ sub rule_properties { optional => 1, ($update ? () : (default => 0)), description => "Priority. The highest-priority matching rule wins." - . " At a tie, a deny overrides an allow.", + . " At a tie, a deny overrides an allow." + . " A narrowed rule is checked ahead of an unnarrowed rule at the same priority.", }, comment => $COMMENT_PROPERTY, }; # the id is derived from these if (!$update) { + my $port_descr = + "A service name or number (0-65535) as defined in '/etc/services', a range such" + . " as '80:85', or a comma-separated list of those. Requires a protocol with ports."; + $properties->{proto} = { + type => 'string', + minLength => 1, + maxLength => 32, + optional => 1, + description => "Restrict the rule to an IP protocol, a name or number from" + . " '/etc/protocols'. Without a protocol or macro the rule decides all traffic" + . " between the matched interfaces.", + }; + $properties->{sport} = { + type => 'string', + minLength => 1, + maxLength => 128, + optional => 1, + description => "Restrict the source port. $port_descr", + }; + $properties->{dport} = { + type => 'string', + minLength => 1, + maxLength => 128, + optional => 1, + description => "Restrict the destination port. $port_descr", + }; + $properties->{'icmp-type'} = { + type => 'string', + minLength => 1, + maxLength => 64, + optional => 1, + description => "Restrict the ICMP type. Only with an ICMP protocol.", + }; + $properties->{macro} = { + type => 'string', + minLength => 1, + maxLength => 128, + optional => 1, + description => "A firewall macro standing in for the protocol, ports and ICMP type." + . " Cannot be combined with them.", + }; $properties->{src} = { type => 'array', items => get_standard_option('pve-sdn-microseg-id'), @@ -174,9 +216,10 @@ sub rule_properties { type => 'boolean', optional => 1, default => 0, - description => "Also apply the rule with the source and destination predicates" - . " swapped. It is exactly that pair of rules and adds no connection tracking." - . " Not with the 'untagged' group.", + description => "Also apply the rule with the source and destination predicates and" + . " ports swapped. It is exactly that pair of rules and adds no connection" + . " tracking. Not with the 'untagged' group or an ICMP type, including one a macro" + . " expands to.", }; } else { $properties->{delete} = delete_property('comment'); -- 2.47.3