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 9A24E1FF17E for ; Thu, 30 Oct 2025 16:48:32 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 0C1CB26963; Thu, 30 Oct 2025 16:48:57 +0100 (CET) From: Stefan Hanreich To: pve-devel@lists.proxmox.com Date: Thu, 30 Oct 2025 16:48:11 +0100 Message-ID: <20251030154851.540408-5-s.hanreich@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20251030154851.540408-1-s.hanreich@proxmox.com> References: <20251030154851.540408-1-s.hanreich@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.187 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 KAM_LAZY_DOMAIN_SECURITY 1 Sending domain does not have any anti-forgery methods RCVD_IN_VALIDITY_CERTIFIED_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_RPBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_SAFE_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RDNS_NONE 0.793 Delivered to internal network by a host with no rDNS SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_NONE 0.001 SPF: sender does not publish an SPF Record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [openfabric.rs, lib.rs, mod.rs, ospf.rs] Subject: [pve-devel] [PATCH proxmox-ve-rs 2/6] frr: add deserialization types for openfabric and ospf 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" From: Gabriel Goller These are used to deserialize `vtysh` command outputs. The output is in json if the `json` parameter is appended to the command. Currently the following commands are parsed: * show openfabric neighbor * show ip ospf neighbor * show ip route Signed-off-by: Gabriel Goller Signed-off-by: Stefan Hanreich --- proxmox-frr/Cargo.toml | 1 + proxmox-frr/debian/control | 4 ++ proxmox-frr/src/de/mod.rs | 56 +++++++++++++++++ proxmox-frr/src/de/openfabric.rs | 101 +++++++++++++++++++++++++++++++ proxmox-frr/src/de/ospf.rs | 64 ++++++++++++++++++++ proxmox-frr/src/lib.rs | 1 + 6 files changed, 227 insertions(+) create mode 100644 proxmox-frr/src/de/mod.rs create mode 100644 proxmox-frr/src/de/openfabric.rs create mode 100644 proxmox-frr/src/de/ospf.rs diff --git a/proxmox-frr/Cargo.toml b/proxmox-frr/Cargo.toml index 8b01fa4..8ada547 100644 --- a/proxmox-frr/Cargo.toml +++ b/proxmox-frr/Cargo.toml @@ -13,6 +13,7 @@ rust-version.workspace = true thiserror = { workspace = true } anyhow = "1" tracing = "0.1" +serde = { workspace = true, features = [ "derive" ] } proxmox-network-types = { workspace = true } proxmox-sdn-types = { workspace = true } diff --git a/proxmox-frr/debian/control b/proxmox-frr/debian/control index 9996619..bce0c70 100644 --- a/proxmox-frr/debian/control +++ b/proxmox-frr/debian/control @@ -9,6 +9,8 @@ Build-Depends-Arch: cargo:native , librust-anyhow-1+default-dev , librust-proxmox-network-types-0.1+default-dev (>= 0.1.1-~~) , librust-proxmox-sdn-types-0.1+default-dev , + librust-serde-1+default-dev , + librust-serde-1+derive-dev , librust-thiserror-2+default-dev , librust-tracing-0.1+default-dev Maintainer: Proxmox Support Team @@ -27,6 +29,8 @@ Depends: librust-anyhow-1+default-dev, librust-proxmox-network-types-0.1+default-dev (>= 0.1.1-~~), librust-proxmox-sdn-types-0.1+default-dev, + librust-serde-1+default-dev, + librust-serde-1+derive-dev, librust-thiserror-2+default-dev, librust-tracing-0.1+default-dev Provides: diff --git a/proxmox-frr/src/de/mod.rs b/proxmox-frr/src/de/mod.rs new file mode 100644 index 0000000..a6674d9 --- /dev/null +++ b/proxmox-frr/src/de/mod.rs @@ -0,0 +1,56 @@ +use std::{collections::HashMap, net::IpAddr}; + +use proxmox_network_types::ip_address::Cidr; +use serde::{Deserialize, Serialize}; + +pub mod openfabric; +pub mod ospf; + +/// A nexthop of a route +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct NextHop { + #[serde(rename = "interfaceIndex")] + pub interface_index: i32, + #[serde(rename = "interfaceName")] + /// Name of the outgoing interface + pub interface_name: String, + /// If the nexthop is active + pub active: bool, + /// If the route has the onlink flag. Onlink means that we pretend that the nexthop is + /// directly attached to this link, even if it does not match any interface prefix. + #[serde(rename = "onLink")] + pub on_link: bool, + /// Remap-Source, this rewrites the source address to the following address, if this + /// nexthop is used. + #[serde(rename = "rmapSource")] + pub remap_source: Option, + /// Weight of the nexthop + pub weight: i32, + /// If this nexthop entry is a duplicate of another (the first one has this unset) + pub duplicate: Option, +} + +/// route +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct Route { + /// Array of all the nexthops associated with this route. When you have e.g. two + /// connections between two nodes, there is going to be one route, but two nexthops. + pub nexthops: Vec, + /// Metric of the route + pub metric: i32, + /// Protocol from which the route originates + pub protocol: String, + #[serde(rename = "vrfName")] + pub vrf_name: String, + /// If the route is installed in the kernel routing table + pub installed: Option, +} + +/// Struct to parse zebra routes by FRR. +/// +/// To get the routes from FRR, instead of asking the daemon of every protocol for their +/// routes we simply ask zebra which routes have been inserted and filter them by protocol. +/// The following command is used to accomplish this: `show ip route json`. +/// This struct can be used the deserialize the output of that command. +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Routes(pub HashMap>); diff --git a/proxmox-frr/src/de/openfabric.rs b/proxmox-frr/src/de/openfabric.rs new file mode 100644 index 0000000..837159b --- /dev/null +++ b/proxmox-frr/src/de/openfabric.rs @@ -0,0 +1,101 @@ +use serde::{Deserialize, Serialize}; + +/// State of the adjacency of a OpenFabric neighbor +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub enum AdjacencyState { + Initializing, + Up, + Down, + Unknown, +} + +/// Neighbor Interface +/// +/// Interface used to communicate with a specific neighbor +#[derive(Debug, Serialize, Deserialize)] +pub struct NeighborInterface { + /// The name of the interface + pub name: String, + /// The state of the adjacency, this is "Up" when everything is well + pub state: Option, + /// Time since the last adj-flap (basically the uptime) + #[serde(rename = "last-ago")] + pub last_ago: String, +} + +/// Adjacency information +/// +/// Circuits are Layer-2 Broadcast domains (Either point-to-point or LAN). +#[derive(Debug, Serialize, Deserialize)] +pub struct Circuit { + /// The hostname of the adjacency peer + pub adj: Option, + /// The interface of the neighbor + pub interface: Option, +} + +/// An openfabric area the same as SDN fabric. +#[derive(Debug, Serialize, Deserialize)] +pub struct Area { + /// The are name, this is the same as the fabric_id, so the name of the fabric. + pub area: String, + /// Circuits are Layer-2 Broadcast domains (Either point-to-point or LAN). + pub circuits: Vec, +} + +/// The parsed neighbors. +/// +/// This models the output of: +/// `vtysh -c 'show openfabric neighbor json'`. +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Neighbors { + /// Every sdn fabric is also an openfabric 'area' + pub areas: Vec, +} + +/// The NetworkType of a OpenFabric interface +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub enum NetworkType { + #[serde(rename(deserialize = "p2p", serialize = "Point-To-Point"))] + PointToPoint, + #[serde(rename(deserialize = "lan", serialize = "Broadcast"))] + Lan, + #[serde(rename(deserialize = "loopback", serialize = "Loopback"))] + Loopback, + #[serde(rename = "Unknown")] + Unknown, +} + +/// The State of a OpenFabric interface +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub enum CircuitState { + Init, + Config, + Up, + Unknown, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct Interface { + pub name: String, + pub state: CircuitState, + #[serde(rename = "type")] + pub ty: NetworkType, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct InterfaceCircuits { + pub interface: Interface, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct InterfaceArea { + pub area: String, + pub circuits: Vec, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Interfaces { + pub areas: Vec, +} diff --git a/proxmox-frr/src/de/ospf.rs b/proxmox-frr/src/de/ospf.rs new file mode 100644 index 0000000..a75609d --- /dev/null +++ b/proxmox-frr/src/de/ospf.rs @@ -0,0 +1,64 @@ +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +/// Information about the Neighbor (Peer) of the Adjacency. +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Neighbor { + /// The full state of the neighbor. This is "{converged}/{role}". + #[serde(rename = "nbrState")] + pub neighbor_state: String, + /// The uptime of the interface + #[serde(rename = "upTime")] + pub up_time: String, + /// The interface name of this adjacency. This is always a combination of interface + /// name and address. e.g. "ens21:5.5.5.3". + #[serde(rename = "ifaceName")] + pub interface_name: String, +} + +/// The parsed OSPF neighbors +#[derive(Debug, Deserialize, Default)] +pub struct Neighbors { + /// The OSPF neighbors. This is nearly always a ip-address - neighbor mapping. + pub neighbors: HashMap>, +} + +/// All possible OSPF network-types that can be returned from frr +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub enum NetworkType { + #[serde(rename = "Null")] + Null, + #[serde(rename(deserialize = "POINTOPOINT", serialize = "Point-To-Point"))] + PointToPoint, + #[serde(rename(deserialize = "BROADCAST", serialize = "Broadcast"))] + Broadcast, + #[serde(rename = "NBMA")] + Nbma, + #[serde(rename(deserialize = "POINTOMULTIPOINT", serialize = "Point-To-Multipoint"))] + PointToMultipoint, + #[serde(rename(deserialize = "VIRTUALLINK", serialize = "Virtual Link"))] + VirtualLink, + #[serde(rename(deserialize = "LOOPBACK", serialize = "Loopback"))] + Loopback, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Interface { + /// The interface state + pub if_up: bool, + /// The network type (e.g. point-to-point, broadcast, etc.) + /// + /// Note there is also a "state" property, but that models the state of the interface (ism), + /// which can also be "point-to-point", but it can also be e.g. "Down" or e.g. "DROther"! + /// So networkType is the configured network type and state is the state of interface, which + /// sometimes is the same as the networkType. + pub network_type: NetworkType, +} + +#[derive(Debug, Deserialize, Default)] +pub struct Interfaces { + pub interfaces: HashMap, +} diff --git a/proxmox-frr/src/lib.rs b/proxmox-frr/src/lib.rs index 35b62cb..2e6ab62 100644 --- a/proxmox-frr/src/lib.rs +++ b/proxmox-frr/src/lib.rs @@ -1 +1,2 @@ +pub mod de; pub mod ser; -- 2.47.3 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel