nftnl_rs/netlink/netlink.rs
1
2/*-
3 * nftnl-rs - a netlink NFtables firewall.
4 * Copyright (C) 2020 Aleksandr Morozov
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public
7 * License, v. 2.0. If a copy of the MPL was not distributed with this
8 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9 */
10
11use bitflags::bitflags;
12use crate::error::NftnlError;
13use crate::int_error_code;
14use crate::netlink::NLA_TYPE_MASK;
15use crate::{netlink::mnl_align, error::NtflRes};
16
17use super::linux::*;
18
19bitflags! {
20 /// Flags values for the batch header.
21 pub struct NlmFFlags: u16
22 {
23 /// It is request message.
24 const NLM_F_REQUEST = 1;
25
26 /// Multipart message, terminated by NLMSG_DONE
27 const NLM_F_MULTI = 2;
28
29 /// Reply with ack, with zero or error code
30 const NLM_F_ACK = 4;
31
32 /// Echo this request
33 const NLM_F_ECHO = 8;
34
35 /// Dump was inconsistent due to sequence change
36 const NLM_F_DUMP_INTR = 16;
37
38 /* Modifiers to GET request */
39
40 /// specify tree root
41 const NLM_F_ROOT = 0x100;
42
43 /// return all matching
44 const NLM_F_MATCH = 0x200;
45 /// atomic GET
46 const NLM_F_ATOMIC = 0x400;
47 const NLM_F_DUMP = Self::NLM_F_ROOT.union(Self::NLM_F_MATCH).bits();
48
49 /* Modifiers to NEW request */
50
51 /// Override existing
52 const NLM_F_REPLACE = 0x100;
53
54 /// Do not touch, if it exists
55 const NLM_F_EXCL = 0x200;
56
57 /// Create, if it does not exist
58 const NLM_F_CREATE = 0x400;
59
60 /// Add to end of list
61 const NLM_F_APPEND = 0x800;
62 }
63}
64
65
66
67/// A binding structure for the `struct nlattr`.
68///
69/// A quote from the `libmnl`:
70/// <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)-->
71/// +---------------------+- - -+- - - - - - - - - -+- - -+
72/// | Header | Pad | Payload | Pad |
73/// | (struct nlattr) | ing | | ing |
74/// +---------------------+- - -+- - - - - - - - - -+- - -+
75/// <-------------- nlattr->nla_len -------------->
76#[repr(C)]
77#[derive(Copy, Clone, Debug)]
78pub struct Nlattr
79{
80 pub nla_len: __u16,
81 pub nla_type: __u16,
82}
83
84impl Nlattr
85{
86 /// Get type of netlink attribute automatically converting it into the
87 /// generic type D.
88 ///
89 /// # Returns
90 ///
91 /// A [Result] in form of the type [NtflRes] is returned:
92 ///
93 /// * [Result::Ok] with the inner type generic `D`
94 ///
95 /// * [Result::Err] with the error description.
96 pub
97 fn mnl_attr_get_type<D>(&self) -> NtflRes<D>
98 where D: TryFrom<u16, Error = NftnlError>
99 {
100 return D::try_from(self.nla_type & NLA_TYPE_MASK);
101 }
102
103 /// Performs a validation of the raw data which should be a string.
104 ///
105 /// # Arguments
106 ///
107 /// * `data` - a slice of raw data.
108 ///
109 /// # Returns
110 ///
111 /// A [Result] as alias [NtflRes] is returned.
112 ///
113 /// * [Result::Ok] is returned if validation was successfull.
114 ///
115 /// * [Result::Err] is returned with error description if validation
116 /// failed.
117 pub
118 fn validate_string(&self, data: &[u8]) -> NtflRes<()>
119 {
120 let full_data_len = data.len() + mnl_align(std::mem::size_of::<Nlattr>());
121
122 if self.nla_len == 0
123 {
124 int_error_code!(libc::EINVAL, "empty string!");
125 }
126 else if full_data_len != self.nla_len as usize
127 {
128 int_error_code!(libc::EINVAL, "expected string len: '{}' got '{}'!", full_data_len, self.nla_len);
129 }
130
131 return Ok(());
132 }
133
134 /// Performs a validation of the raw data which should be a NULL terminated C-string.
135 ///
136 /// # Arguments
137 ///
138 /// * `data` - a slice of raw data.
139 ///
140 /// # Returns
141 ///
142 /// A [Result] as alias [NtflRes] is returned.
143 ///
144 /// * [Result::Ok] is returned if validation was successfull.
145 ///
146 /// * [Result::Err] is returned with error description if validation
147 /// failed.
148 pub
149 fn validate_null_string(&self, data: &[u8]) -> NtflRes<()>
150 {
151 let full_data_len = mnl_align(data.len() + mnl_align(std::mem::size_of::<Nlattr>()));
152
153 if self.nla_len == 0
154 {
155 int_error_code!(libc::EINVAL, "empty string!");
156 }
157 else if full_data_len != mnl_align(self.nla_len as usize)
158 {
159 int_error_code!(libc::EINVAL, "expected null string len: '{}' got '{}'!", full_data_len, self.nla_len);
160 }
161 else if *data.last().unwrap() != 0
162 {
163 int_error_code!(libc::EINVAL, "string is not null terminated!");
164 }
165
166 return Ok(());
167 }
168
169 /// Performs a validation of the raw data which should be a nested block of data
170 /// the content of which is unknown.
171 ///
172 /// # Arguments
173 ///
174 /// * `data` - a slice of raw data.
175 ///
176 /// # Returns
177 ///
178 /// A [Result] as alias [NtflRes] is returned.
179 ///
180 /// * [Result::Ok] is returned if validation was successfull.
181 ///
182 /// * [Result::Err] is returned with error description if validation
183 /// failed.
184 pub
185 fn validate_nested(&self, data: &[u8]) -> NtflRes<()>
186 {
187 let full_data_len = data.len() + mnl_align(std::mem::size_of::<Nlattr>());
188
189 if full_data_len != self.nla_len as usize
190 {
191 int_error_code!(libc::EINVAL, "expected string len: '{}' got '{}'!", full_data_len, self.nla_len);
192 }
193 else if self.nla_len == 0
194 {
195 // empty nested attributes are OK.
196 return Ok(());
197 }
198 else if (self.nla_len as usize) < mnl_align(std::mem::size_of::<Nlattr>())
199 {
200 int_error_code!(libc::EINVAL, "corrupted nested attribute: size less 1 header: '{}'",
201 self.nla_len);
202 }
203
204 return Ok(());
205 }
206}
207
208
209/// Error message.
210#[repr(C)]
211#[derive(Copy, Clone, Debug, PartialEq, Eq)]
212pub struct Nlmsgerr
213{
214 /// Error number
215 error: i32,
216
217 /// A message header
218 msg: Nlmsghdr
219}
220
221impl Nlmsgerr
222{
223 pub
224 fn get_error(&self) -> i32
225 {
226 return self.error;
227 }
228}
229
230
231/// A message header.
232///
233/// A copy of the packet description from the libmnl
234/// * Netlink message:
235/// ```text
236/// |<----------------- 4 bytes ------------------->|
237/// |<----- 2 bytes ------>|<------- 2 bytes ------>|
238/// |-----------------------------------------------|
239/// | Message length (including header) |
240/// |-----------------------------------------------|
241/// | Message type | Message flags |
242/// |-----------------------------------------------|
243/// | Message sequence number |
244/// |-----------------------------------------------|
245/// | Netlink PortID |
246/// |-----------------------------------------------|
247/// | |
248/// . Payload .
249/// |_______________________________________________|
250/// ```
251///
252/// ```text
253/// There is usually an extra header after the the Netlink header (at the
254/// beginning of the payload). This extra header is specific of the Netlink
255/// subsystem. After this extra header, it comes the sequence of attributes
256/// that are expressed in Type-Length-Value (TLV) format.
257/// ```
258#[repr(C)]
259#[derive(Copy, Clone, Debug, PartialEq, Eq)]
260pub struct Nlmsghdr
261{
262 /// Length of message including header
263 pub nlmsg_len: __u32,
264
265 /// Message content
266 pub nlmsg_type: __u16,
267
268 /// Additional flags
269 pub nlmsg_flags: __u16,
270
271 /// Sequence number
272 pub nlmsg_seq: __u32,
273
274 /// Sending process port ID
275 pub nlmsg_pid: __u32,
276}
277
278impl Nlmsghdr
279{
280 pub
281 fn nftnl_nlmsg_flags_native(&self) -> Option<NlmFFlags>
282 {
283 return NlmFFlags::from_bits(self.nlmsg_flags);
284 }
285
286 /// verifies that port id value is correct.
287 pub
288 fn mnl_nlmsg_portid_ok(&self, portid: u32) -> bool
289 {
290 let res =
291 if portid > 0
292 {
293 self.nlmsg_pid == portid
294 }
295 else
296 {
297 true
298 };
299
300 return self.nlmsg_pid > 0 && res;
301 }
302
303 /// Verifies the sequence number is correct.
304 pub
305 fn mnl_nlmsg_seq_ok(&self, seq: u32) -> bool
306 {
307 let res =
308 if seq > 0
309 {
310 self.nlmsg_seq == seq
311 }
312 else
313 {
314 true
315 };
316
317 return self.nlmsg_seq > 0 && res;
318 }
319
320}
321