aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHimbeer <himbeer@disroot.org>2025-03-20 16:49:54 +0100
committerHimbeer <himbeer@disroot.org>2025-03-20 16:49:54 +0100
commitc06ed776ab8081d89099de3fb7ddc8a29426a8df (patch)
treefbcaee003a36ebd445f3baf8292ef75123ef9108
parenta425965131b868aa48b70cf96a945aa66e7ea3c5 (diff)
Configure parsed routes, rules and deletions
-rw-r--r--Cargo.lock2
-rw-r--r--src/main.rs295
2 files changed, 291 insertions, 6 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 89c777c..7e0e3f1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -324,7 +324,7 @@ dependencies = [
[[package]]
name = "rsdsl_netlinklib"
version = "0.6.0"
-source = "git+https://github.com/rsdsl/netlinklib.git#9d55957b737cce1ace694376bdd157624286c020"
+source = "git+https://github.com/rsdsl/netlinklib.git#49ce9b051504e9cdab462f4c7c38b3f38d18ce77"
dependencies = [
"futures",
"libc",
diff --git a/src/main.rs b/src/main.rs
index c1d4583..ff0b6ad 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,6 +3,7 @@ use std::fmt;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
+use rsdsl_netlinklib::blocking::Connection;
use rsdsl_netlinklib::rule::RuleAction;
const ROUTES_PATH: &str = "/data/static.rt";
@@ -155,11 +156,35 @@ impl From<std::num::ParseIntError> for RuleParseError {
impl std::error::Error for RuleParseError {}
#[derive(Debug)]
+enum SetupError {
+ Netlinklib(rsdsl_netlinklib::Error),
+}
+
+impl fmt::Display for SetupError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::Netlinklib(e) => write!(f, "rsdsl_netlinklib: {}", e)?,
+ }
+
+ Ok(())
+ }
+}
+
+impl From<rsdsl_netlinklib::Error> for SetupError {
+ fn from(e: rsdsl_netlinklib::Error) -> SetupError {
+ SetupError::Netlinklib(e)
+ }
+}
+
+impl std::error::Error for SetupError {}
+
+#[derive(Debug)]
enum Error {
ParseRoutes(RouteParseError),
ParseRules(RuleParseError),
ReadRoutes(std::io::Error),
ReadRules(std::io::Error),
+ Setup(SetupError),
}
impl fmt::Display for Error {
@@ -169,6 +194,7 @@ impl fmt::Display for Error {
Self::ParseRules(e) => write!(f, "parse rules: {}", e)?,
Self::ReadRoutes(e) => write!(f, "read routes ({}): {}", ROUTES_PATH, e)?,
Self::ReadRules(e) => write!(f, "read rules ({}): {}", RULES_PATH, e)?,
+ Self::Setup(e) => write!(f, "set up route/rule: {}", e)?,
}
Ok(())
@@ -187,6 +213,12 @@ impl From<RuleParseError> for Error {
}
}
+impl From<SetupError> for Error {
+ fn from(e: SetupError) -> Error {
+ Error::Setup(e)
+ }
+}
+
impl std::error::Error for Error {}
#[derive(Debug)]
@@ -195,18 +227,85 @@ enum RouteVersion {
Ipv6,
}
-#[derive(Debug)]
+#[derive(Clone, Debug)]
enum RouteDef {
V4(rsdsl_netlinklib::route::Route4),
V6(rsdsl_netlinklib::route::Route6),
}
-#[derive(Debug)]
+impl RouteDef {
+ fn add(self, c: &Connection) -> Result<(), SetupError> {
+ match self {
+ Self::V4(r) => c.route_add4(r)?,
+ Self::V6(r) => c.route_add6(r)?,
+ }
+
+ Ok(())
+ }
+
+ fn delete(self, c: &Connection) -> Result<(), SetupError> {
+ match self {
+ Self::V4(r) => c.route_del4(r)?,
+ Self::V6(r) => c.route_del6(r)?,
+ }
+
+ Ok(())
+ }
+}
+
+impl fmt::Display for RouteDef {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::V4(r) => {
+ write!(f, "route4 {}/{}", r.dst, r.prefix_len)?;
+ if let Some(rtr) = r.rtr {
+ write!(f, " via {}", rtr)?;
+ }
+ if r.on_link {
+ write!(f, " onlink")?;
+ }
+ if let Some(table) = r.table {
+ write!(f, " table {}", table)?;
+ }
+ if let Some(metric) = r.metric {
+ write!(f, " metric {}", metric)?;
+ }
+ write!(f, " dev {}", r.link)?;
+ }
+ Self::V6(r) => {
+ write!(f, "route6 {}/{}", r.dst, r.prefix_len)?;
+ if let Some(rtr) = r.rtr {
+ write!(f, " via {}", rtr)?;
+ }
+ if r.on_link {
+ write!(f, " onlink")?;
+ }
+ if let Some(table) = r.table {
+ write!(f, " table {}", table)?;
+ }
+ if let Some(metric) = r.metric {
+ write!(f, " metric {}", metric)?;
+ }
+ write!(f, " dev {}", r.link)?;
+ }
+ }
+
+ Ok(())
+ }
+}
+
+#[derive(Clone, Debug)]
struct Route {
delete: bool,
def: RouteDef,
}
+impl fmt::Display for Route {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.def.fmt(f)
+ }
+}
+
impl FromStr for Route {
type Err = RouteParseError;
@@ -343,7 +442,7 @@ impl FromStr for Routes {
}
}
-#[derive(Debug, Default)]
+#[derive(Clone, Debug, Default)]
enum RuleVersion {
#[default]
Both,
@@ -351,7 +450,7 @@ enum RuleVersion {
Ipv6,
}
-#[derive(Debug)]
+#[derive(Clone, Debug)]
struct Rule {
delete: bool,
version: RuleVersion,
@@ -363,6 +462,162 @@ struct Rule {
table: u32,
}
+impl Rule {
+ fn add(self, c: &Connection) -> Result<(), SetupError> {
+ match self.version {
+ RuleVersion::Both => rsdsl_netlinklib::rule::Rule::<()> {
+ invert: self.invert,
+ fwmark: self.fwmark,
+ dst: None,
+ src: None,
+ action: self.action,
+ table: self.table,
+ }
+ .blocking_add(c)?,
+ RuleVersion::Ipv4 => rsdsl_netlinklib::rule::Rule::<Ipv4Addr> {
+ invert: self.invert,
+ fwmark: self.fwmark,
+ dst: self.dst.map(|dst| {
+ if let (IpAddr::V4(addr), cidr) = dst {
+ (addr, cidr)
+ } else {
+ unreachable!()
+ }
+ }),
+ src: self.src.map(|src| {
+ if let (IpAddr::V4(addr), cidr) = src {
+ (addr, cidr)
+ } else {
+ unreachable!()
+ }
+ }),
+ action: self.action,
+ table: self.table,
+ }
+ .blocking_add(c)?,
+ RuleVersion::Ipv6 => rsdsl_netlinklib::rule::Rule::<Ipv6Addr> {
+ invert: self.invert,
+ fwmark: self.fwmark,
+ dst: self.dst.map(|dst| {
+ if let (IpAddr::V6(addr), cidr) = dst {
+ (addr, cidr)
+ } else {
+ unreachable!()
+ }
+ }),
+ src: self.src.map(|src| {
+ if let (IpAddr::V6(addr), cidr) = src {
+ (addr, cidr)
+ } else {
+ unreachable!()
+ }
+ }),
+ action: self.action,
+ table: self.table,
+ }
+ .blocking_add(c)?,
+ };
+
+ Ok(())
+ }
+
+ fn delete(self, c: &Connection) -> Result<(), SetupError> {
+ match self.version {
+ RuleVersion::Both => rsdsl_netlinklib::rule::Rule::<()> {
+ invert: self.invert,
+ fwmark: self.fwmark,
+ dst: None,
+ src: None,
+ action: self.action,
+ table: self.table,
+ }
+ .blocking_del(c)?,
+ RuleVersion::Ipv4 => rsdsl_netlinklib::rule::Rule::<Ipv4Addr> {
+ invert: self.invert,
+ fwmark: self.fwmark,
+ dst: self.dst.map(|dst| {
+ if let (IpAddr::V4(addr), cidr) = dst {
+ (addr, cidr)
+ } else {
+ unreachable!()
+ }
+ }),
+ src: self.src.map(|src| {
+ if let (IpAddr::V4(addr), cidr) = src {
+ (addr, cidr)
+ } else {
+ unreachable!()
+ }
+ }),
+ action: self.action,
+ table: self.table,
+ }
+ .blocking_del(c)?,
+ RuleVersion::Ipv6 => rsdsl_netlinklib::rule::Rule::<Ipv6Addr> {
+ invert: self.invert,
+ fwmark: self.fwmark,
+ dst: self.dst.map(|dst| {
+ if let (IpAddr::V6(addr), cidr) = dst {
+ (addr, cidr)
+ } else {
+ unreachable!()
+ }
+ }),
+ src: self.src.map(|src| {
+ if let (IpAddr::V6(addr), cidr) = src {
+ (addr, cidr)
+ } else {
+ unreachable!()
+ }
+ }),
+ action: self.action,
+ table: self.table,
+ }
+ .blocking_del(c)?,
+ };
+
+ Ok(())
+ }
+}
+
+impl fmt::Display for Rule {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self.version {
+ RuleVersion::Both => write!(f, "rule")?,
+ RuleVersion::Ipv4 => write!(f, "rule4")?,
+ RuleVersion::Ipv6 => write!(f, "rule6")?,
+ }
+ if self.invert {
+ write!(f, " invert true")?;
+ }
+ if let Some(fwmark) = self.fwmark {
+ write!(f, " fwmark {}", fwmark)?;
+ }
+ if let Some(dst) = self.dst {
+ write!(f, " dst {}/{}", dst.0, dst.1)?;
+ }
+ if let Some(src) = self.src {
+ write!(f, " src {}/{}", src.0, src.1)?;
+ }
+ match self.action {
+ RuleAction::Unspec => write!(f, " action unspec")?,
+ RuleAction::ToTable => write!(f, " action to_table")?,
+ RuleAction::Goto => write!(f, " action goto")?,
+ RuleAction::Nop => write!(f, " action nop")?,
+ RuleAction::Blackhole => write!(f, " action blackhole")?,
+ RuleAction::Unreachable => write!(f, " action unreachable")?,
+ RuleAction::Prohibit => write!(f, " action prohibit")?,
+ RuleAction::Other(a) => write!(f, " action {}", a)?,
+ _ => write!(f, " action ?")?,
+ }
+ if self.action == RuleAction::ToTable {
+ write!(f, " table {}", self.table)?;
+ }
+
+ Ok(())
+ }
+}
+
impl FromStr for Rule {
type Err = RuleParseError;
@@ -557,5 +812,35 @@ fn run() -> Result<(), Error> {
};
let rules: Rules = rules.parse()?;
- todo!()
+ let conn = Connection::new().map_err(SetupError::from)?;
+
+ for route in routes.routes {
+ match route.def.clone().delete(&conn) {
+ Ok(_) => println!("[info] del {}", route),
+ Err(e) => println!("[warn] del {}: {}", route, e),
+ }
+
+ if !route.delete {
+ match route.def.clone().add(&conn) {
+ Ok(_) => println!("[info] add {}", route),
+ Err(e) => println!("[warn] add {}: {}", route, e),
+ }
+ }
+ }
+
+ for rule in rules.rules {
+ match rule.clone().delete(&conn) {
+ Ok(_) => println!("[info] del {}", rule),
+ Err(e) => println!("[warn] del {}: {}", rule, e),
+ }
+
+ if !rule.delete {
+ match rule.clone().add(&conn) {
+ Ok(_) => println!("[info] add {}", rule),
+ Err(e) => println!("[warn] add {}: {}", rule, e),
+ }
+ }
+ }
+
+ Ok(())
}