diff options
| -rw-r--r-- | Cargo.lock | 140 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | dpibreak.1 | 4 | ||||
| -rw-r--r-- | dpibreak.1.md | 4 | ||||
| -rw-r--r-- | src/main.rs | 15 | ||||
| -rw-r--r-- | src/platform/linux.rs | 71 |
6 files changed, 73 insertions, 162 deletions
@@ -115,7 +115,6 @@ dependencies = [ "etherparse 0.18.2", "iptables", "nfq", - "nftables", "nix 0.27.1", "serde_json", "socket2", @@ -124,12 +123,6 @@ dependencies = [ ] [[package]] -name = "dyn-clone" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" - -[[package]] name = "etherparse" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -154,12 +147,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0399f9d26e5191ce32c498bebd31e7a3ceabc2745f0ac54af3f335126c3f24b3" [[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] name = "iptables" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -214,21 +201,6 @@ dependencies = [ ] [[package]] -name = "nftables" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c57e7343eed9e9330e084eef12651b15be3c8ed7825915a0ffa33736b852bed" -dependencies = [ - "schemars", - "serde", - "serde_json", - "serde_path_to_error", - "strum", - "strum_macros", - "thiserror 2.0.17", -] - -[[package]] name = "nix" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -283,26 +255,6 @@ dependencies = [ ] [[package]] -name = "ref-cast" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "regex" version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -338,38 +290,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] -name = "schemars" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" -dependencies = [ - "dyn-clone", - "ref-cast", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d020396d1d138dc19f1165df7545479dcd58d93810dc5d646a16e55abefa80" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn", -] - -[[package]] name = "serde" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", - "serde_derive", ] [[package]] @@ -393,17 +319,6 @@ dependencies = [ ] [[package]] -name = "serde_derive_internals" -version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "serde_json" version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -417,17 +332,6 @@ dependencies = [ ] [[package]] -name = "serde_path_to_error" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" -dependencies = [ - "itoa", - "serde", - "serde_core", -] - -[[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -444,24 +348,6 @@ dependencies = [ ] [[package]] -name = "strum" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" - -[[package]] -name = "strum_macros" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "syn" version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -478,16 +364,7 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" -dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl", ] [[package]] @@ -502,17 +379,6 @@ dependencies = [ ] [[package]] -name = "thiserror-impl" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "toml" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -534,7 +400,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6b6833a760d1c36b489314a5541a12a39d162dc8341d8f6f400212b96d3df1" dependencies = [ "etherparse 0.13.0", - "thiserror 1.0.69", + "thiserror", "windivert-sys", "windows", ] @@ -546,7 +412,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "832bc4af9272458a8a64395b3aabe10dc4089546486fcbd0e19b9b6d28ba6e54" dependencies = [ "cc", - "thiserror 1.0.69", + "thiserror", "windows", ] @@ -37,7 +37,6 @@ iptables = "0.4" nfq = { git = "https://github.com/nbdd0121/nfq-rs.git", rev = "4efbaa3" } # to use as_raw_fd socket2 = { version = "0.6", features = ["all"] } nix = { version = "0.27.1", features = ["fs", "poll"] } -nftables = "0.6" serde_json = "1.0.145" [target.'cfg(windows)'.dependencies] @@ -68,6 +68,10 @@ handshake latency. (default: 0) NFQUEUE number to attach to. The same queue number is used for IPv4 and IPv6. (default: 1) .TP +.B \-\-nft\-command \fI<string>\fR +.Linux only. +Custom nftables command to be executed. (default: nft) +.TP .B \-\-loglevel \fIdebug|info|warning|error\fR Set the logging level (default: .BR warning ). diff --git a/dpibreak.1.md b/dpibreak.1.md index 189eae7..d43a70d 100644 --- a/dpibreak.1.md +++ b/dpibreak.1.md @@ -50,6 +50,10 @@ handshake latency. (default: 0) NFQUEUE number to attach to. The same queue number is used for IPv4 and IPv6. (default: 1) +**--nft-command *\<string\>*** + +Custom nftables command to be executed. (default: nft) + **--loglevel *debug\|info\|warning\|error*** Set the logging level (default: **warning**). Aliases: **warn** -\> **warning**, **err** -\> **error**. 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(()) |
