diff options
author | lafleur <lafleur@boum.org> | 2021-10-18 23:43:35 +0200 |
---|---|---|
committer | lafleur <lafleur@boum.org> | 2021-10-19 00:39:55 +0200 |
commit | 4a87680c89017730f0a8715f87118c63bb8b7ae8 (patch) | |
tree | 9ea0780ef6fb8f3320dedb3c3dc65aebc58ee7a0 /rustables/examples/filter-ethernet.rs | |
parent | 26bfea8ed713ab68f0ffe3945e94fee1d766c98e (diff) |
rename the crates, update copyright notices
Diffstat (limited to 'rustables/examples/filter-ethernet.rs')
-rw-r--r-- | rustables/examples/filter-ethernet.rs | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/rustables/examples/filter-ethernet.rs b/rustables/examples/filter-ethernet.rs new file mode 100644 index 0000000..6cebfc4 --- /dev/null +++ b/rustables/examples/filter-ethernet.rs @@ -0,0 +1,128 @@ +//! Adds a table, chain and a rule that blocks all traffic to a given MAC address +//! +//! Run the following to print out current active tables, chains and rules in netfilter. Must be +//! executed as root: +//! ```bash +//! # nft list ruleset +//! ``` +//! After running this example, the output should be the following: +//! ```ignore +//! table inet example-filter-ethernet { +//! chain chain-for-outgoing-packets { +//! type filter hook output priority 3; policy accept; +//! ether daddr 00:00:00:00:00:00 drop +//! counter packets 0 bytes 0 meta random > 2147483647 counter packets 0 bytes 0 +//! } +//! } +//! ``` +//! +//! +//! Everything created by this example can be removed by running +//! ```bash +//! # nft delete table inet example-filter-ethernet +//! ``` + +use nftnl::{nft_expr, nftnl_sys::libc, Batch, Chain, FinalizedBatch, ProtoFamily, Rule, Table}; +use std::{ffi::CString, io}; + +const TABLE_NAME: &str = "example-filter-ethernet"; +const OUT_CHAIN_NAME: &str = "chain-for-outgoing-packets"; + +const BLOCK_THIS_MAC: &[u8] = &[0, 0, 0, 0, 0, 0]; + +fn main() -> Result<(), Error> { + // For verbose explanations of what all these lines up until the rule creation does, see the + // `add-rules` example. + let mut batch = Batch::new(); + let table = Table::new(&CString::new(TABLE_NAME).unwrap(), ProtoFamily::Inet); + batch.add(&table, nftnl::MsgType::Add); + + let mut out_chain = Chain::new(&CString::new(OUT_CHAIN_NAME).unwrap(), &table); + out_chain.set_hook(nftnl::Hook::Out, 3); + out_chain.set_policy(nftnl::Policy::Accept); + batch.add(&out_chain, nftnl::MsgType::Add); + + // === ADD RULE DROPPING ALL TRAFFIC TO THE MAC ADDRESS IN `BLOCK_THIS_MAC` === + + let mut block_ethernet_rule = Rule::new(&out_chain); + + // Check that the interface type is an ethernet interface. Must be done before we can check + // payload values in the ethernet header. + block_ethernet_rule.add_expr(&nft_expr!(meta iiftype)); + block_ethernet_rule.add_expr(&nft_expr!(cmp == libc::ARPHRD_ETHER)); + + // Compare the ethernet destination address against the MAC address we want to drop + block_ethernet_rule.add_expr(&nft_expr!(payload ethernet daddr)); + block_ethernet_rule.add_expr(&nft_expr!(cmp == BLOCK_THIS_MAC)); + + // Drop the matching packets. + block_ethernet_rule.add_expr(&nft_expr!(verdict drop)); + + batch.add(&block_ethernet_rule, nftnl::MsgType::Add); + + // === FOR FUN, ADD A PACKET THAT MATCHES 50% OF ALL PACKETS === + + // This packet has a counter before and after the check that has 50% chance of matching. + // So after a number of packets has passed through this rule, the first counter should have a + // value approximately double that of the second counter. This rule has no verdict, so it never + // does anything with the matching packets. + let mut random_rule = Rule::new(&out_chain); + // This counter expression will be evaluated (and increment the counter) for all packets coming + // through. + random_rule.add_expr(&nft_expr!(counter)); + + // Load a pseudo-random 32 bit unsigned integer into the netfilter register. + random_rule.add_expr(&nft_expr!(meta random)); + // Check if the random integer is larger than `u32::MAX/2`, thus having 50% chance of success. + random_rule.add_expr(&nft_expr!(cmp > (::std::u32::MAX / 2).to_be())); + + // Add a second counter. This will only be incremented for the packets passing the random check. + random_rule.add_expr(&nft_expr!(counter)); + + batch.add(&random_rule, nftnl::MsgType::Add); + + // === FINALIZE THE TRANSACTION AND SEND THE DATA TO NETFILTER === + + let finalized_batch = batch.finalize(); + send_and_process(&finalized_batch)?; + Ok(()) +} + +fn send_and_process(batch: &FinalizedBatch) -> Result<(), Error> { + // Create a netlink socket to netfilter. + let socket = mnl::Socket::new(mnl::Bus::Netfilter)?; + // Send all the bytes in the batch. + socket.send_all(batch)?; + + // Try to parse the messages coming back from netfilter. This part is still very unclear. + let portid = socket.portid(); + let mut buffer = vec![0; nftnl::nft_nlmsg_maxsize() as usize]; + let very_unclear_what_this_is_for = 2; + while let Some(message) = socket_recv(&socket, &mut buffer[..])? { + match mnl::cb_run(message, very_unclear_what_this_is_for, portid)? { + mnl::CbResult::Stop => { + break; + } + mnl::CbResult::Ok => (), + } + } + Ok(()) +} + +fn socket_recv<'a>(socket: &mnl::Socket, buf: &'a mut [u8]) -> Result<Option<&'a [u8]>, Error> { + let ret = socket.recv(buf)?; + if ret > 0 { + Ok(Some(&buf[..ret])) + } else { + Ok(None) + } +} + +#[derive(Debug)] +struct Error(String); + +impl From<io::Error> for Error { + fn from(error: io::Error) -> Self { + Error(error.to_string()) + } +} |