diff options
author | Himbeer <himbeer@disroot.org> | 2025-03-18 14:41:35 +0100 |
---|---|---|
committer | Himbeer <himbeer@disroot.org> | 2025-03-18 14:41:35 +0100 |
commit | 1319d2e1da02be5842f2b20d50209c99fb6b8df2 (patch) | |
tree | 63415f816ef1b184d7cc8edc032cb2124712376c | |
parent | fd5925655015d6be9f3cfe7b6927f1bc270af28c (diff) |
Add utilities for rule (policy routing) creation and deletion
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/blocking.rs | 12 | ||||
-rw-r--r-- | src/lib.rs | 2 | ||||
-rw-r--r-- | src/rule.rs | 109 |
4 files changed, 124 insertions, 0 deletions
@@ -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); + } +} @@ -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(()) + } +} |