feat(platform): introduce abstractions for cross-platform interface statistics and traffic control

Decouple the rate controller from Linux-specific Netlink calls to support other operating systems. This adds a macOS backend for interface statistics and an "observe-only" mode for platforms where traffic shaping is not yet implemented.
This commit is contained in:
Nils Andreas Svee committed 2026-07-17 11:35:43 +07:00
1 parent c5d673fcb8
commit 9811592f13
10 files changed
+570 -102

No files matched your search

+38 -15
View File
@@ -11,10 +11,12 @@ mod baseliner;
mod config;
mod log;
mod metrics;
#[cfg(target_os = "linux")]
mod netlink;
mod pinger;
mod pinger_icmp;
mod pinger_icmp_ts;
mod platform;
mod ratecontroller;
mod reflector_selector;
mod time;
@@ -34,10 +36,10 @@ use std::time::{Duration, Instant};
use std::{process, thread};
use crate::config::{Config, MeasurementType};
use crate::netlink::{Netlink, Qdisc};
use crate::pinger::{InFlightProbeCache, PingListener, PingSender};
use crate::pinger_icmp::{PingerICMPEchoListener, PingerICMPEchoSender};
use crate::pinger_icmp_ts::{PingerICMPTimestampListener, PingerICMPTimestampSender};
use crate::platform::{TrafficControlBackend, traffic_control_backend};
use crate::ratecontroller::{Ratecontroller, StatsDirection};
use crate::reflector_selector::ReflectorSelector;
@@ -101,19 +103,28 @@ fn create_pinger(measurement_type: MeasurementType) -> (PingListenerBox, PingSen
}
}
fn initialize_shaper(config: &Config) -> anyhow::Result<(Qdisc, Qdisc)> {
let down = Netlink::qdisc_from_ifname(&config.download_interface)?;
let up = Netlink::qdisc_from_ifname(&config.upload_interface)?;
fn initialize_shaper<T: TrafficControlBackend>(
config: &Config,
traffic_control: &mut T,
) -> anyhow::Result<(T::Handle, T::Handle)> {
initialize_shaper_with_settle_time(config, traffic_control, Duration::from_secs(2))
}
fn initialize_shaper_with_settle_time<T: TrafficControlBackend>(
config: &Config,
traffic_control: &mut T,
settle_time: Duration,
) -> anyhow::Result<(T::Handle, T::Handle)> {
let down = traffic_control.find_shaper(&config.download_interface)?;
let up = traffic_control.find_shaper(&config.upload_interface)?;
info!(
"Setting shaper rates to minimum (D/L): {} / {}",
"Requesting minimum shaper rates (D/U): {} / {}",
config.download_min_kbits, config.upload_min_kbits,
);
Netlink::set_qdisc_rate(down, config.download_min_kbits as u64, config.dry_run)?;
Netlink::set_qdisc_rate(up, config.upload_min_kbits as u64, config.dry_run)?;
let settle_time = Duration::from_secs(2);
traffic_control.set_rate(&down, config.download_min_kbits as u64, config.dry_run)?;
traffic_control.set_rate(&up, config.upload_min_kbits as u64, config.dry_run)?;
info!(
"Sleeping for {} seconds to give the shaper a chance to control existing bloat",
@@ -163,19 +174,25 @@ fn probe_identifier() -> u16 {
(process::id() & 0xffff) as u16
}
fn restore_shaper(config: &Config, down: Qdisc, up: Qdisc) {
fn restore_shaper<T: TrafficControlBackend>(
config: &Config,
traffic_control: &mut T,
down: &T::Handle,
up: &T::Handle,
) {
info!(
"Restoring base shaper rates (D/L): {} / {}",
"Requesting restoration to base shaper rates (D/U): {} / {}",
config.download_base_kbits, config.upload_base_kbits,
);
if let Err(error) =
Netlink::set_qdisc_rate(down, config.download_base_kbits as u64, config.dry_run)
traffic_control.set_rate(down, config.download_base_kbits as u64, config.dry_run)
{
warn!("Failed to restore download shaper rate: {error}");
}
if let Err(error) = Netlink::set_qdisc_rate(up, config.upload_base_kbits as u64, config.dry_run)
if let Err(error) =
traffic_control.set_rate(up, config.upload_base_kbits as u64, config.dry_run)
{
warn!("Failed to restore upload shaper rate: {error}");
}
@@ -319,7 +336,13 @@ fn run(config: Config) -> anyhow::Result<()> {
event_metrics: event_metrics.clone(),
};
let (down_qdisc, up_qdisc) = initialize_shaper(&config)?;
let mut main_traffic_control = traffic_control_backend();
if main_traffic_control.is_observe_only() {
warn!(
"Traffic control is observe-only on this platform; requested rate changes will not be applied."
);
}
let (down_shaper, up_shaper) = initialize_shaper(&config, &mut main_traffic_control)?;
let err_tx = error_tx.clone();
let listener_peers = Arc::clone(&reflector_peers);
@@ -448,7 +471,7 @@ fn run(config: Config) -> anyhow::Result<()> {
let _ = handle.join();
}
restore_shaper(&config, down_qdisc, up_qdisc);
restore_shaper(&config, &mut main_traffic_control, &down_shaper, &up_shaper);
result
}