Skip to main content

dfw/nftables/
mod.rs

1// Copyright Pit Kleyersburg <pitkley@googlemail.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified or distributed
8// except according to those terms.
9
10//! This module implements the nftables backend for DFW.
11
12use crate::{errors::*, FirewallBackend, ProcessContext};
13use slog::{debug, info, o, trace};
14use std::{
15    io::{prelude::*, BufWriter},
16    process::Command,
17};
18use strum::Display;
19
20mod process;
21mod rule;
22pub mod types;
23
24const NF_IP_PRI_NAT_DST: i16 = -100;
25const NF_IP_PRI_FILTER: i16 = 0;
26const NF_IP_PRI_NAT_SRC: i16 = 100;
27
28const NF_PRIORITY_IP_NAT_PREROUTING_DFW: i16 = NF_IP_PRI_NAT_DST - 5;
29const NF_PRIORITY_IP6_NAT_PREROUTING_DFW: i16 = NF_IP_PRI_NAT_DST - 5;
30const NF_PRIORITY_INET_FILTER_ANY_DFW: i16 = NF_IP_PRI_FILTER - 5;
31const NF_PRIORITY_IP_NAT_POSTROUTING_DFW: i16 = NF_IP_PRI_NAT_SRC - 5;
32const NF_PRIORITY_IP6_NAT_POSTROUTING_DFW: i16 = NF_IP_PRI_NAT_SRC - 5;
33
34const DFW_MARK: &str = "0xdf";
35
36/// Marker struct to implement nftables as a firewall backend.
37#[derive(Debug)]
38pub struct Nftables;
39impl FirewallBackend for Nftables {
40    type Rule = String;
41    type Defaults = types::Defaults;
42
43    fn apply(rules: Vec<Self::Rule>, ctx: &ProcessContext<Nftables>) -> Result<()> {
44        if ctx.dry_run {
45            info!(ctx.logger, "Performing dry-run, will not update any rules");
46        } else {
47            // To atomically update the ruleset, we need to write a file and pass that to `nft -f`.
48            let rule_file = tempfile::Builder::new().tempfile()?;
49            let rule_file_path = rule_file.as_ref().as_os_str().to_os_string();
50            debug!(ctx.logger, "Writing rules to temporary file";
51                   o!("file_path" => rule_file_path.to_string_lossy().into_owned()));
52            let mut writer = BufWriter::new(rule_file);
53
54            for rule in rules {
55                writeln!(writer, "{}", rule)?;
56            }
57            writer.flush()?;
58            trace!(ctx.logger, "Finished writing rules to temporary file");
59
60            info!(ctx.logger, "Applying rules (using nft)");
61            let output = Command::new("nft").arg("-f").arg(rule_file_path).output()?;
62            if !output.status.success() {
63                return Err(DFWError::NFTablesError {
64                    stdout: String::from_utf8_lossy(&output.stdout).into_owned(),
65                    stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
66                }
67                .into());
68            } else {
69                return Ok(());
70            }
71        }
72
73        Ok(())
74    }
75}
76
77/// Representation of nftables table-families.
78#[derive(Debug, Clone, Copy, Display)]
79#[strum(serialize_all = "snake_case")]
80pub enum Family {
81    /// IPv4 table family
82    Ip,
83    /// IPv6 table family
84    Ip6,
85    /// Dualstack IPv4/IPv6 table family
86    Inet,
87    /// ARP table family
88    Arp,
89    /// Bridge table family
90    Bridge,
91    /// Netdev table family
92    Netdev,
93}
94
95/// Representation of nftables chain-types.
96///
97/// ## Attribution
98///
99/// Parts of the documentation have been taken from
100/// <https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains>.
101#[derive(Debug, Clone, Copy, Display)]
102#[strum(serialize_all = "snake_case")]
103pub enum Type {
104    /// Is used to filter packets.
105    ///
106    /// Supported by the following table-families:
107    ///
108    /// * Arp
109    /// * Bridge
110    /// * Ip
111    /// * Ip6
112    /// * Inet
113    Filter,
114    /// Is used to reroute packets if any relevant IP header field or the packet mark is modified.
115    ///
116    /// Supported by the following table-families:
117    ///
118    /// * Ip
119    /// * Ip6
120    Route,
121    /// Is used to perform Networking Address Translation (NAT).
122    ///
123    /// Supported by the following table-families:
124    ///
125    /// * Ip
126    /// * Ip6
127    Nat,
128}
129
130/// Representation of nftables chain hooks.
131///
132/// Order of hook execution:
133///
134/// * Ingress _(for netdev-family)_
135/// * Prerouting
136/// * Input
137/// * Forward
138/// * Output
139/// * Postrouting
140///
141/// ## Attribution
142///
143/// Parts of the documentation have been taken from
144/// <https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains>.
145#[derive(Debug, Clone, Copy, Display)]
146#[strum(serialize_all = "snake_case")]
147pub enum Hook {
148    /// Ingress allows traffic-filtering before pre-routing, after the packet traversed the NIC.
149    ///
150    /// Only available for the netdev-family.
151    Ingress,
152    /// Prerouting allows traffic-filtering before the packets have been routed.
153    Prerouting,
154    /// Input allows traffic-filtering for packets that have been routed to the local system.
155    Input,
156    /// Forward allows traffic-filtering for packets that were not routed to the local system.
157    Forward,
158    /// Output allows traffic-filtering for packets leaving the local system.
159    Output,
160    /// Postrouting allows traffic-filtering for already routed packets leaving the local system.
161    Postrouting,
162}