From: Gabriel Goller <g.goller@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH proxmox-ve-rs 1/3] frr: add IS-IS frr configuration types
Date: Tue, 19 Aug 2025 15:19:00 +0200 [thread overview]
Message-ID: <20250819131916.324392-2-g.goller@proxmox.com> (raw)
In-Reply-To: <20250819131916.324392-1-g.goller@proxmox.com>
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@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
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
next prev parent reply other threads:[~2025-08-19 13:17 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-08-19 13:18 [pve-devel] [PATCH docs/gui-tests/manager/network/proxmox{-ve-rs, -perl-rs} 0/9] Add IS-IS protocol to fabrics Gabriel Goller
2025-08-19 13:19 ` Gabriel Goller [this message]
2025-08-19 13:19 ` [pve-devel] [PATCH proxmox-ve-rs 2/3] ve-config: add IS-IS fabric config parsing and frr config generation Gabriel Goller
2025-08-19 13:19 ` [pve-devel] [PATCH proxmox-ve-rs 3/3] ve-config: add integration tests for IS-IS fabrics Gabriel Goller
2025-08-19 13:19 ` [pve-devel] [PATCH proxmox-perl-rs 1/1] pve-rs: fabrics: add IS-IS protocol ifupdown config generation Gabriel Goller
2025-08-19 13:19 ` [pve-devel] [PATCH pve-manager 1/2] fabrics: add IS-IS panels Gabriel Goller
2025-08-19 13:19 ` [pve-devel] [PATCH pve-manager 2/2] sdn: add warning about IS-IS controller deprecation Gabriel Goller
2025-08-19 13:19 ` [pve-devel] [PATCH pve-network 1/1] fabrics: add IS-IS api types Gabriel Goller
2025-08-19 13:19 ` [pve-devel] [PATCH pve-docs 1/1] sdn: add section about IS-IS fabric Gabriel Goller
2025-08-19 13:19 ` [pve-devel] [PATCH pve-gui-tests 1/1] fabrics: add screenshots for IS-IS fabric and nodes Gabriel Goller
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=20250819131916.324392-2-g.goller@proxmox.com \
--to=g.goller@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.