Skip to main content

nftnl/
lib.rs

1// Copyright 2025 Mullvad VPN AB.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Safe abstraction for [`libnftnl`]. Provides low-level userspace access to the in-kernel
10//! nf_tables subsystem. See [`nftnl-sys`] for the low level FFI bindings to the C library.
11//!
12//! Can be used to create and remove tables, chains, sets and rules from the nftables firewall,
13//! the successor to iptables.
14//!
15//! This library currently has quite rough edges and does not make adding and removing netfilter
16//! entries super easy and elegant. That is partly because the library needs more work, but also
17//! partly because nftables is super low level and extremely customizable, making it hard, and
18//! probably wrong, to try and create a too simple/limited wrapper. See examples for inspiration.
19//! One can also look at how the original project this crate was developed to support uses it:
20//! [Mullvad VPN app](https://github.com/mullvad/mullvadvpn-app)
21//!
22//! Understanding how to use [`libnftnl`] and implementing this crate has mostly been done by
23//! reading the source code for the [`nftables`] program and attaching debuggers to the `nft`
24//! binary. Since the implementation is mostly based on trial and error, there might of course be
25//! a number of places where the underlying library is used in an invalid or not intended way.
26//! Large portions of [`libnftnl`] are also not covered yet. Contributions are welcome!
27//!
28//! # Selecting version of `libnftnl`
29//!
30//! See the documentation for the corresponding sys crate for details: [`nftnl-sys`]
31//! This crate has the same features as the sys crate, and selecting version works the same.
32//!
33//! [`libnftnl`]: https://netfilter.org/projects/libnftnl/
34//! [`nftables`]: https://netfilter.org/projects/nftables/
35//! [`nftnl-sys`]: https://crates.io/crates/nftnl-sys
36
37#[macro_use]
38extern crate log;
39
40pub use nftnl_sys;
41use nftnl_sys::libc;
42use std::ffi::c_void;
43
44macro_rules! try_alloc {
45    ($e:expr) => {{
46        let ptr: *mut _ = $e;
47        let Some(ptr) = ::std::ptr::NonNull::new(ptr) else {
48            // OOM, and the tried allocation was likely very small,
49            // so we are in a very tight situation. We do what libstd does, aborts.
50            ::std::process::abort();
51        };
52        ptr
53    }};
54}
55
56mod util;
57
58mod batch;
59pub use batch::{Batch, FinalizedBatch, NetlinkError, batch_is_supported, default_batch_page_size};
60
61pub mod expr;
62
63pub mod table;
64pub use table::Table;
65
66mod chain;
67pub use chain::{Chain, ChainType, Hook, Policy, Priority};
68
69mod rule;
70pub use rule::Rule;
71
72pub mod set;
73
74/// The type of the message as it's sent to netfilter. A message consists of an object, such as a
75/// [`Table`], [`Chain`] or [`Rule`] for example, and a [`MsgType`] to describe what to do with
76/// that object. If a [`Table`] object is sent with `MsgType::Add` then that table will be added
77/// to netfilter, if sent with `MsgType::Del` it will be removed.
78///
79/// [`Table`]: struct.Table.html
80/// [`Chain`]: struct.Chain.html
81/// [`Rule`]: struct.Rule.html
82/// [`MsgType`]: enum.MsgType.html
83#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
84pub enum MsgType {
85    /// Add the object to netfilter.
86    Add,
87    /// Remove the object from netfilter.
88    Del,
89}
90
91/// Denotes a protocol. Used to specify which protocol a table or set belongs to.
92#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
93#[repr(u16)]
94pub enum ProtoFamily {
95    Unspec = libc::NFPROTO_UNSPEC as u16,
96    /// Inet - Means both IPv4 and IPv6
97    Inet = libc::NFPROTO_INET as u16,
98    Ipv4 = libc::NFPROTO_IPV4 as u16,
99    Arp = libc::NFPROTO_ARP as u16,
100    NetDev = libc::NFPROTO_NETDEV as u16,
101    Bridge = libc::NFPROTO_BRIDGE as u16,
102    Ipv6 = libc::NFPROTO_IPV6 as u16,
103    DecNet = libc::NFPROTO_DECNET as u16,
104}
105
106/// Trait for all types in this crate that can serialize to a Netlink message.
107///
108/// # Safety
109///
110/// This trait is unsafe to implement because it must never serialize to anything larger than the
111/// largest possible netlink message. Internally the [`nft_nlmsg_maxsize`] function is used to make
112/// sure the `buf` pointer passed to `write` always has room for the largest possible Netlink
113/// message.
114pub unsafe trait NlMsg {
115    /// Serializes the Netlink message to the buffer at `buf`. `buf` must have space for at least
116    /// `nft_nlmsg_maxsize()` bytes. This is not checked by the compiler, which is why this method
117    /// is unsafe.
118    ///
119    /// # Safety
120    ///
121    /// The caller must pass a `buf` with enough space for the largest possible netlink message.
122    /// This size can be obtained with [`nft_nlmsg_maxsize`].
123    unsafe fn write(&self, buf: *mut c_void, seq: u32, msg_type: MsgType);
124}
125
126/// The largest nf_tables netlink message is the set element message, which
127/// contains the NFTA_SET_ELEM_LIST_ELEMENTS attribute. This attribute is
128/// a nest that describes the set elements. Given that the netlink attribute
129/// length (nla_len) is 16 bits, the largest message is a bit larger than
130/// 64 KBytes.
131pub fn nft_nlmsg_maxsize() -> u32 {
132    u32::from(u16::MAX) + unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as u32
133}