mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2025-02-25 18:55:32 -06:00
Add preflight checks that interface(s) are UP. Add a check that if XDP
bridging is enabled, there is no Linux bridge containing the listed interfaces. If the interfaces are enabled, warnings are emitted and the XDP bridge disabled to operate in Linux mode.
This commit is contained in:
parent
58f898e0f0
commit
417b545dc7
@ -50,6 +50,20 @@ pub fn load_config() -> Result<Config, LibreQoSConfigError> {
|
||||
Ok(lock.as_ref().unwrap().clone())
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
/// Enables LTS reporting in the configuration file.
|
||||
pub fn enable_long_term_stats(license_key: String) -> Result<(), LibreQoSConfigError> {
|
||||
let mut config = load_config()?;
|
||||
|
@ -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,106 @@ 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) {
|
||||
if iface.operstate != "UP" {
|
||||
error!("Interface {} is not UP", stick.interface);
|
||||
anyhow::bail!("Interface {} is not UP", stick.interface);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(bridge) = &config.bridge {
|
||||
if let Some(iface) = interfaces.iter().find(|i| i.name == bridge.to_internet) {
|
||||
if iface.operstate != "UP" {
|
||||
error!("Interface {} is not UP", bridge.to_internet);
|
||||
anyhow::bail!("Interface {} is not UP", bridge.to_internet);
|
||||
}
|
||||
}
|
||||
if let Some(iface) = interfaces.iter().find(|i| i.name == bridge.to_network) {
|
||||
if iface.operstate != "UP" {
|
||||
error!("Interface {} is not UP", bridge.to_network);
|
||||
anyhow::bail!("Interface {} is not UP", bridge.to_network);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
{
|
||||
// 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. 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 +179,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