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