brctl/
bridge.rs

1use crate::{BridgeController, CommandError, CommandExecutor};
2use lazy_regex::regex_captures;
3use log::{debug, error, info};
4use std::fmt;
5
6#[derive(Debug)]
7pub struct Bridge {
8	name: String,
9}
10
11impl fmt::Display for Bridge {
12	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
13		write!(f, "{}", self.name)
14	}
15}
16
17impl Bridge {
18	pub fn new(name: &str) -> Bridge {
19		info!("Creating new Bridge: {}", name);
20		Bridge { name: name.to_string() }
21	}
22
23	pub fn get_name(&self) -> &str {
24		&self.name
25	}
26
27	pub fn delete(&self) -> Result<(), CommandError> {
28		info!("Deleting Bridge: {}", self.name);
29		BridgeController::delete_bridge(&self.name)
30	}
31
32	pub fn add_interface(&self, interface: &str) -> Result<(), CommandError> {
33		info!("Adding interface {} to Bridge: {}", interface, self.name);
34		CommandExecutor::run_command(vec!["brctl", "addif", &self.name, interface]).map(|_| ())
35	}
36
37	pub fn remove_interface(&self, interface: &str) -> Result<(), CommandError> {
38		info!("Removing interface {} from Bridge: {}", interface, self.name);
39		CommandExecutor::run_command(vec!["brctl", "delif", &self.name, interface]).map(|_| ())
40	}
41
42	pub fn set_hairpin(&self, port: &str, enable: bool) -> Result<(), CommandError> {
43		let state = if enable { "on" } else { "off" };
44		info!("Setting hairpin {} on port {} for Bridge: {}", state, port, self.name);
45		CommandExecutor::run_command(vec!["brctl", "hairpin", &self.name, port, state]).map(|_| ())
46	}
47
48	pub fn set_stp(&self, enable: bool) -> Result<(), CommandError> {
49		let state = if enable { "on" } else { "off" };
50		info!("Setting STP {} for Bridge: {}", state, self.name);
51		CommandExecutor::run_command(vec!["brctl", "stp", &self.name, state]).map(|_| ())
52	}
53
54	pub fn set_ageing_time(&self, time: u32) -> Result<(), CommandError> {
55		info!("Setting ageing time to {} for Bridge: {}", time, self.name);
56		CommandExecutor::run_command(vec!["brctl", "setageing", &self.name, &time.to_string()]).map(|_| ())
57	}
58
59	fn show(&self) -> Result<String, CommandError> {
60		debug!("Fetching details for Bridge: {}", self.name);
61		let output = CommandExecutor::run_command(vec!["brctl", "show", &self.name])?;
62		let stdout = String::from_utf8_lossy(&output.stdout).to_string();
63		Ok(stdout)
64	}
65
66	pub fn get_id(&self) -> Result<String, CommandError> {
67		debug!("Getting ID for Bridge: {}", self.name);
68		let details = self.show()?;
69
70		if let Some((_, _, bridge_id)) = regex_captures!(r"^\s*(\S+)\s+(\S+)\s+", &details.lines().nth(1).unwrap_or(""))
71		{
72			return Ok(bridge_id.to_string());
73		}
74
75		error!("Failed to get bridge ID for {}", self.name);
76		Err(CommandError::ExecutionError("Failed to get bridge ID".into()))
77	}
78
79	pub fn get_interfaces(&self) -> Result<Vec<String>, CommandError> {
80		debug!("Getting interfaces for Bridge: {}", self.name);
81		let details = self.show()?;
82		let mut interfaces = Vec::new();
83
84		for line in details.lines() {
85			if let Some((_, interface)) = regex_captures!(r"^\s*\S+\s+\S+\s+\S+\s+(\S+)", line) {
86				interfaces.push(interface.to_string());
87			}
88		}
89
90		Ok(interfaces)
91	}
92
93	pub fn get_stp(&self) -> Result<bool, CommandError> {
94		debug!("Checking STP state for Bridge: {}", self.name);
95		let details = self.show()?;
96
97		if let Some((_, stp_state)) = regex_captures!(r"^\s*\S+\s+\S+\s+(\S+)", details.lines().nth(1).unwrap_or("")) {
98			return Ok(match stp_state {
99				"yes" => true,
100				"no" => false,
101				_ => {
102					error!("Unknown STP state for Bridge: {}", self.name);
103					unimplemented!("Unknown STP state {}", stp_state)
104				}
105			});
106		}
107
108		error!("Failed to determine STP state for Bridge: {}", self.name);
109		Err(CommandError::ExecutionError("Failed to determine STP state".into()))
110	}
111}