rustables/
chain.rs

1use libc::{NF_ACCEPT, NF_DROP};
2use rustables_macros::nfnetlink_struct;
3
4use crate::error::{DecodeError, QueryError};
5use crate::nlmsg::{NfNetlinkAttribute, NfNetlinkDeserializable, NfNetlinkObject};
6use crate::sys::{
7    NFTA_CHAIN_FLAGS, NFTA_CHAIN_HOOK, NFTA_CHAIN_NAME, NFTA_CHAIN_POLICY, NFTA_CHAIN_TABLE,
8    NFTA_CHAIN_TYPE, NFTA_HOOK_HOOKNUM, NFTA_HOOK_PRIORITY, NFT_MSG_DELCHAIN, NFT_MSG_NEWCHAIN,
9};
10use crate::{Batch, ProtocolFamily, Table};
11use std::fmt::Debug;
12
13pub type ChainPriority = i32;
14
15/// The netfilter event hooks a chain can register for.
16#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
17#[repr(i32)]
18pub enum HookClass {
19    /// Hook into the pre-routing stage of netfilter. Corresponds to `NF_INET_PRE_ROUTING`.
20    PreRouting = libc::NF_INET_PRE_ROUTING,
21    /// Hook into the input stage of netfilter. Corresponds to `NF_INET_LOCAL_IN`.
22    In = libc::NF_INET_LOCAL_IN,
23    /// Hook into the forward stage of netfilter. Corresponds to `NF_INET_FORWARD`.
24    Forward = libc::NF_INET_FORWARD,
25    /// Hook into the output stage of netfilter. Corresponds to `NF_INET_LOCAL_OUT`.
26    Out = libc::NF_INET_LOCAL_OUT,
27    /// Hook into the post-routing stage of netfilter. Corresponds to `NF_INET_POST_ROUTING`.
28    PostRouting = libc::NF_INET_POST_ROUTING,
29}
30
31#[derive(Clone, PartialEq, Eq, Default, Debug)]
32#[nfnetlink_struct(nested = true)]
33pub struct Hook {
34    /// Define the action netfilter will apply to packets processed by this chain, but that did not match any rules in it.
35    #[field(NFTA_HOOK_HOOKNUM)]
36    class: u32,
37    #[field(NFTA_HOOK_PRIORITY)]
38    priority: u32,
39}
40
41impl Hook {
42    pub fn new(class: HookClass, priority: ChainPriority) -> Self {
43        Hook::default()
44            .with_class(class as u32)
45            .with_priority(priority as u32)
46    }
47}
48
49/// A chain policy. Decides what to do with a packet that was processed by the chain but did not
50/// match any rules.
51#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
52#[repr(i32)]
53pub enum ChainPolicy {
54    /// Accept the packet.
55    Accept = NF_ACCEPT,
56    /// Drop the packet.
57    Drop = NF_DROP,
58}
59
60impl NfNetlinkAttribute for ChainPolicy {
61    fn get_size(&self) -> usize {
62        (*self as i32).get_size()
63    }
64
65    fn write_payload(&self, addr: &mut [u8]) {
66        (*self as i32).write_payload(addr);
67    }
68}
69
70impl NfNetlinkDeserializable for ChainPolicy {
71    fn deserialize(buf: &[u8]) -> Result<(Self, &[u8]), DecodeError> {
72        let (v, remaining_data) = i32::deserialize(buf)?;
73        Ok((
74            match v {
75                NF_ACCEPT => ChainPolicy::Accept,
76                NF_DROP => ChainPolicy::Accept,
77                _ => return Err(DecodeError::UnknownChainPolicy),
78            },
79            remaining_data,
80        ))
81    }
82}
83
84/// Base chain type.
85#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
86pub enum ChainType {
87    /// Used to filter packets.
88    /// Supported protocols: ip, ip6, inet, arp, and bridge tables.
89    Filter,
90    /// Used to reroute packets if IP headers or packet marks are modified.
91    /// Supported protocols: ip, and ip6 tables.
92    Route,
93    /// Used to perform NAT.
94    /// Supported protocols: ip, and ip6 tables.
95    Nat,
96}
97
98impl ChainType {
99    fn as_str(&self) -> &'static str {
100        match *self {
101            ChainType::Filter => "filter",
102            ChainType::Route => "route",
103            ChainType::Nat => "nat",
104        }
105    }
106}
107
108impl NfNetlinkAttribute for ChainType {
109    fn get_size(&self) -> usize {
110        self.as_str().len()
111    }
112
113    fn write_payload(&self, addr: &mut [u8]) {
114        self.as_str().to_string().write_payload(addr);
115    }
116}
117
118impl NfNetlinkDeserializable for ChainType {
119    fn deserialize(buf: &[u8]) -> Result<(Self, &[u8]), DecodeError> {
120        let (s, remaining_data) = String::deserialize(buf)?;
121        Ok((
122            match s.as_str() {
123                "filter" => ChainType::Filter,
124                "route" => ChainType::Route,
125                "nat" => ChainType::Nat,
126                _ => return Err(DecodeError::UnknownChainType),
127            },
128            remaining_data,
129        ))
130    }
131}
132
133/// Abstraction over an nftable chain. Chains reside inside [`Table`]s and they hold [`Rule`]s.
134///
135/// [`Table`]: struct.Table.html
136/// [`Rule`]: struct.Rule.html
137#[nfnetlink_struct(derive_deserialize = false)]
138#[derive(PartialEq, Eq, Default, Debug)]
139pub struct Chain {
140    family: ProtocolFamily,
141    #[field(NFTA_CHAIN_TABLE)]
142    table: String,
143    #[field(NFTA_CHAIN_NAME)]
144    name: String,
145    #[field(NFTA_CHAIN_HOOK)]
146    hook: Hook,
147    #[field(NFTA_CHAIN_POLICY)]
148    policy: ChainPolicy,
149    #[field(NFTA_CHAIN_TYPE, name_in_functions = "type")]
150    chain_type: ChainType,
151    #[field(NFTA_CHAIN_FLAGS)]
152    flags: u32,
153    #[field(optional = true, crate::sys::NFTA_CHAIN_USERDATA)]
154    userdata: Vec<u8>,
155}
156
157impl Chain {
158    /// Creates a new chain instance inside the given [`Table`].
159    ///
160    /// [`Table`]: struct.Table.html
161    pub fn new(table: &Table) -> Chain {
162        let mut chain = Chain::default();
163        chain.family = table.get_family();
164
165        if let Some(table_name) = table.get_name() {
166            chain.set_table(table_name);
167        }
168
169        chain
170    }
171
172    /// Appends this chain to `batch`
173    pub fn add_to_batch(self, batch: &mut Batch) -> Self {
174        batch.add(&self, crate::MsgType::Add);
175        self
176    }
177}
178
179impl NfNetlinkObject for Chain {
180    const MSG_TYPE_ADD: u32 = NFT_MSG_NEWCHAIN;
181    const MSG_TYPE_DEL: u32 = NFT_MSG_DELCHAIN;
182
183    fn get_family(&self) -> ProtocolFamily {
184        self.family
185    }
186
187    fn set_family(&mut self, family: ProtocolFamily) {
188        self.family = family;
189    }
190}
191
192pub fn list_chains_for_table(table: &Table) -> Result<Vec<Chain>, QueryError> {
193    let mut result = Vec::new();
194    crate::query::list_objects_with_data(
195        libc::NFT_MSG_GETCHAIN as u16,
196        &|chain: Chain, (table, chains): &mut (&Table, &mut Vec<Chain>)| {
197            if chain.get_table() == table.get_name() {
198                chains.push(chain);
199            } else {
200                info!(
201                    "Ignoring chain {:?} because it doesn't map the table {:?}",
202                    chain.get_name(),
203                    table.get_name()
204                );
205            }
206            Ok(())
207        },
208        None,
209        &mut (&table, &mut result),
210    )?;
211    Ok(result)
212}