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 8E3DF1FF16F for ; Tue, 19 Aug 2025 15:17:51 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 692FC1566B; Tue, 19 Aug 2025 15:19:26 +0200 (CEST) From: Gabriel Goller To: pve-devel@lists.proxmox.com Date: Tue, 19 Aug 2025 15:19:00 +0200 Message-ID: <20250819131916.324392-2-g.goller@proxmox.com> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250819131916.324392-1-g.goller@proxmox.com> References: <20250819131916.324392-1-g.goller@proxmox.com> MIME-Version: 1.0 X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1755609518154 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.006 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 1/3] frr: add IS-IS frr configuration 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" 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 --- 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 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 +/// ipv6 router isis +/// isis hello-interval +/// isis hello-multiplier +/// isis csnp-interval +/// isis passive +/// ``` +/// +/// 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, + // Note: openfabric is very similar to isis, so we can use the same properties here + pub hello_interval: Option, + pub csnp_interval: Option, + pub hello_multiplier: Option, + 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 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 for Interface { } } +impl From for Interface { + fn from(value: isis::IsisInterface) -> Self { + Self::Isis(value) + } +} + impl From 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