iptables-0.5.1/.cargo_vcs_info.json0000644000000001360000000000100126420ustar { "git": { "sha1": "403afe09e904bc5f2867b125dbe21ff3165a50c6" }, "path_in_vcs": "" }iptables-0.5.1/.github/workflows/build.yml000064400000000000000000000010411046102023000166450ustar 00000000000000name: Build on: push: branches: [master] pull_request: branches: [master] jobs: linux: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: toolchain: stable components: rustfmt, clippy - run: sudo -E `which cargo` test -j`nproc` -- --test-threads 1 - run: sudo -E `which cargo` test -j`nproc` -- --ignored --test-threads 1 - run: sudo -E `which cargo` fmt -- --check - run: sudo -E `which cargo` clippy -j`nproc` iptables-0.5.1/.gitignore000064400000000000000000000000301046102023000134130ustar 00000000000000target Cargo.lock .idea iptables-0.5.1/Cargo.toml0000644000000021600000000000100106370ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "iptables" version = "0.5.1" authors = [ "Navid Fathollahzade ", "Pit Kleyersburg ", ] description = "Rust bindings for iptables" homepage = "https://github.com/yaa110/rust-iptables" documentation = "https://github.com/yaa110/rust-iptables" readme = "README.md" keywords = [ "iptables", "netfilter", "bindings", ] categories = ["api-bindings"] license = "MIT" repository = "https://github.com/yaa110/rust-iptables" [lib] name = "iptables" [dependencies.lazy_static] version = "1" [dependencies.nix] version = "0.27" features = ["fs"] [dependencies.regex] version = "1" iptables-0.5.1/Cargo.toml.orig000064400000000000000000000011301046102023000143140ustar 00000000000000[package] authors = ["Navid Fathollahzade ", "Pit Kleyersburg "] categories = ["api-bindings"] description = "Rust bindings for iptables" documentation = "https://github.com/yaa110/rust-iptables" edition = "2021" homepage = "https://github.com/yaa110/rust-iptables" keywords = ["iptables", "netfilter", "bindings"] license = "MIT" name = "iptables" readme = "README.md" repository = "https://github.com/yaa110/rust-iptables" version = "0.5.1" [lib] name = "iptables" [dependencies] lazy_static = "1" nix = {version = "0.27", features = ["fs"]} regex = "1" iptables-0.5.1/LICENSE000064400000000000000000000020751046102023000124430ustar 00000000000000The MIT License (MIT) Copyright (c) 2017 Navid Fathollahzade Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.iptables-0.5.1/README.md000064400000000000000000000025601046102023000127140ustar 00000000000000Rust iptables ============= [![crates.io](https://img.shields.io/crates/v/iptables.svg)](https://crates.io/crates/iptables) [![Documentation](https://img.shields.io/badge/Docs-iptables-blue.svg)](https://docs.rs/iptables) [![Build](https://github.com/yaa110/rust-iptables/actions/workflows/build.yml/badge.svg)](https://github.com/yaa110/rust-iptables/actions/workflows/build.yml) [![License](http://img.shields.io/:license-mit-blue.svg)](https://github.com/yaa110/rust-iptables/blob/master/LICENSE) This crate provides bindings for [iptables](https://www.netfilter.org/projects/iptables/index.html) application in Linux (inspired by [go-iptables](https://github.com/coreos/go-iptables)). This crate uses iptables binary to manipulate chains and tables. This source code is licensed under MIT license that can be found in the LICENSE file. ```toml [dependencies] iptables = "*" ``` ## Getting started 1- Import the crate `iptables` and manipulate chains: ```rust let ipt = iptables::new(false).unwrap(); assert!(ipt.new_chain("nat", "NEWCHAINNAME").is_ok()); assert!(ipt.append("nat", "NEWCHAINNAME", "-j ACCEPT").is_ok()); assert!(ipt.exists("nat", "NEWCHAINNAME", "-j ACCEPT").unwrap()); assert!(ipt.delete("nat", "NEWCHAINNAME", "-j ACCEPT").is_ok()); assert!(ipt.delete_chain("nat", "NEWCHAINNAME").is_ok()); ``` For more information, please check the test file in `tests` folder. iptables-0.5.1/src/error.rs000064400000000000000000000011501046102023000137150ustar 00000000000000use std::convert::From; use std::error::Error; use std::fmt; use std::process::Output; #[derive(Debug)] pub struct IptablesError { pub code: i32, pub msg: String, } impl fmt::Display for IptablesError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "code: {}, msg: {}", self.code, self.msg) } } impl From for IptablesError { fn from(output: Output) -> Self { Self { code: output.status.code().unwrap_or(-1), msg: String::from_utf8_lossy(output.stderr.as_slice()).into(), } } } impl Error for IptablesError {} iptables-0.5.1/src/lib.rs000064400000000000000000000356301046102023000133440ustar 00000000000000// In the name of Allah //! Provides bindings for [iptables](https://www.netfilter.org/projects/iptables/index.html) application in Linux. //! This crate uses iptables binary to manipulate chains and tables. //! This source code is licensed under MIT license that can be found in the LICENSE file. //! //! # Example //! ``` //! let ipt = iptables::new(false).unwrap(); //! assert!(ipt.new_chain("nat", "NEWCHAINNAME").is_ok()); //! assert!(ipt.append("nat", "NEWCHAINNAME", "-j ACCEPT").is_ok()); //! assert!(ipt.exists("nat", "NEWCHAINNAME", "-j ACCEPT").unwrap()); //! assert!(ipt.delete("nat", "NEWCHAINNAME", "-j ACCEPT").is_ok()); //! assert!(ipt.delete_chain("nat", "NEWCHAINNAME").is_ok()); //! ``` pub mod error; use error::IptablesError; use lazy_static::lazy_static; use nix::fcntl::{flock, FlockArg}; use regex::{Match, Regex}; use std::convert::From; use std::error::Error; use std::ffi::OsStr; use std::fs::File; use std::os::unix::io::AsRawFd; use std::process::{Command, Output}; use std::vec::Vec; // List of built-in chains taken from: man 8 iptables const BUILTIN_CHAINS_FILTER: &[&str] = &["INPUT", "FORWARD", "OUTPUT"]; const BUILTIN_CHAINS_MANGLE: &[&str] = &["PREROUTING", "OUTPUT", "INPUT", "FORWARD", "POSTROUTING"]; const BUILTIN_CHAINS_NAT: &[&str] = &["PREROUTING", "POSTROUTING", "OUTPUT"]; const BUILTIN_CHAINS_RAW: &[&str] = &["PREROUTING", "OUTPUT"]; const BUILTIN_CHAINS_SECURITY: &[&str] = &["INPUT", "OUTPUT", "FORWARD"]; lazy_static! { static ref RE_SPLIT: Regex = Regex::new(r#"["'].+?["']|[^ ]+"#).unwrap(); } trait SplitQuoted { fn split_quoted(&self) -> Vec<&str>; } impl SplitQuoted for str { fn split_quoted(&self) -> Vec<&str> { RE_SPLIT // Iterate over matched segments .find_iter(self) // Get match as str .map(|m| Match::as_str(&m)) // Remove any surrounding quotes (they will be reinserted by `Command`) .map(|s| s.trim_matches(|c| c == '"' || c == '\'')) // Collect .collect::>() } } fn error_from_str(msg: &str) -> Box { msg.into() } fn output_to_result(output: Output) -> Result<(), Box> { if !output.status.success() { return Err(Box::new(IptablesError::from(output))); } Ok(()) } fn get_builtin_chains(table: &str) -> Result<&[&str], Box> { match table { "filter" => Ok(BUILTIN_CHAINS_FILTER), "mangle" => Ok(BUILTIN_CHAINS_MANGLE), "nat" => Ok(BUILTIN_CHAINS_NAT), "raw" => Ok(BUILTIN_CHAINS_RAW), "security" => Ok(BUILTIN_CHAINS_SECURITY), _ => Err(error_from_str("given table is not supported by iptables")), } } /// Contains the iptables command and shows if it supports -w and -C options. /// Use `new` method to create a new instance of this struct. pub struct IPTables { /// The utility command which must be 'iptables' or 'ip6tables'. pub cmd: &'static str, /// Indicates if iptables has -C (--check) option pub has_check: bool, /// Indicates if iptables has -w (--wait) option pub has_wait: bool, /// Indicates if iptables will be run with -n (--numeric) option pub is_numeric: bool, } /// Returns `None` because iptables only works on linux #[cfg(not(target_os = "linux"))] pub fn new(is_ipv6: bool) -> Result> { Err(error_from_str("iptables only works on Linux")) } /// Creates a new `IPTables` Result with the command of 'iptables' if `is_ipv6` is `false`, otherwise the command is 'ip6tables'. #[cfg(target_os = "linux")] pub fn new(is_ipv6: bool) -> Result> { let cmd = if is_ipv6 { "ip6tables" } else { "iptables" }; let version_output = Command::new(cmd).arg("--version").output()?; let re = Regex::new(r"v(\d+)\.(\d+)\.(\d+)")?; let version_string = String::from_utf8_lossy(version_output.stdout.as_slice()); let versions = re .captures(&version_string) .ok_or("invalid version number")?; let v_major = versions .get(1) .ok_or("unable to get major version number")? .as_str() .parse::()?; let v_minor = versions .get(2) .ok_or("unable to get minor version number")? .as_str() .parse::()?; let v_patch = versions .get(3) .ok_or("unable to get patch version number")? .as_str() .parse::()?; Ok(IPTables { cmd, has_check: (v_major > 1) || (v_major == 1 && v_minor > 4) || (v_major == 1 && v_minor == 4 && v_patch > 10), has_wait: (v_major > 1) || (v_major == 1 && v_minor > 4) || (v_major == 1 && v_minor == 4 && v_patch > 19), is_numeric: false, }) } impl IPTables { /// Get the default policy for a table/chain. pub fn get_policy(&self, table: &str, chain: &str) -> Result> { let builtin_chains = get_builtin_chains(table)?; if !builtin_chains.iter().as_slice().contains(&chain) { return Err(error_from_str( "given chain is not a default chain in the given table, can't get policy", )); } let stdout = match self.is_numeric { false => self.run(&["-t", table, "-L", chain])?.stdout, true => self.run(&["-t", table, "-L", chain, "-n"])?.stdout, }; let output = String::from_utf8_lossy(stdout.as_slice()); for item in output.trim().split('\n') { let fields = item.split(' ').collect::>(); if fields.len() > 1 && fields[0] == "Chain" && fields[1] == chain { return Ok(fields[3].replace(")", "")); } } Err(error_from_str( "could not find the default policy for table and chain", )) } /// Set the default policy for a table/chain. pub fn set_policy(&self, table: &str, chain: &str, policy: &str) -> Result<(), Box> { let builtin_chains = get_builtin_chains(table)?; if !builtin_chains.iter().as_slice().contains(&chain) { return Err(error_from_str( "given chain is not a default chain in the given table, can't set policy", )); } self.run(&["-t", table, "-P", chain, policy]) .and_then(output_to_result) } /// Executes a given `command` on the chain. /// Returns the command output if successful. pub fn execute(&self, table: &str, command: &str) -> Result> { self.run(&[&["-t", table], command.split_quoted().as_slice()].concat()) } /// Checks for the existence of the `rule` in the table/chain. /// Returns true if the rule exists. #[cfg(target_os = "linux")] pub fn exists(&self, table: &str, chain: &str, rule: &str) -> Result> { if !self.has_check { return self.exists_old_version(table, chain, rule); } self.run(&[&["-t", table, "-C", chain], rule.split_quoted().as_slice()].concat()) .map(|output| output.status.success()) } /// Checks for the existence of the `chain` in the table. /// Returns true if the chain exists. #[cfg(target_os = "linux")] pub fn chain_exists(&self, table: &str, chain: &str) -> Result> { match self.is_numeric { false => self .run(&["-t", table, "-L", chain]) .map(|output| output.status.success()), true => self .run(&["-t", table, "-L", chain, "-n"]) .map(|output| output.status.success()), } } fn exists_old_version( &self, table: &str, chain: &str, rule: &str, ) -> Result> { match self.is_numeric { false => self.run(&["-t", table, "-S"]).map(|output| { String::from_utf8_lossy(&output.stdout).contains(&format!("-A {} {}", chain, rule)) }), true => self.run(&["-t", table, "-S", "-n"]).map(|output| { String::from_utf8_lossy(&output.stdout).contains(&format!("-A {} {}", chain, rule)) }), } } /// Inserts `rule` in the `position` to the table/chain. pub fn insert( &self, table: &str, chain: &str, rule: &str, position: i32, ) -> Result<(), Box> { self.run( &[ &["-t", table, "-I", chain, &position.to_string()], rule.split_quoted().as_slice(), ] .concat(), ) .and_then(output_to_result) } /// Inserts `rule` in the `position` to the table/chain if it does not exist. pub fn insert_unique( &self, table: &str, chain: &str, rule: &str, position: i32, ) -> Result<(), Box> { if self.exists(table, chain, rule)? { return Err(error_from_str("the rule exists in the table/chain")); } self.insert(table, chain, rule, position) } /// Replaces `rule` in the `position` to the table/chain. pub fn replace( &self, table: &str, chain: &str, rule: &str, position: i32, ) -> Result<(), Box> { self.run( &[ &["-t", table, "-R", chain, &position.to_string()], rule.split_quoted().as_slice(), ] .concat(), ) .and_then(output_to_result) } /// Appends `rule` to the table/chain. pub fn append(&self, table: &str, chain: &str, rule: &str) -> Result<(), Box> { self.run(&[&["-t", table, "-A", chain], rule.split_quoted().as_slice()].concat()) .and_then(output_to_result) } /// Appends `rule` to the table/chain if it does not exist. pub fn append_unique( &self, table: &str, chain: &str, rule: &str, ) -> Result<(), Box> { if self.exists(table, chain, rule)? { return Err(error_from_str("the rule exists in the table/chain")); } self.append(table, chain, rule) } /// Appends or replaces `rule` to the table/chain if it does not exist. pub fn append_replace( &self, table: &str, chain: &str, rule: &str, ) -> Result<(), Box> { if self.exists(table, chain, rule)? { self.delete(table, chain, rule)?; } self.append(table, chain, rule) } /// Deletes `rule` from the table/chain. pub fn delete(&self, table: &str, chain: &str, rule: &str) -> Result<(), Box> { self.run(&[&["-t", table, "-D", chain], rule.split_quoted().as_slice()].concat()) .and_then(output_to_result) } /// Deletes all repetition of the `rule` from the table/chain. pub fn delete_all(&self, table: &str, chain: &str, rule: &str) -> Result<(), Box> { while self.exists(table, chain, rule)? { self.delete(table, chain, rule)?; } Ok(()) } /// Lists rules in the table/chain. pub fn list(&self, table: &str, chain: &str) -> Result, Box> { match self.is_numeric { false => self.get_list(&["-t", table, "-S", chain]), true => self.get_list(&["-t", table, "-S", chain, "-n"]), } } /// Lists rules in the table. pub fn list_table(&self, table: &str) -> Result, Box> { match self.is_numeric { false => self.get_list(&["-t", table, "-S"]), true => self.get_list(&["-t", table, "-S", "-n"]), } } /// Lists the name of each chain in the table. pub fn list_chains(&self, table: &str) -> Result, Box> { let mut list = Vec::new(); let stdout = self.run(&["-t", table, "-S"])?.stdout; let output = String::from_utf8_lossy(stdout.as_slice()); for item in output.trim().split('\n') { let fields = item.split(' ').collect::>(); if fields.len() > 1 && (fields[0] == "-P" || fields[0] == "-N") { list.push(fields[1].to_string()); } } Ok(list) } /// Creates a new user-defined chain. pub fn new_chain(&self, table: &str, chain: &str) -> Result<(), Box> { self.run(&["-t", table, "-N", chain]) .and_then(output_to_result) } /// Flushes (deletes all rules) a chain. pub fn flush_chain(&self, table: &str, chain: &str) -> Result<(), Box> { self.run(&["-t", table, "-F", chain]) .and_then(output_to_result) } /// Renames a chain in the table. pub fn rename_chain( &self, table: &str, old_chain: &str, new_chain: &str, ) -> Result<(), Box> { self.run(&["-t", table, "-E", old_chain, new_chain]) .and_then(output_to_result) } /// Deletes a user-defined chain in the table. pub fn delete_chain(&self, table: &str, chain: &str) -> Result<(), Box> { self.run(&["-t", table, "-X", chain]) .and_then(output_to_result) } /// Flushes all chains in a table. pub fn flush_table(&self, table: &str) -> Result<(), Box> { self.run(&["-t", table, "-F"]).and_then(output_to_result) } fn get_list>(&self, args: &[S]) -> Result, Box> { let stdout = self.run(args)?.stdout; Ok(String::from_utf8_lossy(stdout.as_slice()) .trim() .split('\n') .map(String::from) .collect()) } /// Set whether iptables is called with the -n (--numeric) option, /// to avoid host name and port name lookups pub fn set_numeric(&mut self, numeric: bool) { self.is_numeric = numeric; } fn run>(&self, args: &[S]) -> Result> { let mut file_lock = None; let mut output_cmd = Command::new(self.cmd); let output; if self.has_wait { output = output_cmd.args(args).arg("--wait").output()?; } else { file_lock = Some(File::create("/var/run/xtables_old.lock")?); let mut need_retry = true; while need_retry { match flock( file_lock.as_ref().unwrap().as_raw_fd(), FlockArg::LockExclusiveNonblock, ) { Ok(_) => need_retry = false, Err(e) if e == nix::errno::Errno::EAGAIN => { // FIXME: may cause infinite loop need_retry = true; } Err(e) => { return Err(Box::new(e)); } } } output = output_cmd.args(args).output()?; } if let Some(f) = file_lock { drop(f) } Ok(output) } } iptables-0.5.1/tests/iptables_test.rs000064400000000000000000000141271046102023000160110ustar 00000000000000extern crate iptables; use std::panic; #[test] fn test_new() { nat(iptables::new(false).unwrap(), "NATNEW", "NATNEW2"); filter(iptables::new(false).unwrap(), "FILTERNEW"); } #[test] fn test_old() { nat( iptables::IPTables { cmd: "iptables", has_wait: false, has_check: false, is_numeric: false, }, "NATOLD", "NATOLD2", ); filter( iptables::IPTables { cmd: "iptables", has_wait: false, has_check: false, is_numeric: false, }, "FILTEROLD", ); } fn nat(ipt: iptables::IPTables, old_name: &str, new_name: &str) { assert!(ipt.new_chain("nat", old_name).is_ok()); assert!(ipt.rename_chain("nat", old_name, new_name).is_ok()); assert!(ipt.append("nat", new_name, "-j ACCEPT").is_ok()); assert!(ipt.exists("nat", new_name, "-j ACCEPT").unwrap()); assert!(ipt.delete("nat", new_name, "-j ACCEPT").is_ok()); assert!(ipt.insert("nat", new_name, "-j ACCEPT", 1).is_ok()); assert!(ipt .append( "nat", new_name, "-m comment --comment \"double-quoted comment\" -j ACCEPT" ) .is_ok(),); assert!(ipt .exists( "nat", new_name, "-m comment --comment \"double-quoted comment\" -j ACCEPT" ) .unwrap(),); assert!(ipt .append( "nat", new_name, "-m comment --comment 'single-quoted comment' -j ACCEPT" ) .is_ok(),); // The following `exists`-check has to use double-quotes, since the iptables output (if it // doesn't have the check-functionality) will use double quotes. assert!(ipt .exists( "nat", new_name, "-m comment --comment \"single-quoted comment\" -j ACCEPT" ) .unwrap(),); assert!(ipt.flush_chain("nat", new_name).is_ok()); assert!(!ipt.exists("nat", new_name, "-j ACCEPT").unwrap()); assert!(ipt .execute("nat", &format!("-A {} -j ACCEPT", new_name)) .is_ok()); assert!(ipt.exists("nat", new_name, "-j ACCEPT").unwrap()); assert!(ipt.flush_chain("nat", new_name).is_ok()); assert!(ipt.chain_exists("nat", new_name).unwrap()); assert!(ipt.delete_chain("nat", new_name).is_ok()); assert!(!ipt.chain_exists("nat", new_name).unwrap()); } fn filter(ipt: iptables::IPTables, name: &str) { assert!(ipt.new_chain("filter", name).is_ok()); assert!(ipt.insert("filter", name, "-j ACCEPT", 1).is_ok()); assert!(ipt.replace("filter", name, "-j DROP", 1).is_ok()); assert!(ipt.exists("filter", name, "-j DROP").unwrap()); assert!(!ipt.exists("filter", name, "-j ACCEPT").unwrap()); assert!(ipt.delete("filter", name, "-j DROP").is_ok()); assert_eq!(ipt.list("filter", name).unwrap().len(), 1); assert!(ipt .execute("filter", &format!("-A {} -j ACCEPT", name)) .is_ok()); assert!(ipt.exists("filter", name, "-j ACCEPT").unwrap()); assert!(ipt .append( "filter", name, "-m comment --comment \"double-quoted comment\" -j ACCEPT" ) .is_ok(),); assert!(ipt .exists( "filter", name, "-m comment --comment \"double-quoted comment\" -j ACCEPT" ) .unwrap()); assert!(ipt .append( "filter", name, "-m comment --comment 'single-quoted comment' -j ACCEPT" ) .is_ok(),); // The following `exists`-check has to use double-quotes, since the iptables output (if it // doesn't have the check-functionality) will use double quotes. assert!(ipt .exists( "filter", name, "-m comment --comment \"single-quoted comment\" -j ACCEPT" ) .unwrap(),); assert!(ipt.flush_chain("filter", name).is_ok()); assert!(ipt.chain_exists("filter", name).unwrap()); assert!(ipt.delete_chain("filter", name).is_ok()); assert!(!ipt.chain_exists("filter", name).unwrap()); } #[test] fn test_get_policy() { let ipt = iptables::new(false).unwrap(); // filter assert!(ipt.get_policy("filter", "INPUT").is_ok()); assert!(ipt.get_policy("filter", "FORWARD").is_ok()); assert!(ipt.get_policy("filter", "OUTPUT").is_ok()); // mangle assert!(ipt.get_policy("mangle", "PREROUTING").is_ok()); assert!(ipt.get_policy("mangle", "OUTPUT").is_ok()); assert!(ipt.get_policy("mangle", "INPUT").is_ok()); assert!(ipt.get_policy("mangle", "FORWARD").is_ok()); assert!(ipt.get_policy("mangle", "POSTROUTING").is_ok()); // nat assert!(ipt.get_policy("nat", "PREROUTING").is_ok()); assert!(ipt.get_policy("nat", "POSTROUTING").is_ok()); assert!(ipt.get_policy("nat", "OUTPUT").is_ok()); // raw assert!(ipt.get_policy("raw", "PREROUTING").is_ok()); assert!(ipt.get_policy("raw", "OUTPUT").is_ok()); // security assert!(ipt.get_policy("security", "INPUT").is_ok()); assert!(ipt.get_policy("security", "OUTPUT").is_ok()); assert!(ipt.get_policy("security", "FORWARD").is_ok()); // Wrong table assert!(ipt.get_policy("not_existant", "_").is_err()); // Wrong chain assert!(ipt.get_policy("filter", "_").is_err()); } #[test] #[ignore] fn test_set_policy() { let ipt = iptables::new(false).unwrap(); // Since we can only set policies on built-in chains, we have to retain the policy of the chain // before setting it, to restore it to its original state. let current_policy = ipt.get_policy("mangle", "FORWARD").unwrap(); // If the following assertions fail or any other panic occurs, we still have to ensure not to // change the policy for the user. let result = panic::catch_unwind(|| { assert!(ipt.set_policy("mangle", "FORWARD", "DROP").is_ok()); assert_eq!(ipt.get_policy("mangle", "FORWARD").unwrap(), "DROP"); }); // Reset the policy to the retained value ipt.set_policy("mangle", "FORWARD", ¤t_policy) .unwrap(); // "Rethrow" a potential caught panic assert!(result.is_ok()); }