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 ECA911FF0DF for ; Thu, 05 Nov 2026 15:40:46 +0100 (CET) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 8A91B21511; Thu, 05 Nov 2026 15:40:43 +0100 (CET) Message-ID: Date: Thu, 5 Nov 2026 15:40:37 +0100 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH proxmox-ve-rs 09/13] fabric: wireguard: add helper for findings peer based on endpoint To: Stefan Hanreich , pve-devel@lists.proxmox.com References: <20260617111012.312710-1-s.hanreich@proxmox.com> <20260617111012.312710-10-s.hanreich@proxmox.com> From: Hannes Laimer Content-Language: en-US In-Reply-To: <20260617111012.312710-10-s.hanreich@proxmox.com> Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1793889638057 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.299 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) 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: FA6Y57VVO4DAJISDOL2FQLZAD5XFITRX X-Message-ID-Hash: FA6Y57VVO4DAJISDOL2FQLZAD5XFITRX 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: comment inline On 2026-06-17 13:10, Stefan Hanreich wrote: > This function will be used by the status reporting, which requires the > ability to match an entry from the dump output to the respective node > in the section config, in order to include the corresponding > node/interface in its informational output. This helps users matching > peers from the running WireGuard configuration to their respective > section config entry. > > Signed-off-by: Stefan Hanreich > --- > proxmox-ve-config/src/sdn/fabric/mod.rs | 115 +++++++++++++++++- > .../section_config/protocol/wireguard.rs | 8 ++ > 2 files changed, 121 insertions(+), 2 deletions(-) > > diff --git a/proxmox-ve-config/src/sdn/fabric/mod.rs b/proxmox-ve-config/src/sdn/fabric/mod.rs > index 22f19c7..d608266 100644 > --- a/proxmox-ve-config/src/sdn/fabric/mod.rs > +++ b/proxmox-ve-config/src/sdn/fabric/mod.rs > @@ -7,6 +7,7 @@ use std::marker::PhantomData; > use std::ops::Deref; > > use anyhow::Error; > +use proxmox_network_types::endpoint::ServiceEndpoint; > use section_config::protocol::wireguard::WireGuardProperties; > use serde::{Deserialize, Serialize}; > > @@ -34,8 +35,9 @@ use crate::sdn::fabric::section_config::protocol::ospf::{ > OspfNodePropertiesUpdater, OspfProperties, OspfPropertiesUpdater, > }; > use crate::sdn::fabric::section_config::protocol::wireguard::{ > - WireGuardDeletableProperties, WireGuardNode, WireGuardNodeDeletableProperties, > - WireGuardNodePeer, WireGuardNodeUpdater, WireGuardPropertiesUpdater, > + WireGuardDeletableProperties, WireGuardInterfaceProperties, WireGuardNode, > + WireGuardNodeDeletableProperties, WireGuardNodePeer, WireGuardNodeUpdater, > + WireGuardPropertiesUpdater, > }; > use crate::sdn::fabric::section_config::{FabricOrNode, Section}; > > @@ -215,6 +217,115 @@ impl_entry!(Ospf, OspfProperties, OspfNodeProperties); > impl_entry!(WireGuard, WireGuardProperties, WireGuardNode); > impl_entry!(Bgp, BgpProperties, BgpNode); > > +impl Entry { > + /// Search for a node in the fabric based on its endpoint. > + /// > + /// Searches for the node in the fabric configuration that has the given endpoint on a specific > + /// node. Mainly useful for mapping the `wg show` output to a node entry in the section config > + /// via the specified endpoint. > + pub fn find_node_and_interface_by_endpoint( > + &self, > + local_node_id: &NodeId, > + endpoint: &ServiceEndpoint, > + ) -> Result)>, Error> { > + let node = self.get_node(local_node_id)?; > + > + let Node::WireGuard(wireguard_node) = node else { > + anyhow::bail!("no wireguard node with id {local_node_id} found"); > + }; > + > + let WireGuardNode::Internal(internal_node) = wireguard_node.properties() else { > + anyhow::bail!("wireguard node with id {local_node_id} is not an internal node"); > + }; > + > + for peer in internal_node.peers() { > + if let Some(peer_endpoint) = peer.endpoint() { > + if endpoint == peer_endpoint { > + let referenced_node = self.get_node(peer.node())?; > + > + return Ok(Some(match peer { > + WireGuardNodePeer::Internal(internal_peer) => { > + let referenced_wireguard_node = > + self.node_section(&internal_peer.node)?; > + > + let WireGuardNode::Internal(referenced_internal_node) = > + referenced_wireguard_node.properties() > + else { > + anyhow::bail!( > + "referenced node {} is not a internal wireguard node", > + internal_peer.node > + ); > + }; > + > + ( > + referenced_node, > + Some( > + referenced_internal_node > + .interfaces() > + .find(|interface| { > + interface.name() == &internal_peer.node_iface > + }) > + .ok_or_else(|| { > + anyhow::anyhow!("referenced interface does not exist") > + })?, > + ), > + ) > + } > + WireGuardNodePeer::External(_) => (referenced_node, None), > + })); > + } > + } else { > + let referenced_node = self.get_node(peer.node())?; > + > + match peer { > + WireGuardNodePeer::Internal(internal_peer) => { > + let referenced_wireguard_node = self.node_section(&internal_peer.node)?; > + > + let WireGuardNode::Internal(referenced_internal_node) = > + referenced_wireguard_node.properties() > + else { > + anyhow::bail!( > + "referenced node {} is not a internal wireguard node", > + internal_peer.node > + ); > + }; > + > + let Some(ip_host) = &referenced_internal_node.endpoint else { > + continue; > + }; > + > + for interface in internal_node.interfaces() { > + let node_endpoint = > + ServiceEndpoint::new(&ip_host.to_string(), interface.listen_port)?; > + > + if &node_endpoint == endpoint { > + return Ok(Some((referenced_node, Some(interface)))); > + } > + } we shouldn't use our listen ports here, the config uses the port of the peer's `node_iface`. so peers without an override only match if both ports are the same > + } > + WireGuardNodePeer::External(external_peer) => { > + let referenced_wireguard_node = self.node_section(&external_peer.node)?; > + > + let WireGuardNode::External(referenced_external_node) = referenced_wireguard_node.properties() > + else { > + anyhow::bail!( > + "referenced node {} is not an external wireguard node", > + external_peer.node > + ); > + }; > + > + if &referenced_external_node.endpoint == endpoint { > + return Ok(Some((referenced_node, None))); > + } > + } > + } > + } > + } > + > + return Ok(None); > + } > +} > + > /// All possible entries in a [`FabricConfig`]. > /// > /// It utilizes the [`Entry`] struct to validate proper combinations of [`FabricSection`] and > diff --git a/proxmox-ve-config/src/sdn/fabric/section_config/protocol/wireguard.rs b/proxmox-ve-config/src/sdn/fabric/section_config/protocol/wireguard.rs > index a2d8c6e..38cc8f0 100644 > --- a/proxmox-ve-config/src/sdn/fabric/section_config/protocol/wireguard.rs > +++ b/proxmox-ve-config/src/sdn/fabric/section_config/protocol/wireguard.rs > @@ -488,6 +488,14 @@ impl WireGuardNodePeer { > } > } > > + /// Returns the endpoint override for this peer definition, if it exists. > + pub fn endpoint(&self) -> Option<&ServiceEndpoint> { > + match self { > + WireGuardNodePeer::Internal(internal_peer) => internal_peer.endpoint.as_ref(), > + WireGuardNodePeer::External(external_peer) => external_peer.endpoint.as_ref(), > + } > + } > + > pub fn node_iface(&self) -> Option<&WireGuardInterfaceName> { > match self { > WireGuardNodePeer::Internal(internal_peer) => Some(&internal_peer.node_iface),