aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHimbeer <himbeer@disroot.org>2025-03-18 14:41:35 +0100
committerHimbeer <himbeer@disroot.org>2025-03-18 14:41:35 +0100
commit1319d2e1da02be5842f2b20d50209c99fb6b8df2 (patch)
tree63415f816ef1b184d7cc8edc032cb2124712376c
parentfd5925655015d6be9f3cfe7b6927f1bc270af28c (diff)
Add utilities for rule (policy routing) creation and deletion
-rw-r--r--Cargo.toml1
-rw-r--r--src/blocking.rs12
-rw-r--r--src/lib.rs2
-rw-r--r--src/rule.rs109
4 files changed, 124 insertions, 0 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 5a48d63..3ec6d6b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,6 +19,7 @@ default = ["addr", "link", "route"]
addr = []
link = ["status"]
route = []
+rule = []
tunnel = []
status = []
blocking = ["tokio/rt-multi-thread"]
diff --git a/src/blocking.rs b/src/blocking.rs
index 7cc90d3..7c47b5b 100644
--- a/src/blocking.rs
+++ b/src/blocking.rs
@@ -107,3 +107,15 @@ pub mod route {
blockify!(route_add6, r: Route6);
}
}
+
+#[cfg(feature = "rule")]
+pub mod rule {
+ use super::Connection;
+
+ use crate::rule::Rule;
+
+ impl Connection {
+ blockify!(rule_add, r: Rule);
+ blockify!(rule_del, r: Rule);
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index e417659..dbfde40 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -10,6 +10,8 @@ pub mod addr;
pub mod link;
#[cfg(feature = "route")]
pub mod route;
+#[cfg(feature = "rule")]
+pub mod rule;
#[cfg(feature = "tunnel")]
pub mod tunnel;
diff --git a/src/rule.rs b/src/rule.rs
new file mode 100644
index 0000000..044c067
--- /dev/null
+++ b/src/rule.rs
@@ -0,0 +1,109 @@
+//! Simple functions to add and delete rules (for policy routing).
+
+use crate::{Connection, Error, Result};
+
+pub use netlink_packet_route::rule::RuleAction;
+
+use netlink_packet_route::rule::{RuleAttribute, RuleFlag, RuleHeader, RuleMessage};
+use rtnetlink::RuleAddRequest;
+
+trait IpAddr46 {}
+
+impl IpAddr46 for () {}
+impl IpAddr46 for Ipv4Addr {}
+impl IpAddr46 for Ipv6Addr {}
+
+/// A rule entry.
+#[derive(Debug)]
+pub struct Rule<A: IpAddr46> {
+ /// Whether to invert the matching criteria.
+ pub invert: bool,
+ /// Firewall mark to match against.
+ pub fwmark: Option<u32>,
+ /// Destination prefix to match against.
+ pub dst: Option<(A, u8)>,
+ /// Source prefix to match against.
+ pub src: Option<(A, u8)>,
+ /// Action to perform.
+ pub action: RuleAction,
+ /// Routing table to use if `RuleAction::ToTable` is selected.
+ pub table: u32,
+}
+
+impl Rule<()> {
+ fn addSrcDst(&self, rq: RuleAddRequest) -> RuleAddRequest {
+ rq
+ }
+}
+
+impl Rule<Ipv4Addr> {
+ fn addSrcDst(&self, mut rq: RuleAddRequest) -> RuleAddRequest {
+ rq = rq.v4();
+ if let Some(dst) = self.dst {
+ rq = rq.destination_prefix(dst.0, dst.1);
+ }
+ if let Some(src) = self.src {
+ rq = rq.destination_prefix(src.0, src.1);
+ }
+
+ rq
+ }
+}
+
+impl Rule<Ipv6Addr> {
+ fn addSrcDst(&self, mut rq: RuleAddRequest) -> RuleAddRequest {
+ rq = rq.v6();
+ if let Some(dst) = self.dst {
+ rq = rq.destination_prefix(dst.0, dst.1);
+ }
+ if let Some(src) = self.src {
+ rq = rq.destination_prefix(src.0, src.1);
+ }
+
+ rq
+ }
+}
+
+impl<A: IpAddr46> Connection {
+ /// Adds a rule entry.
+ pub async fn rule_add(&self, r: Rule<A>) -> Result<()> {
+ let mut add = self.handle().rule().add().action(r.action);
+
+ if let Some(fwmark) = r.fwmark {
+ add = add.fw_mark(fwmark);
+ }
+ if let Some(table) = r.table {
+ add = add.table_id(table);
+ }
+
+ add = r.addSrcDst(add);
+
+ add.message_mut().header.flags.push(RuleFlag::Invert);
+
+ add.execute().await?;
+ Ok(())
+ }
+
+ /// Deletes a rule entry.
+ pub async fn rule_del(&self, r: Rule<A>) -> Result<()> {
+ let mut add = self.handle().rule().add().action(r.action);
+
+ if let Some(fwmark) = r.fwmark {
+ add = add.fw_mark(fwmark);
+ }
+ if let Some(table) = r.table {
+ add = add.table_id(table);
+ }
+
+ add = r.addSrcDst(add);
+
+ add.message_mut().header.flags.push(RuleFlag::Invert);
+
+ self.handle()
+ .rule()
+ .del(add.message_mut().clone())
+ .execute()
+ .await?;
+ Ok(())
+ }
+}