From 0d0b1c006b25d90bf077beb53c5d90574a166006 Mon Sep 17 00:00:00 2001 From: Crimson Date: Thu, 28 May 2026 20:04:08 +0500 Subject: [PATCH] fix(sysctl_tuning): complete fix36 production reconcile and bbr modules-load lifecycle --- traffic_core/src/config.rs | 9 ++- traffic_core/src/daemon.rs | 2 +- traffic_core/src/sysctl_tuning.rs | 114 +++++++++++++++++++++++++++++- 3 files changed, 120 insertions(+), 5 deletions(-) diff --git a/traffic_core/src/config.rs b/traffic_core/src/config.rs index bda4635..93d649e 100644 --- a/traffic_core/src/config.rs +++ b/traffic_core/src/config.rs @@ -289,6 +289,7 @@ impl Config { pub fn validate_sysctl_tuning_runtime(&self) -> anyhow::Result<()> { self.validate_config()?; + self.check_supported_host()?; if !self.sysctl_tuning.enabled { return Ok(()); @@ -742,7 +743,13 @@ swappiness = 1 let mut cfg = base_config(); cfg.sysctl_tuning.enabled = false; cfg.sysctl_tuning.tcp_bbr = "auto".into(); - assert!(cfg.validate_sysctl_tuning_runtime().is_ok()); + if let Err(e) = cfg.validate_sysctl_tuning_runtime() { + let msg = e.to_string(); + assert!( + !msg.contains("modprobe"), + "validation must not require modprobe when sysctl_tuning is disabled, got error: {msg}" + ); + } } #[test] diff --git a/traffic_core/src/daemon.rs b/traffic_core/src/daemon.rs index ea7bb51..9960b0c 100644 --- a/traffic_core/src/daemon.rs +++ b/traffic_core/src/daemon.rs @@ -28,7 +28,7 @@ pub fn run(cfg: Config) -> anyhow::Result<()> { info!("starting daemon"); let state = Arc::new(Mutex::new(LastActionState::default())); - if cfg.sysctl_tuning.enabled && cfg.sysctl_tuning.apply_on_daemon_start { + if cfg.sysctl_tuning.apply_on_daemon_start { if let Err(e) = sysctl_tuning::ensure_sysctl_tuning(&cfg) { set_err(&state, format!("startup network tuning reconcile: {e}")); append_event(&format!("startup_network_tuning error=\"{e}\"")); diff --git a/traffic_core/src/sysctl_tuning.rs b/traffic_core/src/sysctl_tuning.rs index 3947645..11b3050 100644 --- a/traffic_core/src/sysctl_tuning.rs +++ b/traffic_core/src/sysctl_tuning.rs @@ -6,6 +6,7 @@ use std::os::unix::fs::PermissionsExt; use crate::{config::Config, lock::FileLock}; const SYSCTL_CONF_PATH: &str = "/etc/sysctl.d/90-awg2-harness-vpn.conf"; +const BBR_MODULES_LOAD_PATH: &str = "/etc/modules-load.d/90-awg2-harness-bbr.conf"; const MANAGED_MARKER: &str = "# Managed by awg2-harness sysctl_tuning"; #[derive(Debug, Clone)] @@ -14,8 +15,11 @@ pub struct SysctlTuningStatus { pub apply_on_daemon_start: bool, pub tcp_bbr_mode: String, pub conf_path: String, + pub modules_load_path: String, pub persisted_present: bool, pub persisted_matches: bool, + pub modules_load_present: bool, + pub modules_load_matches: bool, pub bbr_available: Option, pub bbr_active: Option, pub current: Vec, @@ -38,6 +42,7 @@ pub struct SysctlValue { struct RenderPlan { desired: BTreeMap, persisted: String, + modules_load_persisted: Option, bbr_available: Option, bbr_active: Option, warnings: Vec, @@ -45,7 +50,7 @@ struct RenderPlan { pub fn ensure_sysctl_tuning(cfg: &Config) -> anyhow::Result<()> { if !cfg.sysctl_tuning.enabled { - remove_managed_sysctl_file_if_disabled(cfg)?; + remove_managed_sysctl_files_if_disabled(cfg)?; return Ok(()); } @@ -58,6 +63,9 @@ pub fn ensure_sysctl_tuning(cfg: &Config) -> anyhow::Result<()> { for w in &plan.warnings { tracing::warn!(target: "sysctl_tuning", "{w}"); } + + reconcile_modules_load_file(&plan)?; + ensure_managed_or_absent(SYSCTL_CONF_PATH)?; write_atomic(SYSCTL_CONF_PATH, &plan.persisted)?; @@ -97,8 +105,11 @@ pub fn inspect_sysctl_tuning(cfg: &Config) -> anyhow::Result apply_on_daemon_start: cfg.sysctl_tuning.apply_on_daemon_start, tcp_bbr_mode: cfg.sysctl_tuning.tcp_bbr.clone(), conf_path: SYSCTL_CONF_PATH.to_string(), + modules_load_path: BBR_MODULES_LOAD_PATH.to_string(), persisted_present: Path::new(SYSCTL_CONF_PATH).exists(), persisted_matches: false, + modules_load_present: Path::new(BBR_MODULES_LOAD_PATH).exists(), + modules_load_matches: false, bbr_available: None, bbr_active: None, current: Vec::new(), @@ -114,6 +125,14 @@ pub fn inspect_sysctl_tuning(cfg: &Config) -> anyhow::Result .as_deref() .map(|v| normalize(v) == normalize(&plan.persisted)) .unwrap_or(false); + let modules_load_raw = fs::read_to_string(BBR_MODULES_LOAD_PATH).ok(); + let modules_load_present = modules_load_raw.is_some(); + let modules_load_expected = plan.modules_load_persisted.as_deref(); + let modules_load_matches = match (modules_load_raw.as_deref(), modules_load_expected) { + (Some(actual), Some(expected)) => normalize(actual) == normalize(expected), + (None, None) => true, + _ => false, + }; let mut values = Vec::::new(); for (k, v) in &plan.desired { @@ -131,6 +150,9 @@ pub fn inspect_sysctl_tuning(cfg: &Config) -> anyhow::Result drift_reason = Some("managed sysctl file is missing".to_string()); } else if !persisted_matches { drift_reason = Some("persisted sysctl file differs from rendered config".to_string()); + } else if !modules_load_matches { + drift_reason = + Some("persisted modules-load file differs from rendered config".to_string()); } else if let Some(v) = values .iter() .find(|v| v.desired.as_deref() != v.current.as_deref()) @@ -140,6 +162,35 @@ pub fn inspect_sysctl_tuning(cfg: &Config) -> anyhow::Result v.key, v.current, v.desired )); } + } else if persisted_present { + let managed = persisted_raw + .as_deref() + .and_then(|raw| raw.lines().next()) + .map(|first| first.contains(MANAGED_MARKER)) + .unwrap_or(false); + + if managed { + drift_reason = + Some("managed sysctl file exists while sysctl_tuning is disabled".to_string()); + } else { + drift_reason = + Some("unmanaged sysctl file exists at awg2-harness managed path".to_string()); + } + } else if modules_load_present { + let managed = modules_load_raw + .as_deref() + .and_then(|raw| raw.lines().next()) + .map(|first| first.contains(MANAGED_MARKER)) + .unwrap_or(false); + + if managed { + drift_reason = Some( + "managed modules-load file exists while sysctl_tuning is disabled".to_string(), + ); + } else { + drift_reason = + Some("unmanaged modules-load file exists at awg2-harness managed path".to_string()); + } } Ok(SysctlTuningStatus { @@ -147,8 +198,11 @@ pub fn inspect_sysctl_tuning(cfg: &Config) -> anyhow::Result apply_on_daemon_start: cfg.sysctl_tuning.apply_on_daemon_start, tcp_bbr_mode: cfg.sysctl_tuning.tcp_bbr.clone(), conf_path: SYSCTL_CONF_PATH.to_string(), + modules_load_path: BBR_MODULES_LOAD_PATH.to_string(), persisted_present, persisted_matches, + modules_load_present, + modules_load_matches, bbr_available: plan.bbr_available, bbr_active: plan.bbr_active, current: values.clone(), @@ -164,6 +218,7 @@ pub fn print_sysctl_tuning_status(status: &SysctlTuningStatus) { println!(" apply_on_daemon_start: {}", status.apply_on_daemon_start); println!(" tcp_bbr_mode: {}", status.tcp_bbr_mode); println!(" conf_path: {}", status.conf_path); + println!(" modules_load_path: {}", status.modules_load_path); println!( " persisted_present: {}", if status.persisted_present { @@ -180,6 +235,22 @@ pub fn print_sysctl_tuning_status(status: &SysctlTuningStatus) { "no" } ); + println!( + " modules_load_present: {}", + if status.modules_load_present { + "yes" + } else { + "no" + } + ); + println!( + " modules_load_matches: {}", + if status.modules_load_matches { + "yes" + } else { + "no" + } + ); if let Some(v) = status.bbr_available { println!(" bbr_available: {}", if v { "yes" } else { "no" }); } @@ -207,12 +278,23 @@ pub fn print_sysctl_tuning_status(status: &SysctlTuningStatus) { } } -pub fn remove_managed_sysctl_file_if_disabled(cfg: &Config) -> anyhow::Result<()> { +pub fn remove_managed_sysctl_files_if_disabled(cfg: &Config) -> anyhow::Result<()> { if cfg.sysctl_tuning.enabled { return Ok(()); } - remove_managed_sysctl_file_at(Path::new(SYSCTL_CONF_PATH)) + remove_managed_sysctl_file_at(Path::new(SYSCTL_CONF_PATH))?; + remove_managed_sysctl_file_at(Path::new(BBR_MODULES_LOAD_PATH)) +} + +fn reconcile_modules_load_file(plan: &RenderPlan) -> anyhow::Result<()> { + if let Some(content) = &plan.modules_load_persisted { + ensure_managed_or_absent(BBR_MODULES_LOAD_PATH)?; + write_atomic(BBR_MODULES_LOAD_PATH, content)?; + } else { + remove_managed_sysctl_file_at(Path::new(BBR_MODULES_LOAD_PATH))?; + } + Ok(()) } fn remove_managed_sysctl_file_at(path: &Path) -> anyhow::Result<()> { @@ -256,6 +338,7 @@ fn build_render_plan(cfg: &Config, allow_modprobe: bool) -> anyhow::Result anyhow::Result) -> String { out } +fn render_modules_load_persisted(module: &str) -> String { + let mut out = String::new(); + out.push_str(MANAGED_MARKER); + out.push('\n'); + out.push_str("# Source: /etc/awg2-harness/config.toml\n"); + out.push_str("# Do not edit this file manually; use [sysctl_tuning].\n\n"); + out.push_str(module); + out.push('\n'); + out +} + fn read_sysctl(key: &str) -> anyhow::Result { let out = Command::new("sysctl").args(["-n", key]).output()?; if !out.status.success() { @@ -494,6 +594,13 @@ mod tests { assert!(!path.exists()); } + #[test] + fn render_modules_load_contains_marker_and_module() { + let s = render_modules_load_persisted("tcp_bbr"); + assert!(s.starts_with(MANAGED_MARKER)); + assert!(s.contains("tcp_bbr")); + } + #[test] fn ensure_refuses_overwrite_unmanaged_file() { let path = std::env::temp_dir().join(format!( @@ -544,6 +651,7 @@ mod tests { let plan = build_render_plan(&c, false).expect("render plan"); assert!(!plan.desired.contains_key("net.ipv4.tcp_congestion_control")); assert!(!plan.desired.contains_key("net.core.default_qdisc")); + assert_eq!(plan.modules_load_persisted, None); assert_eq!(plan.bbr_available, None); assert_eq!(plan.bbr_active, None); }