mirror of
https://framagit.org/ppom/reaction
synced 2026-03-14 20:55:47 +01:00
nftables: Fix compilation errors and actually use libnftables
This commit is contained in:
parent
0cd765251a
commit
5b6cc35deb
3 changed files with 61 additions and 42 deletions
|
|
@ -8,7 +8,6 @@ use std::{
|
|||
use nftables::{
|
||||
batch::Batch,
|
||||
expr::Expression,
|
||||
helper::apply_ruleset_async,
|
||||
schema::{Element, NfListObject, Rule, SetFlag, SetType, SetTypeValue},
|
||||
stmt::Statement,
|
||||
types::{NfFamily, NfHook},
|
||||
|
|
@ -247,13 +246,14 @@ impl Set {
|
|||
if let Some(set) = set {
|
||||
let family = NfFamily::INet;
|
||||
let table = Cow::from("reaction");
|
||||
let name = Cow::from(set.as_str());
|
||||
|
||||
// create set
|
||||
batch.add(NfListObject::<'a>::Set(Box::new(nftables::schema::Set {
|
||||
batch.add(NfListObject::<'a>::Set(Box::new(nftables::schema::Set::<
|
||||
'a,
|
||||
> {
|
||||
family,
|
||||
table: table.to_owned(),
|
||||
name,
|
||||
name: Cow::Owned(set.to_owned()),
|
||||
// TODO Try a set which is both ipv4 and ipv6?
|
||||
set_type: SetTypeValue::Single(match version {
|
||||
Version::IPv4 => SetType::Ipv4Addr,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ use std::{
|
|||
collections::{BTreeMap, BTreeSet},
|
||||
};
|
||||
|
||||
use libnftables1_sys::Nftables;
|
||||
use nftables::{
|
||||
batch::Batch,
|
||||
schema::{Chain, NfListObject, Table},
|
||||
|
|
@ -15,7 +14,10 @@ use reaction_plugin::{
|
|||
};
|
||||
use remoc::rtc;
|
||||
|
||||
use crate::action::{Action, ActionOptions, Set, SetOptions};
|
||||
use crate::{
|
||||
action::{Action, ActionOptions, Set, SetOptions},
|
||||
nft::NftClient,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
@ -32,6 +34,7 @@ async fn main() {
|
|||
|
||||
#[derive(Default)]
|
||||
struct Plugin {
|
||||
nft: NftClient,
|
||||
sets: Vec<Set>,
|
||||
actions: Vec<Action>,
|
||||
shutdown: ShutdownController,
|
||||
|
|
@ -90,8 +93,12 @@ impl PluginInfo for Plugin {
|
|||
.map_err(|err| format!("set {}: {err}", options.set))?;
|
||||
|
||||
let (tx, rx) = remoc::rch::mpsc::channel(1);
|
||||
self.actions
|
||||
.push(Action::new(self.shutdown.token(), rx, options)?);
|
||||
self.actions.push(Action::new(
|
||||
self.nft.clone(),
|
||||
self.shutdown.token(),
|
||||
rx,
|
||||
options,
|
||||
)?);
|
||||
|
||||
ret_actions.push(ActionImpl { tx });
|
||||
}
|
||||
|
|
@ -133,7 +140,7 @@ impl PluginInfo for Plugin {
|
|||
}
|
||||
|
||||
// TODO apply batch
|
||||
Nftables::new();
|
||||
self.nft.send(batch).await?;
|
||||
|
||||
// Launch a task that will destroy the table on shutdown
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,26 +7,65 @@ use libnftables1_sys::Nftables;
|
|||
use nftables::batch::Batch;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
|
||||
pub fn nftables_thread() -> NftClient {
|
||||
let (tx, mut rx) = mpsc::channel(10);
|
||||
/// A client with a dedicated server thread to libnftables.
|
||||
/// Calling [`Default::default()`] spawns a new server thread.
|
||||
/// Cloning just creates a new client to the same server thread.
|
||||
#[derive(Clone)]
|
||||
pub struct NftClient {
|
||||
tx: mpsc::Sender<NftCommand>,
|
||||
}
|
||||
|
||||
thread::spawn(move || {
|
||||
let mut conn = Nftables::new();
|
||||
impl Default for NftClient {
|
||||
fn default() -> Self {
|
||||
let (tx, mut rx) = mpsc::channel(10);
|
||||
|
||||
while let Some(NftCommand { json, ret }) = rx.blocking_recv() {
|
||||
let (rc, output, error) = conn.run_cmd(json.as_ptr());
|
||||
let res = match rc {
|
||||
0 => to_rust_string(output)
|
||||
.ok_or_else(|| "unknown ok (rc = 0 but no output buffer)".into()),
|
||||
_ => to_rust_string(error)
|
||||
.map(|err| format!("error (rc = {rc}: {err})"))
|
||||
.ok_or_else(|| format!("unknown error (rc = {rc} but no error buffer)")),
|
||||
};
|
||||
ret.send(res);
|
||||
}
|
||||
});
|
||||
thread::spawn(move || {
|
||||
let mut conn = Nftables::new();
|
||||
|
||||
NftClient { tx }
|
||||
while let Some(NftCommand { json, ret }) = rx.blocking_recv() {
|
||||
let (rc, output, error) = conn.run_cmd(json.as_ptr());
|
||||
let res = match rc {
|
||||
0 => to_rust_string(output)
|
||||
.ok_or_else(|| "unknown ok (rc = 0 but no output buffer)".into()),
|
||||
_ => to_rust_string(error)
|
||||
.map(|err| format!("error (rc = {rc}: {err})"))
|
||||
.ok_or_else(|| format!("unknown error (rc = {rc} but no error buffer)")),
|
||||
};
|
||||
let _ = ret.send(res);
|
||||
}
|
||||
});
|
||||
|
||||
NftClient { tx }
|
||||
}
|
||||
}
|
||||
|
||||
impl NftClient {
|
||||
/// Send a batch to nftables.
|
||||
pub async fn send(&self, batch: Batch<'_>) -> Result<String, String> {
|
||||
// convert JSON to CString
|
||||
let mut json = serde_json::to_vec(&batch.to_nftables())
|
||||
.map_err(|err| format!("couldn't build json to send to nftables: {err}"))?;
|
||||
json.push('\0' as u8);
|
||||
let json = CString::from_vec_with_nul(json)
|
||||
.map_err(|err| format!("invalid json with null char: {err}"))?;
|
||||
|
||||
// Send command
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let command = NftCommand { json, ret: tx };
|
||||
self.tx
|
||||
.send(command)
|
||||
.await
|
||||
.map_err(|err| format!("nftables thread has quit, can't send command: {err}"))?;
|
||||
|
||||
// Wait for result
|
||||
rx.await
|
||||
.map_err(|_| format!("nftables thread has quit, no response for command"))?
|
||||
}
|
||||
}
|
||||
|
||||
struct NftCommand {
|
||||
json: CString,
|
||||
ret: oneshot::Sender<Result<String, String>>,
|
||||
}
|
||||
|
||||
fn to_rust_string(c_ptr: *const i8) -> Option<String> {
|
||||
|
|
@ -40,30 +79,3 @@ fn to_rust_string(c_ptr: *const i8) -> Option<String> {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NftClient {
|
||||
tx: mpsc::Sender<NftCommand>,
|
||||
}
|
||||
|
||||
impl NftClient {
|
||||
pub async fn send(&self, batch: Batch<'_>) -> Result<String, String> {
|
||||
// convert JSON to CString
|
||||
let mut json = serde_json::to_vec(&batch.to_nftables())
|
||||
.map_err(|err| format!("couldn't build json to send to nftables: {err}"))?;
|
||||
json.push('\0' as u8);
|
||||
let json = CString::from_vec_with_nul(json)
|
||||
.map_err(|err| format!("invalid json with null char: {err}"))?;
|
||||
// Send command
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let command = NftCommand { json, ret: tx };
|
||||
self.tx.send(command).await;
|
||||
// Wait for result
|
||||
rx.await
|
||||
.map_err(|err| format!("nftables thread has quit: {err}"))?
|
||||
}
|
||||
}
|
||||
|
||||
struct NftCommand {
|
||||
json: CString,
|
||||
ret: oneshot::Sender<Result<String, String>>,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue