// 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 or the MIT license // , 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 for ProtoFamily { type Error = InvalidProtocolFamily; fn try_from(value: i32) -> Result { 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 NlMsg for T where T: Deref, 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 }