1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
|
use crate::{MsgType, Table};
use crate::sys::{self as sys, libc};
#[cfg(feature = "query")]
use std::convert::TryFrom;
use std::{
ffi::{c_void, CStr, CString},
fmt,
os::raw::c_char,
rc::Rc,
};
pub type Priority = i32;
/// The netfilter event hooks a chain can register for.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(u16)]
pub enum Hook {
/// Hook into the pre-routing stage of netfilter. Corresponds to `NF_INET_PRE_ROUTING`.
PreRouting = libc::NF_INET_PRE_ROUTING as u16,
/// Hook into the input stage of netfilter. Corresponds to `NF_INET_LOCAL_IN`.
In = libc::NF_INET_LOCAL_IN as u16,
/// Hook into the forward stage of netfilter. Corresponds to `NF_INET_FORWARD`.
Forward = libc::NF_INET_FORWARD as u16,
/// Hook into the output stage of netfilter. Corresponds to `NF_INET_LOCAL_OUT`.
Out = libc::NF_INET_LOCAL_OUT as u16,
/// Hook into the post-routing stage of netfilter. Corresponds to `NF_INET_POST_ROUTING`.
PostRouting = libc::NF_INET_POST_ROUTING as u16,
}
/// A chain policy. Decides what to do with a packet that was processed by the chain but did not
/// match any rules.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(u32)]
pub enum Policy {
/// Accept the packet.
Accept = libc::NF_ACCEPT as u32,
/// Drop the packet.
Drop = libc::NF_DROP as u32,
}
/// Base chain type.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ChainType {
/// Used to filter packets.
/// Supported protocols: ip, ip6, inet, arp, and bridge tables.
Filter,
/// Used to reroute packets if IP headers or packet marks are modified.
/// Supported protocols: ip, and ip6 tables.
Route,
/// Used to perform NAT.
/// Supported protocols: ip, and ip6 tables.
Nat,
}
impl ChainType {
fn as_c_str(&self) -> &'static [u8] {
match *self {
ChainType::Filter => b"filter\0",
ChainType::Route => b"route\0",
ChainType::Nat => b"nat\0",
}
}
}
/// Abstraction of a `nftnl_chain`. Chains reside inside [`Table`]s and they hold [`Rule`]s.
///
/// There are two types of chains, "base chain" and "regular chain". See [`set_hook`] for more
/// details.
///
/// [`Table`]: struct.Table.html
/// [`Rule`]: struct.Rule.html
/// [`set_hook`]: #method.set_hook
pub struct Chain {
pub(crate) chain: *mut sys::nftnl_chain,
pub(crate) table: Rc<Table>,
}
impl Chain {
/// Creates a new chain instance inside the given [`Table`] and with the given name.
///
/// [`Table`]: struct.Table.html
pub fn new<T: AsRef<CStr>>(name: &T, table: Rc<Table>) -> Chain {
unsafe {
let chain = try_alloc!(sys::nftnl_chain_alloc());
sys::nftnl_chain_set_u32(
chain,
sys::NFTNL_CHAIN_FAMILY as u16,
table.get_family() as u32,
);
sys::nftnl_chain_set_str(
chain,
sys::NFTNL_CHAIN_TABLE as u16,
table.get_name().as_ptr(),
);
sys::nftnl_chain_set_str(chain, sys::NFTNL_CHAIN_NAME as u16, name.as_ref().as_ptr());
Chain { chain, table }
}
}
pub unsafe fn from_raw(chain: *mut sys::nftnl_chain, table: Rc<Table>) -> Self {
Chain { chain, table }
}
/// Sets the hook and priority for this chain. Without calling this method the chain will
/// become a "regular chain" without any hook and will thus not receive any traffic unless
/// some rule forward packets to it via goto or jump verdicts.
///
/// By calling `set_hook` with a hook the chain that is created will be registered with that
/// hook and is thus a "base chain". A "base chain" is an entry point for packets from the
/// networking stack.
pub fn set_hook(&mut self, hook: Hook, priority: Priority) {
unsafe {
sys::nftnl_chain_set_u32(self.chain, sys::NFTNL_CHAIN_HOOKNUM as u16, hook as u32);
sys::nftnl_chain_set_s32(self.chain, sys::NFTNL_CHAIN_PRIO as u16, priority);
}
}
/// Set the type of a base chain. This only applies if the chain has been registered
/// with a hook by calling `set_hook`.
pub fn set_type(&mut self, chain_type: ChainType) {
unsafe {
sys::nftnl_chain_set_str(
self.chain,
sys::NFTNL_CHAIN_TYPE as u16,
chain_type.as_c_str().as_ptr() as *const c_char,
);
}
}
/// Sets the default policy for this chain. That means what action netfilter will apply to
/// packets processed by this chain, but that did not match any rules in it.
pub fn set_policy(&mut self, policy: Policy) {
unsafe {
sys::nftnl_chain_set_u32(self.chain, sys::NFTNL_CHAIN_POLICY as u16, policy as u32);
}
}
/// Returns the userdata of this chain.
pub fn get_userdata(&self) -> Option<&CStr> {
unsafe {
let ptr = sys::nftnl_chain_get_str(self.chain, sys::NFTNL_CHAIN_USERDATA as u16);
if ptr == std::ptr::null() {
return None;
}
Some(CStr::from_ptr(ptr))
}
}
/// Updates the userdata of this chain.
pub fn set_userdata(&self, data: &CStr) {
unsafe {
sys::nftnl_chain_set_str(self.chain, sys::NFTNL_CHAIN_USERDATA as u16, data.as_ptr());
}
}
/// Returns the name of this chain.
pub fn get_name(&self) -> &CStr {
unsafe {
let ptr = sys::nftnl_chain_get_str(self.chain, sys::NFTNL_CHAIN_NAME as u16);
if ptr.is_null() {
panic!("Impossible situation: retrieving the name of a chain failed")
} else {
CStr::from_ptr(ptr)
}
}
}
/// Returns a textual description of the chain.
pub fn get_str(&self) -> CString {
let mut descr_buf = vec![0i8; 4096];
unsafe {
sys::nftnl_chain_snprintf(
descr_buf.as_mut_ptr() as *mut c_char,
(descr_buf.len() - 1) as u64,
self.chain,
sys::NFTNL_OUTPUT_DEFAULT,
0,
);
CStr::from_ptr(descr_buf.as_ptr() as *mut c_char).to_owned()
}
}
/// Returns a reference to the [`Table`] this chain belongs to.
///
/// [`Table`]: struct.Table.html
pub fn get_table(&self) -> Rc<Table> {
self.table.clone()
}
#[cfg(feature = "unsafe-raw-handles")]
/// Returns the raw handle.
pub fn as_ptr(&self) -> *const sys::nftnl_chain {
self.chain as *const sys::nftnl_chain
}
#[cfg(feature = "unsafe-raw-handles")]
/// Returns a mutable version of the raw handle.
pub fn as_mut_ptr(&mut self) -> *mut sys::nftnl_chain {
self.chain
}
}
impl fmt::Debug for Chain {
/// Returns a string representation of the chain.
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "{:?}", self.get_str())
}
}
impl PartialEq for Chain {
fn eq(&self, other: &Self) -> bool {
self.get_table() == other.get_table() && self.get_name() == other.get_name()
}
}
unsafe impl crate::NlMsg for Chain {
unsafe fn write(&self, buf: *mut c_void, seq: u32, msg_type: MsgType) {
let raw_msg_type = match msg_type {
MsgType::Add => libc::NFT_MSG_NEWCHAIN,
MsgType::Del => libc::NFT_MSG_DELCHAIN,
};
let flags: u16 = match msg_type {
MsgType::Add => (libc::NLM_F_ACK | libc::NLM_F_CREATE) as u16,
MsgType::Del => libc::NLM_F_ACK as u16,
} | libc::NLM_F_ACK as u16;
let header = sys::nftnl_nlmsg_build_hdr(
buf as *mut c_char,
raw_msg_type as u16,
self.table.get_family() as u16,
flags,
seq,
);
sys::nftnl_chain_nlmsg_build_payload(header, self.chain);
}
}
impl Drop for Chain {
fn drop(&mut self) {
unsafe { sys::nftnl_chain_free(self.chain) };
}
}
#[cfg(feature = "query")]
pub fn get_chains_cb<'a>(
header: &libc::nlmsghdr,
(table, chains): &mut (&Rc<Table>, &mut Vec<Chain>),
) -> libc::c_int {
unsafe {
let chain = sys::nftnl_chain_alloc();
if chain == std::ptr::null_mut() {
return mnl::mnl_sys::MNL_CB_ERROR;
}
let err = sys::nftnl_chain_nlmsg_parse(header, chain);
if err < 0 {
error!("Failed to parse nelink chain message - {}", err);
sys::nftnl_chain_free(chain);
return err;
}
let table_name = CStr::from_ptr(sys::nftnl_chain_get_str(
chain,
sys::NFTNL_CHAIN_TABLE as u16,
));
let family = sys::nftnl_chain_get_u32(chain, sys::NFTNL_CHAIN_FAMILY as u16);
let family = match crate::ProtoFamily::try_from(family as i32) {
Ok(family) => family,
Err(crate::InvalidProtocolFamily) => {
error!("The netlink table didn't have a valid protocol family !?");
sys::nftnl_chain_free(chain);
return mnl::mnl_sys::MNL_CB_ERROR;
}
};
if table_name != table.get_name() {
sys::nftnl_chain_free(chain);
return mnl::mnl_sys::MNL_CB_OK;
}
if family != crate::ProtoFamily::Unspec && family != table.get_family() {
sys::nftnl_chain_free(chain);
return mnl::mnl_sys::MNL_CB_OK;
}
chains.push(Chain::from_raw(chain, table.clone()));
}
mnl::mnl_sys::MNL_CB_OK
}
#[cfg(feature = "query")]
pub fn list_chains_for_table(table: Rc<Table>) -> Result<Vec<Chain>, crate::query::Error> {
crate::query::list_objects_with_data(libc::NFT_MSG_GETCHAIN as u16, get_chains_cb, &table, None)
}
|