From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 9962A1FF17E for ; Thu, 30 Oct 2025 16:51:30 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id DA15527C83; Thu, 30 Oct 2025 16:49:38 +0100 (CET) From: Stefan Hanreich To: pve-devel@lists.proxmox.com Date: Thu, 30 Oct 2025 16:48:32 +0100 Message-ID: <20251030154851.540408-26-s.hanreich@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20251030154851.540408-1-s.hanreich@proxmox.com> References: <20251030154851.540408-1-s.hanreich@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.183 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment KAM_LAZY_DOMAIN_SECURITY 1 Sending domain does not have any anti-forgery methods RDNS_NONE 0.793 Delivered to internal network by a host with no rDNS SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_NONE 0.001 SPF: sender does not publish an SPF Record Subject: [pve-devel] [PATCH pve-network 7/9] api: nodes: zones: add bridge status X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox VE development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" This endpoint returns an overview of all ports that are currently members of a bridge (the running state). Additionally it provides information about the configured VLANs on VLAN-aware bridges. If the special zone name 'localnetwork' is used, then this endpoint returns the information for all bridges that are configured outside of SDN via /etc/network/interfaces. Signed-off-by: Stefan Hanreich --- src/PVE/API2/Network/SDN/Nodes/Zone.pm | 173 +++++++++++++++++++++++++ 1 file changed, 173 insertions(+) diff --git a/src/PVE/API2/Network/SDN/Nodes/Zone.pm b/src/PVE/API2/Network/SDN/Nodes/Zone.pm index 1e963fc..d7312df 100644 --- a/src/PVE/API2/Network/SDN/Nodes/Zone.pm +++ b/src/PVE/API2/Network/SDN/Nodes/Zone.pm @@ -124,4 +124,177 @@ __PACKAGE__->register_method({ }, }); +__PACKAGE__->register_method({ + name => 'bridges', + path => 'bridges', + proxyto => 'node', + method => 'GET', + protected => 1, + description => + "Get a list of all bridges (vnets) that are part of a zone, as well as the ports that are members of that bridge.", + permissions => { + check => ['perm', '/sdn/zones/{zone}', ['SDN.Audit']], + }, + parameters => { + additionalProperties => 0, + properties => { + zone => { + type => 'string', + description => 'zone name or "localnetwork"', + }, + node => get_standard_option('pve-node'), + }, + }, + returns => { + type => 'array', + items => { + description => 'List of bridges contained in the SDN zone.', + type => 'object', + properties => { + name => { + description => 'Name of the bridge.', + type => 'string', + }, + vlan_filtering => { + description => + 'Whether VLAN filtering is enabled for this bridge (= VLAN-aware).', + type => 'string', + }, + ports => { + description => 'All ports that are members of the bridge', + type => 'array', + items => { + description => 'Information about bridge ports.', + type => 'object', + properties => { + name => { + description => 'The name of the bridge port.', + type => 'string', + }, + vmid => { + description => + 'The ID of the guest that this interface belongs to.', + type => 'number', + optional => 1, + }, + index => { + description => + 'The index of the guests network device that this interface belongs to.', + type => 'number', + optional => 1, + }, + primary_vlan => { + description => + 'The primary VLAN configured for the port of this bridge (= PVID). Only for VLAN-aware bridges.', + type => 'number', + optional => 1, + }, + vlans => { + description => + 'A list of VLANs and VLAN ranges that are allowed for this bridge port in addition to the primary VLAN. Only for VLAN-aware bridges.', + type => 'array', + items => { + description => + 'A single VLAN (123) or a VLAN range (234-435).', + type => 'string', + }, + optional => 1, + }, + }, + }, + }, + }, + }, + }, + code => sub { + my ($param) = @_; + + my $zone_id = extract_param($param, 'zone'); + my $rpcenv = PVE::RPCEnvironment::get(); + my $authuser = $rpcenv->get_user(); + + my @bridges_in_zone; + if ($zone_id eq 'localnetwork') { + my $interface_config = PVE::INotify::read_file('interfaces', 1); + my $interfaces = $interface_config->{data}->{ifaces}; + + @bridges_in_zone = + grep { $interfaces->{$_}->{type} eq 'bridge' } keys $interfaces->%*; + } else { + my $zone = PVE::Network::SDN::Zones::get_zone($zone_id, 1); + + raise_param_exc({ + zone => "zone does not exist", + }) + if !$zone; + + my $vnet_cfg = PVE::Network::SDN::Vnets::config(1); + @bridges_in_zone = + grep { $vnet_cfg->{ids}->{$_}->{zone} eq $zone_id } keys $vnet_cfg->{ids}->%*; + } + + my $ip_details = PVE::Network::ip_link_details(); + my $vlan_information = PVE::IPRoute2::get_vlan_information(); + + my $result = {}; + for my $bridge_name (@bridges_in_zone) { + next + if !$rpcenv->check_any( + $authuser, + "/sdn/zones/$zone_id/$bridge_name", + ['SDN.Audit', 'SDN.Allocate'], + 1, + ); + + my $ip_link = $ip_details->{$bridge_name}; + + $result->{$bridge_name} = { + name => $bridge_name, + vlan_filtering => $ip_link->{linkinfo}->{info_data}->{vlan_filtering}, + ports => [], + }; + } + + for my $interface (values $ip_details->%*) { + if (PVE::IPRoute2::ip_link_is_bridge_member($interface)) { + my $master = $interface->{master}; + + # avoid potential TOCTOU by just skipping over the interface, + # if we didn't get the master from 'ip link' + next if !defined($result->{$master}); + + my $ifname = $interface->{ifname}; + + my $port = { + name => $ifname, + }; + + if ($ifname =~ m/^(?:fwpr(\d+)p(\d+)|veth(\d+)i(\d+)|tap(\d+)i(\d+))$/) { + $port->{vmid} = $1; + $port->{index} = $2; + } + + if ($result->{$master}->{vlan_filtering} == 1) { + $port->{vlans} = []; + + for my $vlan ($vlan_information->{$ifname}->{vlans}->@*) { + if (grep { $_ eq 'PVID' } $vlan->{flags}->@*) { + $port->{primary_vlan} = $vlan->{vlan}; + } elsif ($vlan->{vlan} && $vlan->{vlanEnd}) { + push $port->{vlans}->@*, "$vlan->{vlan}-$vlan->{vlanEnd}"; + } elsif ($vlan->{vlan}) { + push $port->{vlans}->@*, "$vlan->{vlan}"; + } + } + } + + push $result->{$master}->{ports}->@*, $port; + } + } + + my @result = values $result->%*; + return \@result; + }, +}); + 1; -- 2.47.3 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel