diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.rs | 15 | ||||
| -rw-r--r-- | src/platform/linux.rs | 71 |
2 files changed, 62 insertions, 24 deletions
diff --git a/src/main.rs b/src/main.rs index 75806b4..e8d2f78 100644 --- a/src/main.rs +++ b/src/main.rs @@ -163,9 +163,10 @@ fn usage() { r#"Usage: dpibreak [OPTIONS] Options: - --delay-ms <u64> (default: 0) - --queue-num <u16> (linux only, default: 1) - --loglevel <debug|info|warning|error> (default: warning) + --delay-ms <u64> (default: 0) + --queue-num <u16> (linux only, default: 1) + --nft-command <string> (linux only, default: nft) + --loglevel <debug|info|warning|error> (default: warning) --no-splash Do not print splash messages -h, --help Show this help"# ); @@ -181,6 +182,8 @@ fn parse_args_1() -> Result<()> { let mut log_level: log::LogLevel = LogLevel::Warning; #[cfg(target_os = "linux")] let mut queue_num: u16 = 1; + #[cfg(target_os = "linux")] + let mut nft_command = String::from("nft"); let mut args = std::env::args().skip(1); // program name @@ -196,6 +199,9 @@ fn parse_args_1() -> Result<()> { #[cfg(target_os = "linux")] "--queue-num" => { queue_num = take_value(&mut args, argv)?; } + #[cfg(target_os = "linux")] + "--nft-command" => { nft_command = take_value(&mut args, argv)?; } + _ => { return Err(anyhow!("argument: unknown: {}", arg)); } } } @@ -207,6 +213,9 @@ fn parse_args_1() -> Result<()> { #[cfg(target_os = "linux")] platform::QUEUE_NUM.set(queue_num).map_err(|_| anyhow!("QUEUE_NUM already initialized"))?; + #[cfg(target_os = "linux")] + platform::NFT_COMMAND.set(nft_command).map_err(|_| anyhow!("NFT_COMMAND already initialized"))?; + Ok(()) } diff --git a/src/platform/linux.rs b/src/platform/linux.rs index e04a6e4..259643e 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -22,22 +22,55 @@ use std::sync::{ OnceLock, LazyLock }; -use std::process::Command; -use anyhow::{Result, Error, anyhow}; +use std::process::{Command, Stdio}; +use std::io::Write; +use anyhow::{Result, Error, Context, anyhow}; use crate::{log::LogLevel, log_println, splash, MESSAGE_AT_RUN}; pub static IS_U32_SUPPORTED: AtomicBool = AtomicBool::new(false); pub static IS_XT_U32_LOADED_BY_US: AtomicBool = AtomicBool::new(false); static IS_NFT_NOT_SUPPORTED: AtomicBool = AtomicBool::new(false); -pub static QUEUE_NUM: OnceLock<u16> = OnceLock::new(); - const DPIBREAK_CHAIN: &str = "DPIBREAK"; +pub static QUEUE_NUM: OnceLock<u16> = OnceLock::new(); +pub static NFT_COMMAND: OnceLock<String> = OnceLock::new(); + fn queue_num() -> u16 { *QUEUE_NUM.get().expect("QUEUE_NUM not initialized") } +fn nft_command() -> &'static str { + NFT_COMMAND.get().expect("NFT_COMMAND not initialized").as_str() +} + +/// Apply json format nft rules with `nft_command() -j -f -`. +fn apply_nft_rules(rule: &str) -> Result<()> { + let mut child = Command::new(nft_command()) + .args(&["-j", "-f", "-"]) + .stdin(Stdio::piped()) + .stdout(Stdio::null()) + .stderr(Stdio::piped()) + .spawn() + .context("failed to spawn nft process")?; + + { + let mut stdin = child.stdin.take().context("failed to take stdin")?; + stdin.write_all(rule.as_bytes()).context("failed to write rule to nft")?; + } // Close the pipe + + let output = child.wait_with_output().context("failed to wait for nft")?; + + match output.status.code() { + Some(0) => Ok(()), + Some(code) => + Err(anyhow!("{} exited with status {}: {}", nft_command(), code, + String::from_utf8_lossy(&output.stderr))), + None => + Err(anyhow!("{} terminated by signal", nft_command())) + } +} + fn is_xt_u32_loaded() -> bool { std::fs::read_to_string("/proc/modules") .map(|s| s.lines().any(|l| l.starts_with("xt_u32 "))) @@ -127,9 +160,7 @@ fn cleanup_iptables_rules(ipt: &IPTables) -> Result<()> { const DPIBREAK_TABLE: &str = "dpibreak"; fn install_nft_rules() -> Result<()> { - use nftables::helper; - - let json = serde_json::json!( + let rule = serde_json::json!( { "nftables": [ {"add": {"table": {"family": "inet", "name": DPIBREAK_TABLE}}}, @@ -210,10 +241,7 @@ fn install_nft_rules() -> Result<()> { } ); - let json_str = serde_json::to_string(&json)?; - - helper::apply_ruleset_raw(&json_str, helper::DEFAULT_NFT, - helper::DEFAULT_ARGS)?; + apply_nft_rules(&serde_json::to_string(&rule)?)?; // clienthello filtered by nft IS_U32_SUPPORTED.store(true, Ordering::Relaxed); @@ -251,17 +279,18 @@ fn cleanup_rules() -> Result<()> { cleanup_iptables_rules(&ipt)?; cleanup_iptables_rules(&ip6)?; } else { - use nftables::*; - - let mut nft = batch::Batch::new(); - // nft delete table inet dpibreak - nft.delete(schema::NfListObject::Table(schema::Table { - family: types::NfFamily::INet, - name: DPIBREAK_TABLE.into(), - ..Default::default() - })); - _ = helper::apply_ruleset(&nft.to_nftables()); + let rule = serde_json::json!({ + "nftables": [ + {"delete": {"table": {"family": "inet", "name": DPIBREAK_TABLE}}} + ] + }); + match apply_nft_rules(&serde_json::to_string(&rule)?) { + Ok(_) => + log_println!(LogLevel::Info, "cleanup: nftables: delete table inet {}", DPIBREAK_TABLE), + Err(e) => + log_println!(LogLevel::Warning, "cleanup: nftables: {}", e.to_string().trim()), + } } Ok(()) |
