[pve-devel] [PATCH proxmox-firewall v3 25/39] nftables: add nft client

Stefan Hanreich s.hanreich at proxmox.com
Thu Apr 18 18:14:20 CEST 2024


Add a thin wrapper around nft, which can be used to run commands
defined by the rust types.

Reviewed-by: Lukas Wagner <l.wagner at proxmox.com>
Reviewed-by: Max Carrara <m.carrara at proxmox.com>
Co-authored-by: Wolfgang Bumiller <w.bumiller at proxmox.com>
Signed-off-by: Stefan Hanreich <s.hanreich at proxmox.com>
---
 proxmox-nftables/src/client.rs | 85 ++++++++++++++++++++++++++++++++++
 proxmox-nftables/src/lib.rs    |  2 +
 2 files changed, 87 insertions(+)
 create mode 100644 proxmox-nftables/src/client.rs

diff --git a/proxmox-nftables/src/client.rs b/proxmox-nftables/src/client.rs
new file mode 100644
index 0000000..69e464b
--- /dev/null
+++ b/proxmox-nftables/src/client.rs
@@ -0,0 +1,85 @@
+use std::io::prelude::*;
+use std::process::{Command, Stdio};
+
+use thiserror::Error;
+
+use crate::command::{CommandOutput, Commands};
+
+#[derive(Error, Debug)]
+pub enum NftError {
+    #[error("cannot communicate with child process")]
+    Io(#[from] std::io::Error),
+    #[error("cannot execute nftables commands")]
+    Command(String),
+}
+
+pub struct NftClient;
+
+impl NftClient {
+    fn execute_nft_commands(json: bool, input: &[u8]) -> Result<String, NftError> {
+        let mut command = Command::new("nft");
+
+        if json {
+            command.arg("-j");
+        }
+
+        let mut child = command
+            .arg("-f")
+            .arg("-")
+            .stdin(Stdio::piped())
+            .stdout(Stdio::piped())
+            .stderr(Stdio::piped())
+            .spawn()
+            .map_err(NftError::from)?;
+
+        if let Err(error) = child.stdin.take().expect("can get stdin").write_all(input) {
+            return Err(NftError::from(error));
+        };
+
+        let mut error_output = String::new();
+
+        match child
+            .stderr
+            .take()
+            .expect("can get stderr")
+            .read_to_string(&mut error_output)
+        {
+            Ok(_) if !error_output.is_empty() => {
+                return Err(NftError::Command(error_output));
+            }
+            Err(error) => {
+                return Err(NftError::from(error));
+            }
+            _ => (),
+        };
+
+        let mut output = String::new();
+
+        if let Err(error) = child
+            .stdout
+            .take()
+            .expect("can get stdout")
+            .read_to_string(&mut output)
+        {
+            return Err(NftError::from(error));
+        };
+
+        Ok(output)
+    }
+
+    pub fn run_json_commands(commands: &Commands) -> Result<Option<CommandOutput>, NftError> {
+        let json = serde_json::to_vec(commands).expect("can serialize commands struct");
+        let output = Self::execute_nft_commands(true, &json)?;
+
+        if !output.is_empty() {
+            let parsed_output: Option<CommandOutput> = serde_json::from_str(&output).ok();
+            return Ok(parsed_output);
+        }
+
+        Ok(None)
+    }
+
+    pub fn run_commands(commands: &str) -> Result<String, NftError> {
+        Self::execute_nft_commands(false, commands.as_bytes())
+    }
+}
diff --git a/proxmox-nftables/src/lib.rs b/proxmox-nftables/src/lib.rs
index 60ddb3f..2003e1b 100644
--- a/proxmox-nftables/src/lib.rs
+++ b/proxmox-nftables/src/lib.rs
@@ -1,9 +1,11 @@
+pub mod client;
 pub mod command;
 pub mod expression;
 pub mod helper;
 pub mod statement;
 pub mod types;
 
+pub use client::NftClient;
 pub use command::Command;
 pub use expression::Expression;
 pub use statement::Statement;
-- 
2.39.2




More information about the pve-devel mailing list