mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2024-11-22 08:16:25 -06:00
Merge pull request #491 from LibreQoE/preflight-bridge-check
Preflight bridge check with relaxed interface status checks
This commit is contained in:
commit
24f59e1d3d
@ -117,6 +117,24 @@ pub fn update_config(new_config: &Config) -> Result<(), LibreQoSConfigError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper function that disables the XDP bridge in the LIVE, CACHED
|
||||
/// configuration --- it does NOT save the changes to disk. This is
|
||||
/// intended for use when the XDP bridge is disabled by pre-flight
|
||||
/// because of a Linux bridge.
|
||||
pub fn disable_xdp_bridge() -> Result<(), LibreQoSConfigError> {
|
||||
let mut config = load_config()?;
|
||||
let mut lock = CONFIG.lock().unwrap();
|
||||
|
||||
if let Some(bridge) = &mut config.bridge {
|
||||
bridge.use_xdp_bridge = false;
|
||||
}
|
||||
|
||||
// Write the lock
|
||||
*lock = Some(config);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum LibreQoSConfigError {
|
||||
#[error("Unable to read /etc/lqos.conf. See other errors for details.")]
|
||||
@ -140,3 +158,4 @@ pub enum LibreQoSConfigError {
|
||||
#[error("Unable to serialize config")]
|
||||
SerializeError,
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ mod program_control;
|
||||
mod shaped_devices;
|
||||
|
||||
pub use authentication::{UserRole, WebUsers};
|
||||
pub use etc::{load_config, Config, enable_long_term_stats, Tunables, BridgeConfig, update_config};
|
||||
pub use etc::{load_config, Config, enable_long_term_stats, Tunables, BridgeConfig, update_config, disable_xdp_bridge};
|
||||
pub use network_json::{NetworkJson, NetworkJsonNode, NetworkJsonTransport};
|
||||
pub use program_control::load_libreqos;
|
||||
pub use shaped_devices::{ConfigShapedDevices, ShapedDevice};
|
||||
|
@ -1,7 +1,8 @@
|
||||
use std::path::Path;
|
||||
use std::{path::Path, process::Command};
|
||||
|
||||
use anyhow::Result;
|
||||
use log::{error, info};
|
||||
use lqos_config::Config;
|
||||
use lqos_sys::interface_name_to_index;
|
||||
|
||||
fn check_queues(interface: &str) -> Result<()> {
|
||||
@ -42,6 +43,99 @@ fn check_queues(interface: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IpLinkInterface {
|
||||
pub name: String,
|
||||
pub index: u32,
|
||||
pub operstate: String,
|
||||
pub link_type: String,
|
||||
pub master: Option<String>,
|
||||
}
|
||||
|
||||
pub fn get_interfaces_from_ip_link() -> Result<Vec<IpLinkInterface>> {
|
||||
let output = Command::new("/sbin/ip")
|
||||
.args(["-j", "link"])
|
||||
.output()?;
|
||||
let output = String::from_utf8(output.stdout)?;
|
||||
let output_json = serde_json::from_str::<serde_json::Value>(&output)?;
|
||||
|
||||
let mut interfaces = Vec::new();
|
||||
for interface in output_json.as_array().unwrap() {
|
||||
let name = interface["ifname"].as_str().unwrap().to_string();
|
||||
let index = interface["ifindex"].as_u64().unwrap() as u32;
|
||||
let operstate = interface["operstate"].as_str().unwrap().to_string();
|
||||
let link_type = interface["link_type"].as_str().unwrap().to_string();
|
||||
let master = interface["master"].as_str().map(|s| s.to_string());
|
||||
|
||||
interfaces.push(IpLinkInterface {
|
||||
name,
|
||||
index,
|
||||
operstate,
|
||||
link_type,
|
||||
master,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(interfaces)
|
||||
}
|
||||
|
||||
fn check_interface_status(config: &Config, interfaces: &[IpLinkInterface]) -> Result<()> {
|
||||
if let Some(stick) = &config.single_interface {
|
||||
if let Some(iface) = interfaces.iter().find(|i| i.name == stick.interface) {
|
||||
info!("Interface {} is in status: {}", stick.interface, iface.operstate);
|
||||
}
|
||||
} else if let Some(bridge) = &config.bridge {
|
||||
if let Some(iface) = interfaces.iter().find(|i| i.name == bridge.to_internet) {
|
||||
info!("Interface {} is in status: {}", iface.name, iface.operstate);
|
||||
}
|
||||
if let Some(iface) = interfaces.iter().find(|i| i.name == bridge.to_network) {
|
||||
info!("Interface {} is in status: {}", iface.name, iface.operstate);
|
||||
}
|
||||
} else {
|
||||
error!("You MUST have either a single interface or a bridge defined in the configuration file.");
|
||||
anyhow::bail!("You MUST have either a single interface or a bridge defined in the configuration file.");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_bridge_status(config: &Config, interfaces: &[IpLinkInterface]) -> Result<()> {
|
||||
// On a stick mode is bridge-free
|
||||
if config.on_a_stick_mode() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Is the XDP bridge enabled?
|
||||
if let Some(bridge) = &config.bridge {
|
||||
if bridge.use_xdp_bridge {
|
||||
for bridge_if in interfaces
|
||||
.iter()
|
||||
.filter(|bridge_if| bridge_if.link_type == "ether" && bridge_if.operstate == "UP")
|
||||
{
|
||||
// We found a bridge. Check member interfaces to check that it does NOT include any XDP
|
||||
// bridge members.
|
||||
let in_bridge: Vec<&IpLinkInterface> = interfaces
|
||||
.iter()
|
||||
.filter(|member_if| {
|
||||
if let Some(master) = &member_if.master {
|
||||
master == &bridge_if.name
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.filter(|member_if| member_if.name == config.internet_interface() || member_if.name == config.isp_interface())
|
||||
.collect();
|
||||
|
||||
if in_bridge.len() == 2 {
|
||||
error!("Bridge ({}) contains both the internet and ISP interfaces, and you have the xdp_bridge enabled. This is not supported.", bridge_if.name);
|
||||
anyhow::bail!("Bridge {} contains both the internet and ISP interfaces. This is not supported.", bridge_if.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Runs a series of preflight checks to ensure that the configuration is sane
|
||||
pub fn preflight_checks() -> Result<()> {
|
||||
info!("Sanity checking configuration...");
|
||||
@ -78,6 +172,19 @@ pub fn preflight_checks() -> Result<()> {
|
||||
check_queues(&config.isp_interface())?;
|
||||
}
|
||||
|
||||
// Obtain the "IP link" output
|
||||
let interfaces = get_interfaces_from_ip_link()?;
|
||||
|
||||
// Are the interfaces up?
|
||||
check_interface_status(&config, &interfaces)?;
|
||||
|
||||
// Does the bridge system make sense?
|
||||
if check_bridge_status(&config, &interfaces).is_err() {
|
||||
log::warn!("Disabling XDP bridge");
|
||||
lqos_config::disable_xdp_bridge()?;
|
||||
log::warn!("XDP bridge disabled in ACTIVE config. Please fix the configuration file.");
|
||||
};
|
||||
|
||||
info!("Sanity checks passed");
|
||||
|
||||
Ok(())
|
||||
|
Loading…
Reference in New Issue
Block a user