[pve-devel] [PATCH proxmox-ve-rs 1/3] frr: add IS-IS frr configuration types
Gabriel Goller
g.goller at proxmox.com
Tue Aug 19 15:19:00 CEST 2025
Add types to generate the IS-IS frr configuration.
These are very similar to the OpenFabric configuration, but we want to
keep them separate because they will diverge in the future.
Signed-off-by: Gabriel Goller <g.goller at proxmox.com>
---
proxmox-frr/src/isis.rs | 90 +++++++++++++++++++++++++++++++++++
proxmox-frr/src/lib.rs | 13 +++++
proxmox-frr/src/route_map.rs | 2 +
proxmox-frr/src/serializer.rs | 37 ++++++++++++++
4 files changed, 142 insertions(+)
create mode 100644 proxmox-frr/src/isis.rs
diff --git a/proxmox-frr/src/isis.rs b/proxmox-frr/src/isis.rs
new file mode 100644
index 000000000000..a407c900ae58
--- /dev/null
+++ b/proxmox-frr/src/isis.rs
@@ -0,0 +1,90 @@
+use std::fmt::Debug;
+use std::fmt::Display;
+
+use proxmox_sdn_types::net::Net;
+
+use thiserror::Error;
+
+use crate::FrrWord;
+use crate::FrrWordError;
+
+/// The name of a IS-IS router. Is an FrrWord.
+#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct IsisRouterName(FrrWord);
+
+impl From<FrrWord> for IsisRouterName {
+ fn from(value: FrrWord) -> Self {
+ Self(value)
+ }
+}
+
+impl IsisRouterName {
+ pub fn new(name: FrrWord) -> Self {
+ Self(name)
+ }
+}
+
+impl Display for IsisRouterName {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "isis {}", self.0)
+ }
+}
+
+/// All the properties a IS-IS router can hold.
+///
+/// These can serialized with a " " space prefix as they are in the `router isis` block.
+#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct IsisRouter {
+ /// The NET address
+ pub net: Net,
+}
+
+impl IsisRouter {
+ pub fn new(net: Net) -> Self {
+ Self { net }
+ }
+
+ pub fn net(&self) -> &Net {
+ &self.net
+ }
+}
+
+/// The IS-IS interface properties.
+///
+/// This struct holds all the IS-IS interface properties. The most important one here is the
+/// fabric_id, which ties the interface to a fabric. When serialized these properties all get
+/// prefixed with a space (" ") as they are inside the interface block. They serialize roughly to:
+///
+/// ```text
+/// interface ens20
+/// ip router isis <fabric_id>
+/// ipv6 router isis <fabric_id>
+/// isis hello-interval <value>
+/// isis hello-multiplier <value>
+/// isis csnp-interval <value>
+/// isis passive <value>
+/// ```
+///
+/// The is_ipv4 and is_ipv6 properties decide if we need to add `ip router isis`, `ipv6
+/// router isis`, or both. An interface can only be part of a single fabric.
+#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct IsisInterface {
+ // Note: an interface can only be a part of a single fabric (so no vec needed here)
+ pub fabric_id: IsisRouterName,
+ pub passive: Option<bool>,
+ // Note: openfabric is very similar to isis, so we can use the same properties here
+ pub hello_interval: Option<proxmox_sdn_types::openfabric::HelloInterval>,
+ pub csnp_interval: Option<proxmox_sdn_types::openfabric::CsnpInterval>,
+ pub hello_multiplier: Option<proxmox_sdn_types::openfabric::HelloMultiplier>,
+ pub point_to_point: bool,
+ pub is_ipv4: bool,
+ pub is_ipv6: bool,
+}
+
+#[derive(Error, Debug)]
+pub enum IsisInterfaceError {
+ #[error("Unknown error converting to IsisInterface")]
+ UnknownError,
+ #[error("Error parsing frr word")]
+ FrrWordParse(#[from] FrrWordError),
+}
diff --git a/proxmox-frr/src/lib.rs b/proxmox-frr/src/lib.rs
index 86101182fafd..daf592e0ad7f 100644
--- a/proxmox-frr/src/lib.rs
+++ b/proxmox-frr/src/lib.rs
@@ -1,3 +1,4 @@
+pub mod isis;
pub mod openfabric;
pub mod ospf;
pub mod route_map;
@@ -25,6 +26,7 @@ use thiserror::Error;
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Router {
Openfabric(openfabric::OpenfabricRouter),
+ Isis(isis::IsisRouter),
Ospf(ospf::OspfRouter),
}
@@ -41,6 +43,7 @@ impl From<openfabric::OpenfabricRouter> for Router {
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum RouterName {
Openfabric(openfabric::OpenfabricRouterName),
+ Isis(isis::IsisRouterName),
Ospf(ospf::OspfRouterName),
}
@@ -54,6 +57,7 @@ impl Display for RouterName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Openfabric(r) => r.fmt(f),
+ Self::Isis(r) => r.fmt(f),
Self::Ospf(r) => r.fmt(f),
}
}
@@ -65,6 +69,7 @@ impl Display for RouterName {
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum InterfaceName {
Openfabric(CommonInterfaceName),
+ Isis(CommonInterfaceName),
Ospf(CommonInterfaceName),
}
@@ -72,6 +77,7 @@ impl Display for InterfaceName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
InterfaceName::Openfabric(frr_word) => frr_word.fmt(f),
+ InterfaceName::Isis(frr_word) => frr_word.fmt(f),
InterfaceName::Ospf(frr_word) => frr_word.fmt(f),
}
}
@@ -86,6 +92,7 @@ impl Display for InterfaceName {
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Interface {
Openfabric(openfabric::OpenfabricInterface),
+ Isis(isis::IsisInterface),
Ospf(ospf::OspfInterface),
}
@@ -95,6 +102,12 @@ impl From<openfabric::OpenfabricInterface> for Interface {
}
}
+impl From<isis::IsisInterface> for Interface {
+ fn from(value: isis::IsisInterface) -> Self {
+ Self::Isis(value)
+ }
+}
+
impl From<ospf::OspfInterface> for Interface {
fn from(value: ospf::OspfInterface) -> Self {
Self::Ospf(value)
diff --git a/proxmox-frr/src/route_map.rs b/proxmox-frr/src/route_map.rs
index 0918a3cead14..4e163a912425 100644
--- a/proxmox-frr/src/route_map.rs
+++ b/proxmox-frr/src/route_map.rs
@@ -201,6 +201,7 @@ pub struct RouteMap {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ProtocolType {
Openfabric,
+ Isis,
Ospf,
}
@@ -208,6 +209,7 @@ impl Display for ProtocolType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ProtocolType::Openfabric => write!(f, "openfabric"),
+ ProtocolType::Isis => write!(f, "isis"),
ProtocolType::Ospf => write!(f, "ospf"),
}
}
diff --git a/proxmox-frr/src/serializer.rs b/proxmox-frr/src/serializer.rs
index f8a3c7238d94..794c43db8888 100644
--- a/proxmox-frr/src/serializer.rs
+++ b/proxmox-frr/src/serializer.rs
@@ -1,6 +1,7 @@
use std::fmt::{self, Write};
use crate::{
+ isis::{IsisInterface, IsisRouter},
openfabric::{OpenfabricInterface, OpenfabricRouter},
ospf::{OspfInterface, OspfRouter},
route_map::{AccessList, AccessListName, ProtocolRouteMap, RouteMap},
@@ -84,6 +85,7 @@ impl FrrSerializer for Interface {
fn serialize(&self, f: &mut FrrConfigBlob<'_>) -> fmt::Result {
match self {
Interface::Openfabric(openfabric_interface) => openfabric_interface.serialize(f)?,
+ Interface::Isis(isis_interface) => isis_interface.serialize(f)?,
Interface::Ospf(ospf_interface) => ospf_interface.serialize(f)?,
}
Ok(())
@@ -114,6 +116,33 @@ impl FrrSerializer for OpenfabricInterface {
}
}
+impl FrrSerializer for IsisInterface {
+ fn serialize(&self, f: &mut FrrConfigBlob<'_>) -> fmt::Result {
+ if self.is_ipv6 {
+ writeln!(f, " ipv6 router {}", self.fabric_id)?;
+ }
+ if self.is_ipv4 {
+ writeln!(f, " ip router {}", self.fabric_id)?;
+ }
+ if self.passive == Some(true) {
+ writeln!(f, " isis passive")?;
+ }
+ if let Some(interval) = self.hello_interval {
+ writeln!(f, " isis hello-interval {interval}",)?;
+ }
+ if let Some(multiplier) = self.hello_multiplier {
+ writeln!(f, " isis hello-multiplier {multiplier}",)?;
+ }
+ if let Some(interval) = self.csnp_interval {
+ writeln!(f, " isis csnp-interval {interval}",)?;
+ }
+ if self.point_to_point {
+ writeln!(f, " isis network point-to-point")?;
+ }
+ Ok(())
+ }
+}
+
impl FrrSerializer for OspfInterface {
fn serialize(&self, f: &mut FrrConfigBlob<'_>) -> fmt::Result {
writeln!(f, " ip ospf {}", self.area)?;
@@ -131,6 +160,7 @@ impl FrrSerializer for Router {
fn serialize(&self, f: &mut FrrConfigBlob<'_>) -> fmt::Result {
match self {
Router::Openfabric(open_fabric_router) => open_fabric_router.serialize(f),
+ Router::Isis(isis_router) => isis_router.serialize(f),
Router::Ospf(ospf_router) => ospf_router.serialize(f),
}
}
@@ -143,6 +173,13 @@ impl FrrSerializer for OpenfabricRouter {
}
}
+impl FrrSerializer for IsisRouter {
+ fn serialize(&self, f: &mut FrrConfigBlob<'_>) -> fmt::Result {
+ writeln!(f, " net {}", self.net())?;
+ Ok(())
+ }
+}
+
impl FrrSerializer for OspfRouter {
fn serialize(&self, f: &mut FrrConfigBlob<'_>) -> fmt::Result {
writeln!(f, " ospf router-id {}", self.router_id())?;
--
2.47.2
More information about the pve-devel
mailing list