ipfw_rs/lib.rs
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2021 Aleksandr Morozov, RELKOM s.r.o
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 */
32
33//! ipfw-rs
34//!
35//! <img src="https://cdn.4neko.org/ipfw.webp" width="280"/> <img src="https://cdn.4neko.org/source_avail.webp" width="280"/> <img src="https://cdn.4neko.org/bds2_2.webp" width="280"/>
36//!
37//! Provides a userspace (partial) interface to the raw socket of the FreeBSD's firewall IPFW.
38//!
39//! Supports:
40//! - Add, Test, Delete multiple IP/IPv6/DNS
41//!
42//! This crate supports only FreeBSD 14, but it that protocol is compatiable with the FreeBSD 13.
43//!
44//! This is an experimantal crate!
45extern crate libc;
46extern crate nix;
47extern crate mac_address;
48extern crate cdns_rs;
49
50mod xbuffer;
51mod ipfw;
52mod ipfw2;
53mod misc;
54
55mod portable;
56
57#[macro_use] mod common;
58#[macro_use] pub mod runtime_exception;
59
60pub use ipfw_api::*;
61
62pub mod ipfw_api
63{
64 use std::os::fd::{AsFd, FromRawFd, OwnedFd};
65
66 use nix::errno::Errno;
67
68 use crate::{ipfw::{ipfw_check_name, ipfw_obj_header}, ipfw2::IP_FW3Opcodes, runtime_exception::IpfwResult};
69
70
71 pub enum IpfwCmd<'host>
72 {
73 /// Adds a host/hostname to table
74 Add{ hosts: Vec<&'host str>, req_atomic_op: bool },
75
76 /// Removes a host/hostname from table
77 Delete{ hosts: Vec<&'host str> },
78
79 /// Checks if host/hostname presents in table
80 Test{ hosts: Vec<&'host str> },
81
82 /// Flushes the
83 Flush,
84 }
85
86 impl<'host> IpfwCmd<'host>
87 {
88 pub
89 fn require_atomic(&self) -> bool
90 {
91 match *self
92 {
93 Self::Add{ req_atomic_op, .. } =>
94 return req_atomic_op,
95 _ =>
96 return false,
97 }
98 }
99
100 pub
101 fn count_hosts(&self) -> usize
102 {
103 match *self
104 {
105 Self::Add{ ref hosts, .. } => return hosts.len(),
106 Self::Delete{ ref hosts } => return hosts.len(),
107 Self::Test{ ref hosts } => return hosts.len(), // lookup
108 Self::Flush => return 0,
109 }
110 }
111 }
112
113
114 /// The crates main, public instance which should be used by the program.
115 pub struct Ipfw
116 {
117 /// A raw socket to the IPFW instance.
118 socket_fd: OwnedFd,
119 }
120
121 impl Ipfw
122 {
123 /// Creates new instance and opens the connection to the IPFW instance in kernel.
124 pub
125 fn new() -> IpfwResult<Self>
126 {
127 let fd =
128 unsafe
129 {
130 libc::socket(libc::AF_INET, libc::SOCK_RAW, libc::IPPROTO_RAW)
131 };
132
133 if fd < 0
134 {
135 runtime_error!("socket() failed: {}", nix::Error::last());
136 }
137
138 return Ok(
139 Self{ socket_fd: unsafe{ OwnedFd::from_raw_fd(fd) } }
140 );
141 }
142
143 // https://www.unix.com/man-page/FreeBSD/8/ipfw/
144 /// A commands over tables only.
145 ///
146 /// # Arguments
147 ///
148 /// * `tablename` - a title of the table.
149 ///
150 /// * `cmd` - a [IpfwCmd] command which is required to run.
151 ///
152 /// * `quite` - do not output to stdout (unused).
153 ///
154 /// # Returns
155 ///
156 /// A [Result] is returned as type [IpfwResult]
157 ///
158 /// * [Result::Ok] - with the amount of the successfull operations.
159 ///
160 /// * [Result::Err] - an error description witht eh error code.
161 pub
162 fn ipfw_table_handler<T>(&self, tablename: T, cmd: IpfwCmd, quite: bool) -> IpfwResult<i32>
163 where T: AsRef<str>
164 {
165 //let do_add: bool = false;
166 let mut is_all: libc::c_int = 0;
167 let mut oh = ipfw_obj_header::new_zeroed();
168 let tbl_name = tablename.as_ref();
169
170 let set = 0;
171 /*if g_co_set != 0
172 {
173 g_co_set - 1
174 }
175 else
176 {
177 0
178 };*/
179
180 // performing operations on table name
181 match ipfw_check_name(tbl_name)
182 {
183 Ok(_) =>
184 {
185 oh.ntlv.table_fill_ntlv(tbl_name, set, 1)?;
186 oh.idx = 1;
187 },
188 Err(e) =>
189 {
190 if tbl_name == "all"
191 {
192 is_all = 1;
193 }
194 else
195 {
196 return Err(e);
197 }
198 }
199 }
200
201 // table command
202 // tcmd = get_token(tablecmds, *av, "table command");
203 //let tcmd = _s_x::get_token(tablecmds, IpfwCmd::to_cmd(&self), "table command")?;
204
205 /*if req_atomic_op == true
206 {
207 if tcmd != tokens::TOK_ADD
208 {
209 runtime_error!("atomic is not compatible with: '{}'", IpfwCmd::to_cmd(&self));
210 }
211 }*/
212
213 /*match tcmd
214 {
215 tokens::TOK_LIST | tokens::TOK_INFO |
216 tokens::TOK_DETAIL |
217 tokens::TOK_FLUSH | tokens::TOK_DESTROY => {},
218 _ =>
219 {
220 if is_all != 0
221 {
222 runtime_error!("table name is required");
223 }
224 }
225 }*/
226 match cmd
227 {
228 IpfwCmd::Flush => {},
229 _ =>
230 {
231 if is_all != 0
232 {
233 runtime_error!("table name is required");
234 }
235 }
236 }
237
238 // https://github.com/freebsd/freebsd-src/blob/373ffc62c158e52cde86a5b934ab4a51307f9f2e/sbin/ipfw/tables.c#L225
239 match cmd
240 {
241 IpfwCmd::Add{ .. } | IpfwCmd::Delete{ .. } =>
242 {
243 //C table_modify_record(&oh, ac, av, do_add, g_co.do_quiet, g_co.do_quiet, atomic);
244 // I don't know what is update argument but it is supplied with quite flag, so when quite flag is
245 // raised then update is 1
246 //todo
247 return oh.table_modify_record(self.socket_fd.as_fd(), cmd, quite);
248 },
249 /*tokens::TOK_CREATE =>
250 {
251
252 },*/
253 /*TOK_MODIFY =>
254 {
255
256 },*/
257 /*TOK_DESTROY =>
258 {
259
260 },*/
261 IpfwCmd::Flush =>
262 {
263 // Flush ALL only!
264 match IP_FW3Opcodes::table_flush(self.socket_fd.as_fd(), &oh)
265 {
266 Ok(_) =>
267 return Ok(0),
268 Err(e) if e.get_errno() == Errno::ESRCH =>
269 return Ok(0),
270 Err(e) =>
271 runtime_error!("Flush of table: '{}' failed with error: {}", tbl_name, e),
272 }
273 },
274 //TOK_SWAP
275 //TOK_LOCK | TOK_UNLOCK
276 //TOK_DETAIL | TOK_INFO
277 // TOK_LIST
278 IpfwCmd::Test{ hosts } => // TOK_LOOKUP
279 {
280 let mut ret: i32 = 0;
281 for host in hosts
282 {
283 match oh.table_lookup(self.socket_fd.as_fd(), host)
284 {
285 Ok(res) => ret += res,
286 Err(e) =>
287 {
288 println!("{}", e)
289 }
290 }
291
292 }
293
294 return Ok(ret);
295 }
296 }
297
298 }
299
300 }
301
302}