aboutsummaryrefslogtreecommitdiff
path: root/rustables/examples/filter-ethernet.rs
diff options
context:
space:
mode:
authorlafleur <lafleur@boum.org>2021-10-18 23:43:35 +0200
committerlafleur <lafleur@boum.org>2021-10-19 00:39:55 +0200
commit4a87680c89017730f0a8715f87118c63bb8b7ae8 (patch)
tree9ea0780ef6fb8f3320dedb3c3dc65aebc58ee7a0 /rustables/examples/filter-ethernet.rs
parent26bfea8ed713ab68f0ffe3945e94fee1d766c98e (diff)
rename the crates, update copyright notices
Diffstat (limited to 'rustables/examples/filter-ethernet.rs')
-rw-r--r--rustables/examples/filter-ethernet.rs128
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())
+ }
+}