From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 054881FF16B for ; Tue, 26 Aug 2025 11:50:58 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 6A53E2E4A6; Tue, 26 Aug 2025 11:50:16 +0200 (CEST) From: Gabriel Goller To: pve-devel@lists.proxmox.com Date: Tue, 26 Aug 2025 11:49:43 +0200 Message-ID: <20250826095000.180173-3-g.goller@proxmox.com> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250826095000.180173-1-g.goller@proxmox.com> References: <20250826095000.180173-1-g.goller@proxmox.com> MIME-Version: 1.0 X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1756201800387 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.005 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 v3 2/3] 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" 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 --- proxmox-frr/Cargo.toml | 1 + proxmox-frr/debian/control | 4 ++ proxmox-frr/src/de/mod.rs | 104 +++++++++++++++++++++++++++++++ proxmox-frr/src/de/openfabric.rs | 42 +++++++++++++ proxmox-frr/src/de/ospf.rs | 57 +++++++++++++++++ proxmox-frr/src/lib.rs | 1 + 6 files changed, 209 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 47fb8bb3969c..d1a24a899b55 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 544fc3ec9ec4..aa74860f2b2f 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 000000000000..43890506253d --- /dev/null +++ b/proxmox-frr/src/de/mod.rs @@ -0,0 +1,104 @@ +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 { + /// Flags + pub flags: i32, + /// If the route is in the FIB (Forward Information Base) + pub fib: Option, + /// IP of the nexthop + pub ip: Option, + /// AFI (either IPv4, IPv6 or something else) + pub afi: String, + /// Index of the outgoing interface + #[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, +} + +/// route +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct Route { + /// Prefix of the route + pub prefix: Cidr, + /// Prefix Length + #[serde(rename = "prefixLen")] + pub prefix_len: u32, + /// Protocol from which the route originates + pub protocol: String, + /// VRF id + #[serde(rename = "vrfId")] + pub vrf_id: u32, + /// VRF name + #[serde(rename = "vrfName")] + pub vrf_name: String, + /// If the route has been selected (if multiple of the same routes from different + /// daemons exist, the one with the shortest distance is selected). + pub selected: Option, + /// Destination Selected + #[serde(rename = "destSelected")] + pub destination_selected: Option, + /// Distance of the route + pub distance: Option, + /// Metric of the route + pub metric: i32, + /// If the route is installed in the kernel routing table + pub installed: Option, + /// The id of the routing table + pub table: i32, + /// Internal Status + #[serde(rename = "internalStatus")] + pub internal_status: i32, + /// Internal Flags + #[serde(rename = "internalFlags")] + pub internal_flags: i32, + /// Internal Nexthop Num, this is the id to lookup the nexthop (visible in e.g. `ip + /// nexthop ls`). + #[serde(rename = "internalNextHopNum")] + pub internal_nexthop_num: i32, + /// Internal Nexthop Active Num + #[serde(rename = "internalNextHopActiveNum")] + pub internal_nexthop_active_num: i32, + /// Nexthop Group Id + #[serde(rename = "nexthopGroupId")] + pub nexthop_group_id: i32, + /// Installed Nexthop Group Id + #[serde(rename = "installedNexthopGroupId")] + pub installed_nexthop_group_id: Option, + /// The uptime of the route + pub uptime: String, + + /// 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, +} + +/// 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 000000000000..99d281f24bcd --- /dev/null +++ b/proxmox-frr/src/de/openfabric.rs @@ -0,0 +1,42 @@ +use serde::{Deserialize, Serialize}; + +/// Adjacency information +/// +/// Circuits are Layer-2 Broadcast domains (Either point-to-point or LAN). +#[derive(Debug, Serialize, Deserialize)] +pub struct Circuit { + /// The circuit id + pub circuit: u32, + /// The hostname of the adjacency peer + pub adj: Option, + /// The interface on which this adjacency exists + pub interface: Option, + /// If the adjacent router is a L1 or L2 router + pub level: Option, + /// The state of the adjacency, this is "Up" when everything is well + pub state: Option, + /// When the adjacency expires + #[serde(rename = "expires-in")] + pub expires_in: Option, + /// Subnetwork Point of Attachment + pub snpa: 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, +} diff --git a/proxmox-frr/src/de/ospf.rs b/proxmox-frr/src/de/ospf.rs new file mode 100644 index 000000000000..0e813ff1e614 --- /dev/null +++ b/proxmox-frr/src/de/ospf.rs @@ -0,0 +1,57 @@ +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, + /// Priority of the Neighbor + #[serde(rename = "nbrPriority")] + pub neighbor_priority: u32, + /// The current state of the adjancecy, this is a complex state machine with many + /// states. The most important ones are "Full" if the full table has been exchanged + /// and "Init" when the adjacency has been formed but no routing information has + /// been exchanged. + pub converged: String, + /// Role of the peer (If he's a designated router (DR) or not (DROther) + pub role: String, + /// Uptime in milliseconds + #[serde(rename = "upTimeInMsec")] + pub up_time_in_msec: u64, + /// Router Dead Interval Timer Due in milliseconds + #[serde(rename = "routerDeadIntervalTimerDueMsec")] + pub router_dead_interval_timer_due_msec: u64, + /// Uptime of the adjacency + #[serde(rename = "upTime")] + pub up_time: String, + /// Expires in countdown + #[serde(rename = "deadTime")] + pub dead_time: String, + /// The remote interface address, so the address of the other peer. + #[serde(rename = "ifaceAddress")] + pub interface_address: 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, + /// Link State Retransmission List Counter + #[serde(rename = "linkStateRetransmissionListCounter")] + pub link_state_retransmission_list_counter: u32, + /// Link State Request List Counter + #[serde(rename = "linkStateRequestListCounter")] + pub link_state_request_list_counter: u32, + /// Database Summary List Counter + #[serde(rename = "databaseSummaryListCounter")] + pub database_summary_list_counter: u32, +} + +/// The parsed OSPF neighbors +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Neighbors { + /// The OSPF neighbors. This is nearly always a ip-address - neighbor mapping. + pub neighbors: HashMap>, +} diff --git a/proxmox-frr/src/lib.rs b/proxmox-frr/src/lib.rs index 35b62cb39c91..2e6ab62c6119 100644 --- a/proxmox-frr/src/lib.rs +++ b/proxmox-frr/src/lib.rs @@ -1 +1,2 @@ +pub mod de; pub mod ser; -- 2.47.2 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel