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}