//! Find a rule in a given chain, then atomically replace it its last expression with a 'log'
//! expression.
//!
//! To use this example, create rules with the example `add-rules`, then run this binary.
use rustables::{
expr::{ExpressionVariant, Log, RawExpression, Register, Verdict, VerdictType},
list_chains_for_table, list_rules_for_chain, list_tables, Batch, Chain, MsgType, Rule, Table,
};
const TABLE_NAME: &str = "example-table";
const CHAIN_NAME: &str = "chain-for-incoming-packets";
fn main() -> Result<(), Error> {
env_logger::init();
// Find the tables currently inserted on the system
let get_table = || -> Result, Error> {
let tables = list_tables()?;
for table in tables {
if let Some(name) = table.get_name() {
println!("Found table {}", name);
if name == TABLE_NAME {
return Ok(Some(table));
}
}
}
Ok(None)
};
let get_chain = |table: &Table| -> Result , Error> {
let chains = list_chains_for_table(table)?;
for chain in chains {
if let Some(name) = chain.get_name() {
println!("Found chain {}", name);
if name == CHAIN_NAME {
return Ok(Some(chain));
}
}
}
Ok(None)
};
let get_rule = |chain: &Chain| -> Result , Error> {
let rules = list_rules_for_chain(&chain)?;
for mut rule in rules {
let old_handle = *rule.get_handle().expect("no handle on an existing rule!?");
println!("Found rule {}", old_handle);
if let Some(exprs) = rule.get_mut_expressions() {
let mut found = false;
// match the rule that contains an Accept verdict
for expr in exprs.iter_mut() {
if let Some(ExpressionVariant::Immediate(imm)) = expr.get_data() {
if imm.get_dreg() == Some(&Register::Verdict)
&& imm.get_data().map(|d| d.get_verdict()).flatten()
== Some(&Verdict::default().with_code(VerdictType::Accept))
{
*expr = RawExpression::from(Log::default());
found = true;
}
}
}
if found {
let rule = Rule::new(&chain)?.with_expressions(exprs.clone());
return Ok(Some((old_handle, rule)));
}
}
}
Ok(None)
};
let table = get_table()?.expect("no table?");
let chain = get_chain(&table)?.expect("no chain?");
let (old_rule_handle, new_rule) = get_rule(&chain)?.expect("no rule?");
println!("Editing rule with handle {}", old_rule_handle);
// Create a batch. This is used to store all the netlink messages we will later send.
// Creating a new batch also automatically writes the initial batch begin message needed
// to tell netlink this is a single transaction that might arrive over multiple netlink packets.
let mut batch = Batch::new();
batch.add(
&Rule::new(&chain)?.with_handle(old_rule_handle),
MsgType::Del,
);
batch.add(&new_rule, MsgType::Add);
// Finalize the batch and send it. This means the batch end message is written into the batch, telling
// netfilter the we reached the end of the transaction message. It's also converted to a
// Vec, containing the raw netlink data so it can be sent over a netlink socket to netfilter.
// Finally, the batch is sent over to the kernel.
Ok(batch.send()?)
}
#[allow(dead_code)]
#[derive(Debug)]
struct Error(String);
impl From for Error {
fn from(error: T) -> Self {
Error(error.to_string())
}
}