diff options
Diffstat (limited to 'src/lib.rs')
-rw-r--r-- | src/lib.rs | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..6eedf9f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,205 @@ +// 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}; + +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 +} |