From: Christoph Heiss <c.heiss@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH proxmox-ve-rs 11/11] ve-config: sdn: fabrics: add wireguard section config types
Date: Fri, 16 Jan 2026 16:33:16 +0100 [thread overview]
Message-ID: <20260116153317.1146323-12-c.heiss@proxmox.com> (raw)
In-Reply-To: <20260116153317.1146323-1-c.heiss@proxmox.com>
Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
---
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<OpenfabricProperties, OpenfabricNodeProperties>),
Ospf(Entry<OspfProperties, OspfNodeProperties>),
+ WireGuard(Entry<WireGuardProperties, WireGuardNodeProperties>),
}
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<Fabric> 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<OspfProperties> {
type Updater = FabricSectionUpdater<OspfPropertiesUpdater, OspfDeletableProperties>;
}
+impl UpdaterType for FabricSection<WireGuardProperties> {
+ type Updater = FabricSectionUpdater<WireGuardPropertiesUpdater, WireGuardDeletableProperties>;
+}
+
/// Enum containing all types of fabrics.
///
/// It utilizes [`FabricSection<T>`] to define all possible types of fabrics. For parsing the
@@ -159,6 +167,7 @@ impl UpdaterType for FabricSection<OspfProperties> {
pub enum Fabric {
Openfabric(FabricSection<OpenfabricProperties>),
Ospf(FabricSection<OspfProperties>),
+ WireGuard(FabricSection<WireGuardProperties>),
}
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<FabricSection<OspfProperties>> for Fabric {
}
}
+impl From<FabricSection<WireGuardProperties>> for Fabric {
+ fn from(section: FabricSection<WireGuardProperties>) -> 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(<FabricSection<OpenfabricProperties> as UpdaterType>::Updater),
Ospf(<FabricSection<OspfProperties> as UpdaterType>::Updater),
+ WireGuard(<FabricSection<WireGuardProperties> 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<Section> for FabricOrNode<Fabric, Node> {
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<OpenfabricProperties>),
OspfFabric(FabricSection<OspfProperties>),
+ WireGuardFabric(FabricSection<WireGuardProperties>),
OpenfabricNode(NodeSection<OpenfabricNodeProperties>),
OspfNode(NodeSection<OspfNodeProperties>),
+ WireGuardNode(NodeSection<WireGuardNodeProperties>),
}
impl From<FabricSection<OpenfabricProperties>> for Section {
@@ -78,6 +83,12 @@ impl From<FabricSection<OspfProperties>> for Section {
}
}
+impl From<FabricSection<WireGuardProperties>> for Section {
+ fn from(section: FabricSection<WireGuardProperties>) -> Self {
+ Self::WireGuardFabric(section)
+ }
+}
+
impl From<NodeSection<OpenfabricNodeProperties>> for Section {
fn from(section: NodeSection<OpenfabricNodeProperties>) -> Self {
Self::OpenfabricNode(section)
@@ -90,11 +101,18 @@ impl From<NodeSection<OspfNodeProperties>> for Section {
}
}
+impl From<NodeSection<WireGuardNodeProperties>> for Section {
+ fn from(section: NodeSection<WireGuardNodeProperties>) -> Self {
+ Self::WireGuardNode(section)
+ }
+}
+
impl From<Fabric> 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<Node> 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<T> NodeSection<T> {
/// 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<std::net::Ipv4Addr> {
self.ip.as_deref().copied()
}
@@ -156,8 +158,8 @@ impl<T> NodeSection<T> {
/// 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<std::net::Ipv6Addr> {
self.ip6.as_deref().copied()
}
@@ -186,6 +188,7 @@ impl<T: ApiType> ApiType for NodeSection<T> {
pub enum Node {
Openfabric(NodeSection<OpenfabricNodeProperties>),
Ospf(NodeSection<OspfNodeProperties>),
+ WireGuard(NodeSection<WireGuardNodeProperties>),
}
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<NodeSection<OspfNodeProperties>> for Node {
}
}
+impl From<NodeSection<WireGuardNodeProperties>> for Node {
+ fn from(value: NodeSection<WireGuardNodeProperties>) -> 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<OpenfabricNodeProperties>),
Ospf(NodeData<OspfNodeProperties>),
+ WireGuard(NodeData<WireGuardNodeProperties>),
}
impl From<super::Node> 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<OspfNodePropertiesUpdater, OspfNodeDeletableProperties>;
}
+ impl UpdaterType for NodeData<WireGuardNodeProperties> {
+ type Updater =
+ NodeDataUpdater<WireGuardNodePropertiesUpdater, WireGuardNodeDeletableProperties>;
+ }
+
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeDataUpdater<T, D> {
#[serde(skip_serializing_if = "Option::is_none")]
@@ -384,6 +406,9 @@ pub mod api {
NodeDataUpdater<OpenfabricNodePropertiesUpdater, OpenfabricNodeDeletableProperties>,
),
Ospf(NodeDataUpdater<OspfNodePropertiesUpdater, OspfNodeDeletableProperties>),
+ WireGuard(
+ NodeDataUpdater<WireGuardNodePropertiesUpdater, WireGuardNodeDeletableProperties>,
+ ),
}
#[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<PersistentKeepalive>,
+ /// Fabric-wide listen port for WireGuard traffic.
+ pub(crate) listen_port: u16,
+}
+
+impl Validatable for FabricSection<WireGuardProperties> {
+ type Error = FabricConfigError;
+
+ /// Validates a [FabricSection<WireGuardProperties>].
+ 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<PropertyString<WireGuardInterfaceProperties>>,
+ /// Listen port for WireGuard on this node. Overrides the fabric-wide setting.
+ pub(crate) listen_port: Option<u16>,
+ // TODO: add public key to "pin" them in the section config?
+}
+
+impl WireGuardNodeProperties {
+ pub fn interfaces(&self) -> impl Iterator<Item = &WireGuardInterfaceProperties> {
+ 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<Item = &mut WireGuardInterfaceProperties> {
+ self.interfaces
+ .iter_mut()
+ .map(|property_string| property_string.deref_mut())
+ }
+}
+
+impl Validatable for NodeSection<WireGuardNodeProperties> {
+ type Error = FabricConfigError;
+
+ /// Validates the [FabricSection<WireGuardNodeProperties>].
+ ///
+ /// 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<Ipv4Cidr>,
+
+ /// 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<Ipv6Cidr>,
+}
+
+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<PersistentKeepalive>) -> 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
prev parent reply other threads:[~2026-01-16 15:34 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-16 15:33 [pve-devel] [PATCH proxmox{, -ve-rs} 00/11] sdn: add wireguard fabric configuration support Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 01/11] serde: implement ini serializer Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 02/11] serde: add base64 module for byte arrays Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 03/11] network-types: add ServiceEndpoint type as host/port tuple abstraction Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 04/11] schema: provide integer schema for node ports Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 05/11] schema: api-types: add ed25519 base64 encoded key schema Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 06/11] wireguard: init configuration support crate Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 07/11] wireguard: implement api for PublicKey Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 08/11] wireguard: make per-peer preshared key optional Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox-ve-rs 09/11] sdn-types: add wireguard-specific PersistentKeepalive api type Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox-ve-rs 10/11] ve-config: fabric: refactor fabric config entry impl using macro Christoph Heiss
2026-01-16 15:33 ` Christoph Heiss [this message]
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=20260116153317.1146323-12-c.heiss@proxmox.com \
--to=c.heiss@proxmox.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox