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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.