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#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
17#[repr(i32)]
18pub enum HookClass {
19 PreRouting = libc::NF_INET_PRE_ROUTING,
21 In = libc::NF_INET_LOCAL_IN,
23 Forward = libc::NF_INET_FORWARD,
25 Out = libc::NF_INET_LOCAL_OUT,
27 PostRouting = libc::NF_INET_POST_ROUTING,
29}
30
31#[derive(Clone, PartialEq, Eq, Default, Debug)]
32#[nfnetlink_struct(nested = true)]
33pub struct Hook {
34 #[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#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
52#[repr(i32)]
53pub enum ChainPolicy {
54 Accept = NF_ACCEPT,
56 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#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
86pub enum ChainType {
87 Filter,
90 Route,
93 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#[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 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 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}