1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
|
// Copyryght (c) 2021 GPL lafleur@boum.org and Simon Thoby
//
// This file is free software: you may copy, redistribute and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This file is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see the LICENSE file.
//
// This file incorporates work covered by the following copyright and
// permission notice:
//
// Copyright 2018 Amagicom AB.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Safe abstraction for [`libnftnl`]. Provides low-level userspace access to the in-kernel
//! nf_tables subsystem. See [`rustables-sys`] for the low level FFI bindings to the C library.
//!
//! Can be used to create and remove tables, chains, sets and rules from the nftables firewall,
//! the successor to iptables.
//!
//! This library currently has quite rough edges and does not make adding and removing netfilter
//! entries super easy and elegant. That is partly because the library needs more work, but also
//! partly because nftables is super low level and extremely customizable, making it hard, and
//! probably wrong, to try and create a too simple/limited wrapper. See examples for inspiration.
//! One can also look at how the original project this crate was developed to support uses it:
//! [Mullvad VPN app](https://github.com/mullvad/mullvadvpn-app)
//!
//! Understanding how to use [`libnftnl`] and implementing this crate has mostly been done by
//! reading the source code for the [`nftables`] program and attaching debuggers to the `nft`
//! binary. Since the implementation is mostly based on trial and error, there might of course be
//! a number of places where the underlying library is used in an invalid or not intended way.
//! Large portions of [`libnftnl`] are also not covered yet. Contributions are welcome!
//!
//! # Selecting version of `libnftnl`
//!
//! See the documentation for the corresponding sys crate for details: [`rustables-sys`].
//! This crate has the same features as the sys crate, and selecting version works the same.
//!
//! # Access to raw handles
//!
//! Retrieving raw handles is considered unsafe and should only ever be enabled if you absoluetely
//! need it. It is disabled by default and hidden behind the feature gate `unsafe-raw-handles`.
//! The reason for that special treatment is we cannot guarantee the lack of aliasing. For example,
//! a program using a const handle to a object in a thread and writing through a mutable handle
//! in another could reach all kind of undefined (and dangerous!) behaviors.
//! By enabling that feature flag, you acknowledge that guaranteeing the respect of safety
//! invariants is now your responsibility!
//! Despite these shortcomings, that feature is still available because it may allow you to perform
//! manipulations that this library doesn't currently expose. If that is your case, we would
//! be very happy to hear from you and maybe help you get the necessary functionality upstream.
//!
//! Our current lack of confidence in our availability to provide a safe abstraction over the
//! use of raw handles in the face of concurrency is the reason we decided to settly on `Rc`
//! pointers instead of `Arc` (besides, this should gives us some nice performance boost, not
//! that it matters much of course) and why we do not declare the types exposes by the library
//! as `Send` nor `Sync`.
//!
//! [`libnftnl`]: https://netfilter.org/projects/libnftnl/
//! [`nftables`]: https://netfilter.org/projects/nftables/
//! [`rustables-sys`]: https://crates.io/crates/rustables-sys
use thiserror::Error;
#[macro_use]
extern crate log;
pub mod sys;
use sys::libc;
use std::{convert::TryFrom, ffi::c_void, ops::Deref};
macro_rules! try_alloc {
($e:expr) => {{
let ptr = $e;
if ptr.is_null() {
// OOM, and the tried allocation was likely very small,
// so we are in a very tight situation. We do what libstd does, aborts.
std::process::abort();
}
ptr
}};
}
mod batch;
#[cfg(feature = "query")]
pub use batch::{batch_is_supported, default_batch_page_size};
pub use batch::{Batch, FinalizedBatch, NetlinkError};
pub mod expr;
pub mod table;
pub use table::Table;
#[cfg(feature = "query")]
pub use table::{get_tables_cb, list_tables};
mod chain;
#[cfg(feature = "query")]
pub use chain::{get_chains_cb, list_chains_for_table};
pub use chain::{Chain, ChainType, Hook, Policy, Priority};
pub mod query;
mod rule;
pub use rule::Rule;
#[cfg(feature = "query")]
pub use rule::{get_rules_cb, list_rules_for_chain};
mod rule_match;
pub use rule_match::{Match, Protocol, Error as MatchError};
pub mod set;
/// The type of the message as it's sent to netfilter. A message consists of an object, such as a
/// [`Table`], [`Chain`] or [`Rule`] for example, and a [`MsgType`] to describe what to do with
/// that object. If a [`Table`] object is sent with `MsgType::Add` then that table will be added
/// to netfilter, if sent with `MsgType::Del` it will be removed.
///
/// [`Table`]: struct.Table.html
/// [`Chain`]: struct.Chain.html
/// [`Rule`]: struct.Rule.html
/// [`MsgType`]: enum.MsgType.html
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum MsgType {
/// Add the object to netfilter.
Add,
/// Remove the object from netfilter.
Del,
}
/// Denotes a protocol. Used to specify which protocol a table or set belongs to.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(u16)]
pub enum ProtoFamily {
Unspec = libc::NFPROTO_UNSPEC as u16,
/// Inet - Means both IPv4 and IPv6
Inet = libc::NFPROTO_INET as u16,
Ipv4 = libc::NFPROTO_IPV4 as u16,
Arp = libc::NFPROTO_ARP as u16,
NetDev = libc::NFPROTO_NETDEV as u16,
Bridge = libc::NFPROTO_BRIDGE as u16,
Ipv6 = libc::NFPROTO_IPV6 as u16,
DecNet = libc::NFPROTO_DECNET as u16,
}
#[derive(Error, Debug)]
#[error("Couldn't find a matching protocol")]
pub struct InvalidProtocolFamily;
impl TryFrom<i32> for ProtoFamily {
type Error = InvalidProtocolFamily;
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value {
libc::NFPROTO_UNSPEC => Ok(ProtoFamily::Unspec),
libc::NFPROTO_INET => Ok(ProtoFamily::Inet),
libc::NFPROTO_IPV4 => Ok(ProtoFamily::Ipv4),
libc::NFPROTO_ARP => Ok(ProtoFamily::Arp),
libc::NFPROTO_NETDEV => Ok(ProtoFamily::NetDev),
libc::NFPROTO_BRIDGE => Ok(ProtoFamily::Bridge),
libc::NFPROTO_IPV6 => Ok(ProtoFamily::Ipv6),
libc::NFPROTO_DECNET => Ok(ProtoFamily::DecNet),
_ => Err(InvalidProtocolFamily),
}
}
}
/// Trait for all types in this crate that can serialize to a Netlink message.
///
/// # Unsafe
///
/// This trait is unsafe to implement because it must never serialize to anything larger than the
/// largest possible netlink message. Internally the `nft_nlmsg_maxsize()` function is used to make
/// sure the `buf` pointer passed to `write` always has room for the largest possible Netlink
/// message.
pub unsafe trait NlMsg {
/// Serializes the Netlink message to the buffer at `buf`. `buf` must have space for at least
/// `nft_nlmsg_maxsize()` bytes. This is not checked by the compiler, which is why this method
/// is unsafe.
unsafe fn write(&self, buf: *mut c_void, seq: u32, msg_type: MsgType);
}
unsafe impl<T, R> NlMsg for T
where
T: Deref<Target = R>,
R: NlMsg,
{
unsafe fn write(&self, buf: *mut c_void, seq: u32, msg_type: MsgType) {
self.deref().write(buf, seq, msg_type);
}
}
/// The largest nf_tables netlink message is the set element message, which
/// contains the NFTA_SET_ELEM_LIST_ELEMENTS attribute. This attribute is
/// a nest that describes the set elements. Given that the netlink attribute
/// length (nla_len) is 16 bits, the largest message is a bit larger than
/// 64 KBytes.
pub fn nft_nlmsg_maxsize() -> u32 {
u32::from(::std::u16::MAX) + unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as u32
}
|