lab_ops_natmap/
policy_route.rs1use std::process::Command;
2
3use color_eyre::Result;
4use color_eyre::eyre::WrapErr;
5
6use crate::models::PolicyRouteConfig;
7
8pub struct PolicyRouteManager;
9
10impl PolicyRouteManager {
11 pub fn new() -> Self {
12 Self
13 }
14
15 fn check_rule_exists(&self, config: &PolicyRouteConfig) -> Result<bool> {
16 let output = Command::new("ip")
17 .args(["rule", "show"])
18 .output()
19 .wrap_err("Failed to execute ip rule show")?;
20
21 let stdout = String::from_utf8_lossy(&output.stdout);
22 let expected = format!("from {} lookup {}", config.src_ip, config.table);
24 Ok(stdout.contains(&expected))
25 }
26
27 fn check_route_exists(&self, config: &PolicyRouteConfig) -> Result<bool> {
28 let output = Command::new("ip")
29 .args(["route", "show", "table", &config.table.to_string()])
30 .output()
31 .wrap_err("Failed to execute ip route show")?;
32
33 let stdout = String::from_utf8_lossy(&output.stdout);
34 let expected = format!("default via {}", config.via);
35 Ok(stdout.contains(&expected))
36 }
37
38 pub fn install(&self, config: &PolicyRouteConfig) -> Result<()> {
39 if !self.check_route_exists(config)? {
40 let status = Command::new("ip")
41 .args([
42 "route",
43 "add",
44 "default",
45 "via",
46 &config.via,
47 "table",
48 &config.table.to_string(),
49 ])
50 .status()
51 .wrap_err("Failed to execute ip route add")?;
52
53 if !status.success() {
54 color_eyre::eyre::bail!("ip route add failed with status: {}", status);
55 }
56 }
57
58 if !self.check_rule_exists(config)? {
59 let status = Command::new("ip")
60 .args([
61 "rule",
62 "add",
63 "from",
64 &config.src_ip,
65 "table",
66 &config.table.to_string(),
67 ])
68 .status()
69 .wrap_err("Failed to execute ip rule add")?;
70
71 if !status.success() {
72 color_eyre::eyre::bail!("ip rule add failed with status: {}", status);
73 }
74 }
75
76 Ok(())
77 }
78
79 pub fn remove(&self, config: &PolicyRouteConfig) -> Result<()> {
80 let _ = Command::new("ip")
82 .args([
83 "rule",
84 "del",
85 "from",
86 &config.src_ip,
87 "table",
88 &config.table.to_string(),
89 ])
90 .status();
91
92 let _ = Command::new("ip")
93 .args([
94 "route",
95 "del",
96 "default",
97 "via",
98 &config.via,
99 "table",
100 &config.table.to_string(),
101 ])
102 .status();
103
104 Ok(())
105 }
106
107 pub fn flush_all(&self, policy_routes: &[PolicyRouteConfig]) -> Result<()> {
108 for config in policy_routes {
109 self.remove(config)?;
110 }
111 Ok(())
112 }
113}