From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id EEA331FF141 for ; Fri, 16 Jan 2026 16:34:21 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 927A71C593; Fri, 16 Jan 2026 16:34:19 +0100 (CET) From: Christoph Heiss To: pve-devel@lists.proxmox.com Date: Fri, 16 Jan 2026 16:33:16 +0100 Message-ID: <20260116153317.1146323-12-c.heiss@proxmox.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260116153317.1146323-1-c.heiss@proxmox.com> References: <20260116153317.1146323-1-c.heiss@proxmox.com> MIME-Version: 1.0 X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1768577602337 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.052 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 SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pve-devel] [PATCH proxmox-ve-rs 11/11] ve-config: sdn: fabrics: add wireguard section config types 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" Signed-off-by: Christoph Heiss --- Cargo.toml | 1 + proxmox-ve-config/Cargo.toml | 2 + proxmox-ve-config/debian/control | 18 +- proxmox-ve-config/src/sdn/fabric/frr.rs | 1 + proxmox-ve-config/src/sdn/fabric/mod.rs | 119 +++++++++++++ .../src/sdn/fabric/section_config/fabric.rs | 23 +++ .../src/sdn/fabric/section_config/mod.rs | 19 ++ .../src/sdn/fabric/section_config/node.rs | 33 +++- .../sdn/fabric/section_config/protocol/mod.rs | 1 + .../section_config/protocol/wireguard.rs | 162 ++++++++++++++++++ 10 files changed, 374 insertions(+), 5 deletions(-) create mode 100644 proxmox-ve-config/src/sdn/fabric/section_config/protocol/wireguard.rs diff --git a/Cargo.toml b/Cargo.toml index 99bd54a..d925d5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,3 +29,4 @@ proxmox-network-types = { version = "0.1.1" } proxmox-schema = { version = "5" } proxmox-sdn-types = { version = "0.1", path = "proxmox-sdn-types" } proxmox-serde = { version = "1.0.0" } +proxmox-wireguard = { version = "0.1.0" } diff --git a/proxmox-ve-config/Cargo.toml b/proxmox-ve-config/Cargo.toml index 3a4dd61..130430c 100644 --- a/proxmox-ve-config/Cargo.toml +++ b/proxmox-ve-config/Cargo.toml @@ -22,6 +22,7 @@ proxmox-frr = { workspace = true, optional = true } proxmox-network-types = { workspace = true, features = [ "api-types" ] } proxmox-schema = { workspace = true, features = [ "api-types" ] } proxmox-sdn-types = { workspace = true } +proxmox-wireguard = { workspace = true, optional = true } proxmox-section-config = { version = "3" } proxmox-serde = { workspace = true, features = [ "perl" ]} proxmox-sys = "1" @@ -29,6 +30,7 @@ proxmox-sortable-macro = "1" [features] frr = ["dep:proxmox-frr"] +wireguard = ["dep:proxmox-wireguard"] [dev-dependencies] insta = "1.21" diff --git a/proxmox-ve-config/debian/control b/proxmox-ve-config/debian/control index b211827..9cbe1b8 100644 --- a/proxmox-ve-config/debian/control +++ b/proxmox-ve-config/debian/control @@ -58,7 +58,8 @@ Depends: librust-thiserror-2+default-dev, librust-tracing-0.1+default-dev (>= 0.1.37-~~) Suggests: - librust-proxmox-ve-config+frr-dev (= ${binary:Version}) + librust-proxmox-ve-config+frr-dev (= ${binary:Version}), + librust-proxmox-ve-config+wireguard-dev (= ${binary:Version}) Provides: librust-proxmox-ve-config+default-dev (= ${binary:Version}), librust-proxmox-ve-config-0-dev (= ${binary:Version}), @@ -84,3 +85,18 @@ Provides: Description: Rust crate "proxmox-ve-config" - feature "frr" This metapackage enables feature "frr" for the Rust proxmox-ve-config crate, by pulling in any additional dependencies needed by that feature. + +Package: librust-proxmox-ve-config+wireguard-dev +Architecture: any +Multi-Arch: same +Depends: + ${misc:Depends}, + librust-proxmox-ve-config-dev (= ${binary:Version}), + librust-proxmox-wireguard-0.1+default-dev +Provides: + librust-proxmox-ve-config-0+wireguard-dev (= ${binary:Version}), + librust-proxmox-ve-config-0.4+wireguard-dev (= ${binary:Version}), + librust-proxmox-ve-config-0.4.6+wireguard-dev (= ${binary:Version}) +Description: Rust crate "proxmox-ve-config" - feature "wireguard" + This metapackage enables feature "wireguard" for the Rust proxmox-ve-config + crate, by pulling in any additional dependencies needed by that feature. diff --git a/proxmox-ve-config/src/sdn/fabric/frr.rs b/proxmox-ve-config/src/sdn/fabric/frr.rs index 10025b3..fc41410 100644 --- a/proxmox-ve-config/src/sdn/fabric/frr.rs +++ b/proxmox-ve-config/src/sdn/fabric/frr.rs @@ -232,6 +232,7 @@ pub fn build_fabric( frr_config.protocol_routemaps.insert(protocol_routemap); } + FabricEntry::WireGuard(_) => {} // not a frr fabric } } Ok(()) diff --git a/proxmox-ve-config/src/sdn/fabric/mod.rs b/proxmox-ve-config/src/sdn/fabric/mod.rs index d0add92..a3b9606 100644 --- a/proxmox-ve-config/src/sdn/fabric/mod.rs +++ b/proxmox-ve-config/src/sdn/fabric/mod.rs @@ -7,6 +7,10 @@ use std::marker::PhantomData; use std::ops::Deref; use anyhow::Error; +use section_config::protocol::wireguard::{ + WireGuardDeletableProperties, WireGuardNodeDeletableProperties, WireGuardNodeProperties, + WireGuardNodePropertiesUpdater, WireGuardProperties, WireGuardPropertiesUpdater, +}; use serde::{Deserialize, Serialize}; use proxmox_section_config::typed::{ApiSectionDataEntry, SectionConfigData}; @@ -189,6 +193,7 @@ macro_rules! impl_entry { impl_entry!(Openfabric, OpenfabricProperties, OpenfabricNodeProperties); impl_entry!(Ospf, OspfProperties, OspfNodeProperties); +impl_entry!(WireGuard, WireGuardProperties, WireGuardNodeProperties); /// All possible entries in a [`FabricConfig`]. /// @@ -198,6 +203,7 @@ impl_entry!(Ospf, OspfProperties, OspfNodeProperties); pub enum FabricEntry { Openfabric(Entry), Ospf(Entry), + WireGuard(Entry), } impl FabricEntry { @@ -209,6 +215,9 @@ impl FabricEntry { entry.add_node(node_section) } (FabricEntry::Ospf(entry), Node::Ospf(node_section)) => entry.add_node(node_section), + (FabricEntry::WireGuard(entry), Node::WireGuard(node_section)) => { + entry.add_node(node_section) + } _ => Err(FabricConfigError::ProtocolMismatch), } } @@ -219,6 +228,7 @@ impl FabricEntry { match self { FabricEntry::Openfabric(entry) => entry.get_node(id), FabricEntry::Ospf(entry) => entry.get_node(id), + FabricEntry::WireGuard(entry) => entry.get_node(id), } } @@ -228,6 +238,7 @@ impl FabricEntry { match self { FabricEntry::Openfabric(entry) => entry.get_node_mut(id), FabricEntry::Ospf(entry) => entry.get_node_mut(id), + FabricEntry::WireGuard(entry) => entry.get_node_mut(id), } } @@ -307,6 +318,52 @@ impl FabricEntry { Ok(()) } + (Node::WireGuard(node_section), NodeUpdater::WireGuard(updater)) => { + let NodeDataUpdater::< + WireGuardNodePropertiesUpdater, + WireGuardNodeDeletableProperties, + > { + ip, + ip6, + properties: + WireGuardNodePropertiesUpdater { + interfaces, + listen_port, + }, + delete, + } = updater; + + if let Some(ip) = ip { + node_section.ip = Some(ip); + } + + if let Some(ip) = ip6 { + node_section.ip6 = Some(ip); + } + + if let Some(interfaces) = interfaces { + node_section.properties.interfaces = interfaces; + } + + if let Some(listen_port) = listen_port { + node_section.properties.listen_port = Some(listen_port); + } + + for property in delete { + match property { + NodeDeletableProperties::Ip => node_section.ip = None, + NodeDeletableProperties::Ip6 => node_section.ip6 = None, + NodeDeletableProperties::Protocol( + WireGuardNodeDeletableProperties::Interfaces, + ) => node_section.properties.interfaces = Vec::new(), + NodeDeletableProperties::Protocol( + WireGuardNodeDeletableProperties::ListenPort, + ) => node_section.properties.listen_port = None, + } + } + + Ok(()) + } _ => Err(FabricConfigError::ProtocolMismatch), } } @@ -316,6 +373,7 @@ impl FabricEntry { match self { FabricEntry::Openfabric(entry) => entry.nodes.iter(), FabricEntry::Ospf(entry) => entry.nodes.iter(), + FabricEntry::WireGuard(entry) => entry.nodes.iter(), } } @@ -324,6 +382,7 @@ impl FabricEntry { match self { FabricEntry::Openfabric(entry) => entry.delete_node(id), FabricEntry::Ospf(entry) => entry.delete_node(id), + FabricEntry::WireGuard(entry) => entry.delete_node(id), } } @@ -333,6 +392,7 @@ impl FabricEntry { match self { FabricEntry::Openfabric(entry) => entry.into_pair(), FabricEntry::Ospf(entry) => entry.into_pair(), + FabricEntry::WireGuard(entry) => entry.into_pair(), } } @@ -341,6 +401,7 @@ impl FabricEntry { match self { FabricEntry::Openfabric(entry) => &entry.fabric, FabricEntry::Ospf(entry) => &entry.fabric, + FabricEntry::WireGuard(entry) => &entry.fabric, } } @@ -349,6 +410,7 @@ impl FabricEntry { match self { FabricEntry::Openfabric(entry) => &mut entry.fabric, FabricEntry::Ospf(entry) => &mut entry.fabric, + FabricEntry::WireGuard(entry) => &mut entry.fabric, } } } @@ -360,6 +422,7 @@ impl From for FabricEntry { FabricEntry::Openfabric(Entry::new(fabric_section)) } Fabric::Ospf(fabric_section) => FabricEntry::Ospf(Entry::new(fabric_section)), + Fabric::WireGuard(fabric_section) => FabricEntry::WireGuard(Entry::new(fabric_section)), } } } @@ -541,6 +604,13 @@ impl Validatable for FabricConfig { return Err(FabricConfigError::DuplicateInterface); } } + Node::WireGuard(node_section) => { + if !node_section.properties().interfaces().all(|interface| { + node_interfaces.insert((node_id, interface.name.as_str())) + }) { + return Err(FabricConfigError::DuplicateInterface); + } + } } } @@ -695,6 +765,55 @@ impl FabricConfig { Ok(()) } + (Fabric::WireGuard(fabric_section), FabricUpdater::WireGuard(updater)) => { + let FabricSectionUpdater::< + WireGuardPropertiesUpdater, + WireGuardDeletableProperties, + > { + ip_prefix, + ip6_prefix, + properties: + WireGuardPropertiesUpdater { + persistent_keepalive, + listen_port, + }, + delete, + } = updater; + + if let Some(prefix) = ip_prefix { + fabric_section.ip_prefix = Some(prefix); + } + + if let Some(prefix) = ip6_prefix { + fabric_section.ip6_prefix = Some(prefix); + } + + if let Some(keepalive) = persistent_keepalive { + fabric_section.properties.persistent_keepalive = Some(keepalive); + } + + if let Some(listen_port) = listen_port { + fabric_section.properties.listen_port = listen_port; + } + + for property in delete { + match property { + FabricDeletableProperties::IpPrefix => { + fabric_section.ip_prefix = None; + } + FabricDeletableProperties::Ip6Prefix => { + fabric_section.ip6_prefix = None; + } + FabricDeletableProperties::Protocol( + WireGuardDeletableProperties::PersistentKeepalive, + ) => { + fabric_section.properties.persistent_keepalive = None; + } + } + } + + Ok(()) + } _ => Err(FabricConfigError::ProtocolMismatch), } } diff --git a/proxmox-ve-config/src/sdn/fabric/section_config/fabric.rs b/proxmox-ve-config/src/sdn/fabric/section_config/fabric.rs index 38911a6..fbfd1a8 100644 --- a/proxmox-ve-config/src/sdn/fabric/section_config/fabric.rs +++ b/proxmox-ve-config/src/sdn/fabric/section_config/fabric.rs @@ -16,6 +16,10 @@ use crate::sdn::fabric::section_config::protocol::ospf::{ }; use crate::sdn::fabric::FabricConfigError; +use super::protocol::wireguard::{ + WireGuardDeletableProperties, WireGuardProperties, WireGuardPropertiesUpdater, +}; + pub const FABRIC_ID_REGEX_STR: &str = r"(?:[a-zA-Z0-9])(?:[a-zA-Z0-9\-]){0,6}(?:[a-zA-Z0-9])?"; const_regex! { @@ -139,6 +143,10 @@ impl UpdaterType for FabricSection { type Updater = FabricSectionUpdater; } +impl UpdaterType for FabricSection { + type Updater = FabricSectionUpdater; +} + /// Enum containing all types of fabrics. /// /// It utilizes [`FabricSection`] to define all possible types of fabrics. For parsing the @@ -159,6 +167,7 @@ impl UpdaterType for FabricSection { pub enum Fabric { Openfabric(FabricSection), Ospf(FabricSection), + WireGuard(FabricSection), } impl UpdaterType for Fabric { @@ -173,6 +182,7 @@ impl Fabric { match self { Self::Openfabric(fabric_section) => fabric_section.id(), Self::Ospf(fabric_section) => fabric_section.id(), + Self::WireGuard(fabric_section) => fabric_section.id(), } } @@ -183,6 +193,7 @@ impl Fabric { match self { Fabric::Openfabric(fabric_section) => fabric_section.ip_prefix(), Fabric::Ospf(fabric_section) => fabric_section.ip_prefix(), + Fabric::WireGuard(fabric_section) => fabric_section.ip_prefix(), } } @@ -193,6 +204,7 @@ impl Fabric { match self { Fabric::Openfabric(fabric_section) => fabric_section.ip_prefix = Some(ipv4_cidr), Fabric::Ospf(fabric_section) => fabric_section.ip_prefix = Some(ipv4_cidr), + Fabric::WireGuard(fabric_section) => fabric_section.ip_prefix = Some(ipv4_cidr), } } @@ -203,6 +215,7 @@ impl Fabric { match self { Fabric::Openfabric(fabric_section) => fabric_section.ip6_prefix(), Fabric::Ospf(fabric_section) => fabric_section.ip6_prefix(), + Fabric::WireGuard(fabric_section) => fabric_section.ip6_prefix(), } } @@ -213,6 +226,7 @@ impl Fabric { match self { Fabric::Openfabric(fabric_section) => fabric_section.ip6_prefix = Some(ipv6_cidr), Fabric::Ospf(fabric_section) => fabric_section.ip6_prefix = Some(ipv6_cidr), + Fabric::WireGuard(fabric_section) => fabric_section.ip6_prefix = Some(ipv6_cidr), } } } @@ -225,6 +239,7 @@ impl Validatable for Fabric { match self { Fabric::Openfabric(fabric_section) => fabric_section.validate(), Fabric::Ospf(fabric_section) => fabric_section.validate(), + Fabric::WireGuard(fabric_section) => fabric_section.validate(), } } } @@ -241,12 +256,19 @@ impl From> for Fabric { } } +impl From> for Fabric { + fn from(section: FabricSection) -> Self { + Fabric::WireGuard(section) + } +} + /// Enum containing all updater types for fabrics #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "snake_case", tag = "protocol")] pub enum FabricUpdater { Openfabric( as UpdaterType>::Updater), Ospf( as UpdaterType>::Updater), + WireGuard( as UpdaterType>::Updater), } impl Updater for FabricUpdater { @@ -254,6 +276,7 @@ impl Updater for FabricUpdater { match self { FabricUpdater::Openfabric(updater) => updater.is_empty(), FabricUpdater::Ospf(updater) => updater.is_empty(), + FabricUpdater::WireGuard(updater) => updater.is_empty(), } } } diff --git a/proxmox-ve-config/src/sdn/fabric/section_config/mod.rs b/proxmox-ve-config/src/sdn/fabric/section_config/mod.rs index d02d4ae..454145d 100644 --- a/proxmox-ve-config/src/sdn/fabric/section_config/mod.rs +++ b/proxmox-ve-config/src/sdn/fabric/section_config/mod.rs @@ -4,6 +4,7 @@ pub mod node; pub mod protocol; use const_format::concatcp; +use protocol::wireguard::{WireGuardNodeProperties, WireGuardProperties}; use serde::{Deserialize, Serialize}; use crate::sdn::fabric::section_config::{ @@ -31,8 +32,10 @@ impl From
for FabricOrNode { match section { Section::OpenfabricFabric(fabric_section) => Self::Fabric(fabric_section.into()), Section::OspfFabric(fabric_section) => Self::Fabric(fabric_section.into()), + Section::WireGuardFabric(fabric_section) => Self::Fabric(fabric_section.into()), Section::OpenfabricNode(node_section) => Self::Node(node_section.into()), Section::OspfNode(node_section) => Self::Node(node_section.into()), + Section::WireGuardNode(node_section) => Self::Node(node_section.into()), } } } @@ -62,8 +65,10 @@ pub const SECTION_ID_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&SECTION pub enum Section { OpenfabricFabric(FabricSection), OspfFabric(FabricSection), + WireGuardFabric(FabricSection), OpenfabricNode(NodeSection), OspfNode(NodeSection), + WireGuardNode(NodeSection), } impl From> for Section { @@ -78,6 +83,12 @@ impl From> for Section { } } +impl From> for Section { + fn from(section: FabricSection) -> Self { + Self::WireGuardFabric(section) + } +} + impl From> for Section { fn from(section: NodeSection) -> Self { Self::OpenfabricNode(section) @@ -90,11 +101,18 @@ impl From> for Section { } } +impl From> for Section { + fn from(section: NodeSection) -> Self { + Self::WireGuardNode(section) + } +} + impl From for Section { fn from(fabric: Fabric) -> Self { match fabric { Fabric::Openfabric(fabric_section) => fabric_section.into(), Fabric::Ospf(fabric_section) => fabric_section.into(), + Fabric::WireGuard(fabric_section) => fabric_section.into(), } } } @@ -104,6 +122,7 @@ impl From for Section { match node { Node::Openfabric(node_section) => node_section.into(), Node::Ospf(node_section) => node_section.into(), + Node::WireGuard(node_section) => node_section.into(), } } } diff --git a/proxmox-ve-config/src/sdn/fabric/section_config/node.rs b/proxmox-ve-config/src/sdn/fabric/section_config/node.rs index 17d2f0b..5397b17 100644 --- a/proxmox-ve-config/src/sdn/fabric/section_config/node.rs +++ b/proxmox-ve-config/src/sdn/fabric/section_config/node.rs @@ -16,6 +16,8 @@ use crate::sdn::fabric::section_config::{ }; use crate::sdn::fabric::FabricConfigError; +use super::protocol::wireguard::WireGuardNodeProperties; + pub const NODE_ID_REGEX_STR: &str = r"(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]){0,61}(?:[a-zA-Z0-9]){0,1})"; const_regex! { @@ -147,8 +149,8 @@ impl NodeSection { /// Get the IPv4 address (Router-ID) of the [`NodeSection`]. /// /// Either the [`NodeSection::ip`] (IPv4) address or the [`NodeSection::ip6`] (IPv6) address *must* - /// be set. This is checked during the validation, so it's guaranteed. OpenFabric can also be - /// used dual-stack, so both IPv4 and IPv6 addresses can be set. + /// be set. This is checked during the validation, so it's guaranteed. OpenFabric and WireGuard + /// can also be used dual-stack, so both IPv4 and IPv6 addresses can be set. pub fn ip(&self) -> Option { self.ip.as_deref().copied() } @@ -156,8 +158,8 @@ impl NodeSection { /// Get the IPv6 address (Router-ID) of the [`NodeSection`]. /// /// Either the [`NodeSection::ip`] (IPv4) address or the [`NodeSection::ip6`] (IPv6) address *must* - /// be set. This is checked during the validation, so it's guaranteed. OpenFabric can also be - /// used dual-stack, so both IPv4 and IPv6 addresses can be set. + /// be set. This is checked during the validation, so it's guaranteed. OpenFabric and WireGuard + /// can also be used dual-stack, so both IPv4 and IPv6 addresses can be set. pub fn ip6(&self) -> Option { self.ip6.as_deref().copied() } @@ -186,6 +188,7 @@ impl ApiType for NodeSection { pub enum Node { Openfabric(NodeSection), Ospf(NodeSection), + WireGuard(NodeSection), } impl Node { @@ -194,6 +197,7 @@ impl Node { match self { Node::Openfabric(node_section) => node_section.id(), Node::Ospf(node_section) => node_section.id(), + Node::WireGuard(node_section) => node_section.id(), } } @@ -202,6 +206,7 @@ impl Node { match self { Node::Openfabric(node_section) => node_section.ip(), Node::Ospf(node_section) => node_section.ip(), + Node::WireGuard(node_section) => node_section.ip(), } } @@ -210,6 +215,7 @@ impl Node { match self { Node::Openfabric(node_section) => node_section.ip6(), Node::Ospf(node_section) => node_section.ip6(), + Node::WireGuard(node_section) => node_section.ip6(), } } } @@ -221,6 +227,7 @@ impl Validatable for Node { match self { Node::Openfabric(node_section) => node_section.validate(), Node::Ospf(node_section) => node_section.validate(), + Node::WireGuard(node_section) => node_section.validate(), } } } @@ -237,6 +244,12 @@ impl From> for Node { } } +impl From> for Node { + fn from(value: NodeSection) -> Self { + Self::WireGuard(value) + } +} + /// API types for SDN fabric node configurations. /// /// This module provides specialized types that are used for API interactions when retrieving, @@ -263,6 +276,7 @@ pub mod api { OpenfabricNodePropertiesUpdater, }, ospf::{OspfNodeDeletableProperties, OspfNodeProperties, OspfNodePropertiesUpdater}, + wireguard::{WireGuardNodeDeletableProperties, WireGuardNodePropertiesUpdater}, }; use super::*; @@ -320,6 +334,7 @@ pub mod api { pub enum Node { Openfabric(NodeData), Ospf(NodeData), + WireGuard(NodeData), } impl From for Node { @@ -327,6 +342,7 @@ pub mod api { match value { super::Node::Openfabric(node_section) => Self::Openfabric(node_section.into()), super::Node::Ospf(node_section) => Self::Ospf(node_section.into()), + super::Node::WireGuard(node_section) => Self::WireGuard(node_section.into()), } } } @@ -336,6 +352,7 @@ pub mod api { match value { Node::Openfabric(node_section) => Self::Openfabric(node_section.into()), Node::Ospf(node_section) => Self::Ospf(node_section.into()), + Node::WireGuard(node_section) => Self::WireGuard(node_section.into()), } } } @@ -349,6 +366,11 @@ pub mod api { type Updater = NodeDataUpdater; } + impl UpdaterType for NodeData { + type Updater = + NodeDataUpdater; + } + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NodeDataUpdater { #[serde(skip_serializing_if = "Option::is_none")] @@ -384,6 +406,9 @@ pub mod api { NodeDataUpdater, ), Ospf(NodeDataUpdater), + WireGuard( + NodeDataUpdater, + ), } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/proxmox-ve-config/src/sdn/fabric/section_config/protocol/mod.rs b/proxmox-ve-config/src/sdn/fabric/section_config/protocol/mod.rs index c1ec847..fd77426 100644 --- a/proxmox-ve-config/src/sdn/fabric/section_config/protocol/mod.rs +++ b/proxmox-ve-config/src/sdn/fabric/section_config/protocol/mod.rs @@ -1,2 +1,3 @@ pub mod openfabric; pub mod ospf; +pub mod wireguard; 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 new file mode 100644 index 0000000..2cc44fc --- /dev/null +++ b/proxmox-ve-config/src/sdn/fabric/section_config/protocol/wireguard.rs @@ -0,0 +1,162 @@ +//! API and section-config interface for WireGuard as an SDN fabric. + +use std::ops::{Deref, DerefMut}; + +use anyhow::Result; +use proxmox_network_types::ip_address::{Ipv4Cidr, Ipv6Cidr}; +use proxmox_schema::{ + api, api_types::PORT_SCHEMA, property_string::PropertyString, ApiStringFormat, Updater, +}; +use proxmox_sdn_types::wireguard::PersistentKeepalive; +use serde::{Deserialize, Serialize}; + +use crate::{ + common::valid::Validatable, + sdn::fabric::{ + section_config::{fabric::FabricSection, interface::InterfaceName, node::NodeSection}, + FabricConfigError, + }, +}; + +/// Protocol-specific options for an WireGuard fabric. +#[api] +#[derive(Clone, Debug, Serialize, Deserialize, Updater, Hash)] +pub struct WireGuardProperties { + /// Fabric-wide persistent keepalive interval between peers. + // While this is actually a per-peer property, + // we only allow setting it on a per-fabric level (for now) keep configuration + // simpler, as you rarely actually want specific intervals per peer, especially + // in a cluster environment. + #[serde(skip_serializing_if = "persistent_keepalive_is_off")] + pub(crate) persistent_keepalive: Option, + /// Fabric-wide listen port for WireGuard traffic. + pub(crate) listen_port: u16, +} + +impl Validatable for FabricSection { + type Error = FabricConfigError; + + /// Validates a [FabricSection]. + fn validate(&self) -> Result<(), Self::Error> { + Ok(()) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum WireGuardDeletableProperties { + PersistentKeepalive, +} + +/// Properties of a WireGuard node. +#[api( + properties: { + interfaces: { + type: Array, + optional: true, + items: { + type: String, + description: "WireGuard interface", + format: &ApiStringFormat::PropertyString(&WireGuardInterfaceProperties::API_SCHEMA), + } + }, + "listen-port": { + optional: true, + schema: PORT_SCHEMA, + }, + } +)] +#[derive(Clone, Debug, Serialize, Deserialize, Updater, Hash)] +#[serde(rename_all = "kebab-case")] +pub struct WireGuardNodeProperties { + /// Interface properties for this node. + #[serde(default)] + pub(crate) interfaces: Vec>, + /// Listen port for WireGuard on this node. Overrides the fabric-wide setting. + pub(crate) listen_port: Option, + // TODO: add public key to "pin" them in the section config? +} + +impl WireGuardNodeProperties { + pub fn interfaces(&self) -> impl Iterator { + self.interfaces + .iter() + .map(|property_string| property_string.deref()) + } + + /// Returns an iterator over all the interfaces (mutable). + pub fn interfaces_mut(&mut self) -> impl Iterator { + self.interfaces + .iter_mut() + .map(|property_string| property_string.deref_mut()) + } +} + +impl Validatable for NodeSection { + type Error = FabricConfigError; + + /// Validates the [FabricSection]. + /// + /// Checks if we have either an IPv4 or an IPv6 address. If neither is set, return an error. + fn validate(&self) -> Result<(), Self::Error> { + if self.ip().is_none() && self.ip6().is_none() { + return Err(FabricConfigError::NodeNoIp(self.id().to_string())); + } + + Ok(()) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum WireGuardNodeDeletableProperties { + Interfaces, + ListenPort, +} + +/// Properties of a WireGuard interface. +#[api] +#[derive(Clone, Debug, Serialize, Deserialize, Updater, Hash)] +pub struct WireGuardInterfaceProperties { + /// Name for this WireGuard interface. + pub(crate) name: InterfaceName, + + /// If ip and ip6 are unset, then this is an point-to-point interface. + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) ip: Option, + + /// If ip6 and ip are unset, then this is an point-to-point interface. + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) ip6: Option, +} + +impl WireGuardInterfaceProperties { + /// Get the name of the interface. + pub fn name(&self) -> &InterfaceName { + &self.name + } + + /// Set the name of the interface. + pub fn set_name(&mut self, name: InterfaceName) { + self.name = name + } + + /// Get the ip (IPv4) of the interface. + pub fn ip(&self) -> Option<&Ipv4Cidr> { + self.ip.as_ref() + } + + /// Get the ip6 (IPv6) of the interface. + pub fn ip6(&self) -> Option<&Ipv6Cidr> { + self.ip6.as_ref() + } +} + +/// Determines whether the given `PersistentKeepalive` value means that it is +/// turned off. Useful for usage with serde's `skip_serializing_if`. +fn persistent_keepalive_is_off(value: &Option) -> bool { + value + .as_ref() + .map(PersistentKeepalive::is_off) + .unwrap_or(true) +} -- 2.52.0 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel