Use proper error handling instead of lurking panics everywhere
This commit is contained in:
1 parent
999987d157
commit
214ffef6f3
11 files changed
+349
-392
No files matched your search
+6
-3
@@ -1,5 +1,6 @@
|
|||||||
use crate::pinger::PingReply;
|
use crate::pinger::PingReply;
|
||||||
use crate::{Config, Utils};
|
use crate::utils::Utils;
|
||||||
|
use crate::Config;
|
||||||
use log::info;
|
use log::info;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
@@ -22,7 +23,7 @@ pub struct Baseliner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Baseliner {
|
impl Baseliner {
|
||||||
pub fn run(&self) {
|
pub fn run(&self) -> anyhow::Result<()> {
|
||||||
/*
|
/*
|
||||||
* 135 seconds to decay to 50% for the slow factor and
|
* 135 seconds to decay to 50% for the slow factor and
|
||||||
* 0.4 seconds to decay to 50% for the fast factor.
|
* 0.4 seconds to decay to 50% for the fast factor.
|
||||||
@@ -35,7 +36,7 @@ impl Baseliner {
|
|||||||
let fast_factor = Utils::ewma_factor(self.config.tick_interval, 0.4);
|
let fast_factor = Utils::ewma_factor(self.config.tick_interval, 0.4);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let time_data = self.stats_receiver.recv().unwrap();
|
let time_data = self.stats_receiver.recv()?;
|
||||||
|
|
||||||
let mut owd_baseline_map = self.owd_baseline.lock().unwrap();
|
let mut owd_baseline_map = self.owd_baseline.lock().unwrap();
|
||||||
let mut owd_recent_map = self.owd_recent.lock().unwrap();
|
let mut owd_recent_map = self.owd_recent.lock().unwrap();
|
||||||
@@ -84,6 +85,8 @@ impl Baseliner {
|
|||||||
"Reflector {} has OWD > 5 seconds more than baseline, triggering reselection",
|
"Reflector {} has OWD > 5 seconds more than baseline, triggering reselection",
|
||||||
time_data.reflector
|
time_data.reflector
|
||||||
);
|
);
|
||||||
|
// If reselection is disabled this would trigger an error
|
||||||
|
// so just ignore the result
|
||||||
let _ = self.reselect_trigger.send(true);
|
let _ = self.reselect_trigger.send(true);
|
||||||
} else {
|
} else {
|
||||||
owd_baseline.down_ewma = owd_baseline.down_ewma * slow_factor
|
owd_baseline.down_ewma = owd_baseline.down_ewma * slow_factor
|
||||||
|
|||||||
+20
-15
@@ -1,10 +1,9 @@
|
|||||||
use crate::error::{ConfigParseError, InvalidMeasurementTypeError, MissingConfigError};
|
use anyhow::Result;
|
||||||
#[cfg(feature = "uci")]
|
#[cfg(feature = "uci")]
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use log::Level;
|
use log::Level;
|
||||||
#[cfg(feature = "uci")]
|
#[cfg(feature = "uci")]
|
||||||
use rust_uci::Uci;
|
use rust_uci::Uci;
|
||||||
use std::error::Error;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::BufRead;
|
use std::io::BufRead;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
@@ -12,6 +11,18 @@ use std::path::Path;
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::{env, io};
|
use std::{env, io};
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum ConfigError {
|
||||||
|
#[error("Invalid measurement type")]
|
||||||
|
InvalidMeasurementType(String),
|
||||||
|
#[error("Couldn't parse value for key: `{0}`: invalid value")]
|
||||||
|
ParseError(String),
|
||||||
|
#[error("No config value found for key: `{0}`")]
|
||||||
|
MissingValue(String),
|
||||||
|
}
|
||||||
|
|
||||||
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
|
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
@@ -29,7 +40,7 @@ pub enum MeasurementType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for MeasurementType {
|
impl FromStr for MeasurementType {
|
||||||
type Err = InvalidMeasurementTypeError;
|
type Err = ConfigError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
return match s.to_lowercase().as_str() {
|
return match s.to_lowercase().as_str() {
|
||||||
@@ -37,9 +48,7 @@ impl FromStr for MeasurementType {
|
|||||||
"icmp-timestamps" => Ok(MeasurementType::ICMPTimestamps),
|
"icmp-timestamps" => Ok(MeasurementType::ICMPTimestamps),
|
||||||
"ntp" => Ok(MeasurementType::NTP),
|
"ntp" => Ok(MeasurementType::NTP),
|
||||||
"tcp-timestamps" => Ok(MeasurementType::TCPTimestamps),
|
"tcp-timestamps" => Ok(MeasurementType::TCPTimestamps),
|
||||||
&_ => Err(InvalidMeasurementTypeError {
|
&_ => Err(ConfigError::InvalidMeasurementType(s.to_string())),
|
||||||
type_: s.to_string(),
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,7 +82,7 @@ pub(crate) struct Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn new() -> Result<Self, Box<dyn Error>> {
|
pub fn new() -> Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
// Network section
|
// Network section
|
||||||
download_base_kbits: Self::get::<f64>(
|
download_base_kbits: Self::get::<f64>(
|
||||||
@@ -180,22 +189,18 @@ impl Config {
|
|||||||
env_key: &str,
|
env_key: &str,
|
||||||
uci_key: &str,
|
uci_key: &str,
|
||||||
default: Option<T>,
|
default: Option<T>,
|
||||||
) -> Result<T, Box<dyn Error>> {
|
) -> Result<T, ConfigError> {
|
||||||
return match Self::get_value(env_key, uci_key) {
|
return match Self::get_value(env_key, uci_key) {
|
||||||
Some(val) => match val.parse::<T>() {
|
Some(val) => match val.parse::<T>() {
|
||||||
Ok(parsed_val) => Ok(parsed_val),
|
Ok(parsed_val) => Ok(parsed_val),
|
||||||
// Ran into an compilation error while trying to return the
|
// Ran into an compilation error while trying to return the
|
||||||
// error as-is, so using my own error type to indicate something went wrong while parsing
|
// error as-is, so using my own error type to indicate something went wrong while parsing
|
||||||
Err(_) => Err(Box::new(ConfigParseError {
|
Err(_) => Err(ConfigError::ParseError(env_key.to_string())),
|
||||||
config_key: env_key.to_string(),
|
|
||||||
})),
|
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
return match default {
|
return match default {
|
||||||
Some(val) => Ok(val),
|
Some(val) => Ok(val),
|
||||||
None => Err(Box::new(MissingConfigError {
|
None => Err(ConfigError::MissingValue(env_key.to_string())),
|
||||||
config_key: env_key.to_string(),
|
|
||||||
})),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -237,7 +242,7 @@ impl Config {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_reflectors(&self) -> Result<Vec<IpAddr>, Box<dyn Error>> {
|
pub fn load_reflectors(&self) -> Result<Vec<IpAddr>> {
|
||||||
let lines = read_lines(self.reflector_list_file.clone())?;
|
let lines = read_lines(self.reflector_list_file.clone())?;
|
||||||
|
|
||||||
let mut reflectors: Vec<IpAddr> = Vec::with_capacity(50);
|
let mut reflectors: Vec<IpAddr> = Vec::with_capacity(50);
|
||||||
|
|||||||
@@ -1,98 +0,0 @@
|
|||||||
use std::error::Error;
|
|
||||||
use std::fmt;
|
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct ConfigParseError {
|
|
||||||
pub(crate) config_key: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for ConfigParseError {
|
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement std::fmt::Display for AppError
|
|
||||||
impl Display for ConfigParseError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"Couldn't parse value from string for key: {}",
|
|
||||||
self.config_key
|
|
||||||
) // user-facing output
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct InvalidMeasurementTypeError {
|
|
||||||
pub(crate) type_: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for InvalidMeasurementTypeError {
|
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for InvalidMeasurementTypeError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "Couldn't find measurement type: {}", self.type_)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct MissingConfigError {
|
|
||||||
pub(crate) config_key: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for MissingConfigError {
|
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement std::fmt::Display for AppError
|
|
||||||
impl Display for MissingConfigError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"Couldn't find required configuration option: {}",
|
|
||||||
self.config_key
|
|
||||||
) // user-facing output
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct NoQdiscFoundError;
|
|
||||||
|
|
||||||
impl Error for NoQdiscFoundError {
|
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement std::fmt::Display for AppError
|
|
||||||
impl Display for NoQdiscFoundError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "Couldn't find a CAKE qdisc for interface") // user-facing output
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct PingParseError {
|
|
||||||
pub(crate) msg: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for PingParseError {
|
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement std::fmt::Display for AppError
|
|
||||||
impl Display for PingParseError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "Couldn't parse received ping packet: {}", self.msg) // user-facing output
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+42
-91
@@ -3,7 +3,6 @@ extern crate core;
|
|||||||
mod baseliner;
|
mod baseliner;
|
||||||
mod cake;
|
mod cake;
|
||||||
mod config;
|
mod config;
|
||||||
mod error;
|
|
||||||
mod log;
|
mod log;
|
||||||
mod netlink;
|
mod netlink;
|
||||||
mod pinger;
|
mod pinger;
|
||||||
@@ -14,16 +13,15 @@ mod reflector_selector;
|
|||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use crate::baseliner::{Baseliner, ReflectorStats};
|
use crate::baseliner::{Baseliner, ReflectorStats};
|
||||||
use ::log::{debug, error, info};
|
use ::log::debug;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
use std::process::ExitCode;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{panic, process, thread};
|
use std::{process, thread};
|
||||||
|
|
||||||
use crate::config::{Config, MeasurementType};
|
use crate::config::{Config, MeasurementType};
|
||||||
use crate::netlink::Netlink;
|
use crate::netlink::Netlink;
|
||||||
@@ -32,33 +30,15 @@ use crate::pinger_icmp::{PingerICMPEchoListener, PingerICMPEchoSender};
|
|||||||
use crate::pinger_icmp_ts::{PingerICMPTimestampListener, PingerICMPTimestampSender};
|
use crate::pinger_icmp_ts::{PingerICMPTimestampListener, PingerICMPTimestampSender};
|
||||||
use crate::ratecontroller::{Ratecontroller, StatsDirection};
|
use crate::ratecontroller::{Ratecontroller, StatsDirection};
|
||||||
use crate::reflector_selector::ReflectorSelector;
|
use crate::reflector_selector::ReflectorSelector;
|
||||||
use crate::utils::Utils;
|
|
||||||
|
|
||||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> anyhow::Result<()> {
|
||||||
println!("Starting sqm-autorate version {}", VERSION);
|
println!("Starting sqm-autorate version {}", VERSION);
|
||||||
|
|
||||||
let config = match Config::new() {
|
let config = Config::new()?;
|
||||||
Ok(val) => val,
|
log::init(config.log_level)?;
|
||||||
Err(e) => {
|
let mut reflectors = config.load_reflectors()?;
|
||||||
println!(
|
|
||||||
"Something went wrong while getting config: {}",
|
|
||||||
e.to_string()
|
|
||||||
);
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
log::init(config.log_level).expect("Couldn't initialize logger");
|
|
||||||
|
|
||||||
let mut reflectors = match config.load_reflectors() {
|
|
||||||
Ok(refl) => refl,
|
|
||||||
Err(e) => {
|
|
||||||
info!("Couldn't load reflector list: {}", e.to_string());
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// The identifier field in ICMP is only 2 bytes
|
// The identifier field in ICMP is only 2 bytes
|
||||||
// so take the last 2 bytes of the PID as the ID
|
// so take the last 2 bytes of the PID as the ID
|
||||||
@@ -72,12 +52,12 @@ fn main() -> ExitCode {
|
|||||||
let reflector_pool_size = reflectors.len();
|
let reflector_pool_size = reflectors.len();
|
||||||
|
|
||||||
let default_reflectors = [
|
let default_reflectors = [
|
||||||
IpAddr::from_str("9.9.9.9").unwrap(),
|
IpAddr::from_str("9.9.9.9")?,
|
||||||
IpAddr::from_str("8.238.120.14").unwrap(),
|
IpAddr::from_str("8.238.120.14")?,
|
||||||
IpAddr::from_str("74.82.42.42").unwrap(),
|
IpAddr::from_str("74.82.42.42")?,
|
||||||
IpAddr::from_str("194.242.2.2").unwrap(),
|
IpAddr::from_str("194.242.2.2")?,
|
||||||
IpAddr::from_str("208.67.222.222").unwrap(),
|
IpAddr::from_str("208.67.222.222")?,
|
||||||
IpAddr::from_str("94.140.14.14").unwrap(),
|
IpAddr::from_str("94.140.14.14")?,
|
||||||
];
|
];
|
||||||
|
|
||||||
match reflector_pool_size > 5 {
|
match reflector_pool_size > 5 {
|
||||||
@@ -117,66 +97,37 @@ fn main() -> ExitCode {
|
|||||||
stats_receiver: baseliner_stats_receiver,
|
stats_receiver: baseliner_stats_receiver,
|
||||||
};
|
};
|
||||||
|
|
||||||
let down_qdisc = match Netlink::qdisc_from_ifname(config.download_interface.as_str()) {
|
let down_qdisc = Netlink::qdisc_from_ifname(config.download_interface.as_str())?;
|
||||||
Ok(qdisc) => qdisc,
|
let up_qdisc = Netlink::qdisc_from_ifname(config.upload_interface.as_str())?;
|
||||||
Err(e) => {
|
|
||||||
error!("Couldn't find download qdisc: {}", e);
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let up_qdisc = match Netlink::qdisc_from_ifname(config.upload_interface.as_str()) {
|
|
||||||
Ok(qdisc) => qdisc,
|
|
||||||
Err(e) => {
|
|
||||||
error!("Couldn't find upload qdisc: {}", e);
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Set initial TC values to minimum
|
/* Set initial TC values to minimum
|
||||||
* so there should be no initial bufferbloat to
|
* so there should be no initial bufferbloat to
|
||||||
* fool the baseliner
|
* fool the baseliner
|
||||||
*/
|
*/
|
||||||
Netlink::set_qdisc_rate(down_qdisc, config.download_min_kbits as u64)
|
Netlink::set_qdisc_rate(down_qdisc, config.download_min_kbits as u64)?;
|
||||||
.expect("Couldn't set ingress bandwidth");
|
Netlink::set_qdisc_rate(up_qdisc, config.upload_min_kbits as u64)?;
|
||||||
Netlink::set_qdisc_rate(up_qdisc, config.upload_min_kbits as u64)
|
|
||||||
.expect("Couldn't set egress bandwidth");
|
|
||||||
sleep(Duration::new(0, 5e8 as u32));
|
sleep(Duration::new(0, 5e8 as u32));
|
||||||
|
|
||||||
let reflector_peers_lock_clone = reflector_peers_lock.clone();
|
let reflector_peers_lock_clone = reflector_peers_lock.clone();
|
||||||
let receiver_handle = thread::Builder::new()
|
let receiver_handle = thread::Builder::new().name("receiver".to_string()).spawn(
|
||||||
.name("receiver".to_string())
|
move || -> anyhow::Result<()> {
|
||||||
.spawn(move || {
|
pinger_receiver.listen(
|
||||||
match pinger_receiver.listen(
|
|
||||||
id,
|
id,
|
||||||
config.measurement_type,
|
config.measurement_type,
|
||||||
reflector_peers_lock_clone,
|
reflector_peers_lock_clone,
|
||||||
baseliner_stats_sender,
|
baseliner_stats_sender,
|
||||||
) {
|
)
|
||||||
Ok(_) => {}
|
},
|
||||||
Err(e) => {
|
)?;
|
||||||
error!("Error occured in receiver thread: {}", e);
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.expect("Couldn't spawn ping receiver thread");
|
|
||||||
let baseliner_handle = thread::Builder::new()
|
let baseliner_handle = thread::Builder::new()
|
||||||
.name("baseliner".to_string())
|
.name("baseliner".to_string())
|
||||||
.spawn(move || baseliner.run())
|
.spawn(move || -> anyhow::Result<()> { baseliner.run() })?;
|
||||||
.expect("Couldn't spawn baseliner thread");
|
|
||||||
let reflector_peers_lock_clone = reflector_peers_lock.clone();
|
let reflector_peers_lock_clone = reflector_peers_lock.clone();
|
||||||
let sender_handle = thread::Builder::new()
|
let sender_handle = thread::Builder::new().name("sender".to_string()).spawn(
|
||||||
.name("sender".to_string())
|
move || -> anyhow::Result<()> {
|
||||||
.spawn(move || {
|
pinger_sender.send(id, config.measurement_type, reflector_peers_lock_clone)
|
||||||
match pinger_sender.send(id, config.measurement_type, reflector_peers_lock_clone) {
|
},
|
||||||
Ok(_) => {}
|
)?;
|
||||||
Err(e) => {
|
|
||||||
error!("Error occured in sender thread: {}", e);
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.expect("Couldn't spawn ping sender thread");
|
|
||||||
|
|
||||||
let mut threads = vec![receiver_handle, sender_handle, baseliner_handle];
|
let mut threads = vec![receiver_handle, sender_handle, baseliner_handle];
|
||||||
|
|
||||||
@@ -190,22 +141,13 @@ fn main() -> ExitCode {
|
|||||||
};
|
};
|
||||||
let reselection_handle = thread::Builder::new()
|
let reselection_handle = thread::Builder::new()
|
||||||
.name("reselection".to_string())
|
.name("reselection".to_string())
|
||||||
.spawn(move || reflector_selector.run())
|
.spawn(move || reflector_selector.run())?;
|
||||||
.expect("Couldn't spawn reflector selector thread");
|
|
||||||
threads.push(reselection_handle);
|
threads.push(reselection_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sleep 10 seconds before we start adjusting speeds
|
// Sleep 10 seconds before we start adjusting speeds
|
||||||
sleep(Duration::new(10, 0));
|
sleep(Duration::new(10, 0));
|
||||||
|
|
||||||
let ratecontroller = Ratecontroller {
|
|
||||||
config: config.clone(),
|
|
||||||
owd_baseline: owd_baseline.clone(),
|
|
||||||
owd_recent: owd_recent.clone(),
|
|
||||||
reflectors_lock: reflector_peers_lock.clone(),
|
|
||||||
reselect_trigger: reselect_sender.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let dl_direction;
|
let dl_direction;
|
||||||
let ul_direction;
|
let ul_direction;
|
||||||
|
|
||||||
@@ -224,6 +166,16 @@ fn main() -> ExitCode {
|
|||||||
ul_direction = StatsDirection::TX;
|
ul_direction = StatsDirection::TX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let ratecontroller = Ratecontroller::new(
|
||||||
|
config.clone(),
|
||||||
|
owd_baseline.clone(),
|
||||||
|
owd_recent.clone(),
|
||||||
|
reflector_peers_lock.clone(),
|
||||||
|
reselect_sender.clone(),
|
||||||
|
dl_direction,
|
||||||
|
ul_direction,
|
||||||
|
)?;
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"Download direction: {}:{:?}",
|
"Download direction: {}:{:?}",
|
||||||
config.download_interface, dl_direction
|
config.download_interface, dl_direction
|
||||||
@@ -236,14 +188,13 @@ fn main() -> ExitCode {
|
|||||||
|
|
||||||
let ratecontroller_handle = thread::Builder::new()
|
let ratecontroller_handle = thread::Builder::new()
|
||||||
.name("ratecontroller".to_string())
|
.name("ratecontroller".to_string())
|
||||||
.spawn(move || ratecontroller.run(dl_direction, ul_direction))
|
.spawn(move || ratecontroller.run(dl_direction, ul_direction))?;
|
||||||
.expect("Couldn't spawn ratecontroller thread");
|
|
||||||
|
|
||||||
threads.push(ratecontroller_handle);
|
threads.push(ratecontroller_handle);
|
||||||
|
|
||||||
for thread in threads {
|
for thread in threads {
|
||||||
thread.join().unwrap();
|
thread.join().expect("Error happened in thread")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExitCode::SUCCESS
|
Ok(())
|
||||||
}
|
}
|
||||||
+80
-39
@@ -1,19 +1,49 @@
|
|||||||
use crate::cake::TcaCake;
|
use crate::cake::TcaCake;
|
||||||
use crate::error::NoQdiscFoundError;
|
|
||||||
|
|
||||||
use neli::consts::nl::{NlmF, NlmFFlags};
|
use neli::consts::nl::{NlmF, NlmFFlags};
|
||||||
use neli::consts::rtnl::{Arphrd, IffFlags, Ifla, RtAddrFamily, Rtm, Tca};
|
use neli::consts::rtnl::{Arphrd, IffFlags, Ifla, RtAddrFamily, Rtm, Tca};
|
||||||
use neli::consts::socket::NlFamily;
|
use neli::consts::socket::NlFamily;
|
||||||
use neli::err::NlError;
|
use neli::err::{NlError, SerError};
|
||||||
use neli::nl::{NlPayload, Nlmsghdr};
|
use neli::nl::{NlPayload, Nlmsghdr};
|
||||||
use neli::rtnl::{Ifinfomsg, Rtattr, Tcmsg};
|
use neli::rtnl::{Ifinfomsg, Rtattr, Tcmsg};
|
||||||
use neli::socket::NlSocketHandle;
|
use neli::socket::NlSocketHandle;
|
||||||
use neli::types::{Buffer, RtBuffer};
|
use neli::types::{Buffer, RtBuffer};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::error::Error;
|
use std::io;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
use bincode::deserialize;
|
use bincode::deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum NetlinkError {
|
||||||
|
#[error("Couldn't deserialize to struct")]
|
||||||
|
Deserialization(#[from] Box<bincode::ErrorKind>),
|
||||||
|
|
||||||
|
#[error("Couldn't find intreface `{0}`")]
|
||||||
|
InterfaceNotFound(String),
|
||||||
|
|
||||||
|
#[error("Netlink interface error")]
|
||||||
|
NlInterfaceError(#[from] NlError<Rtm, Ifinfomsg>),
|
||||||
|
|
||||||
|
#[error("Netlink interface error")]
|
||||||
|
NlQdiscError(#[from] NlError<Rtm, Tcmsg>),
|
||||||
|
|
||||||
|
#[error("Couldn't find CAKE qdisc on interface `{0}`")]
|
||||||
|
NoQdiscFound(String),
|
||||||
|
|
||||||
|
#[error("Couldn't find interface statistics: `{0}`")]
|
||||||
|
NoInterfaceStatsFound(String),
|
||||||
|
|
||||||
|
#[error("Couldn't open Netlink socket")]
|
||||||
|
OpenSocket(#[from] io::Error),
|
||||||
|
|
||||||
|
#[error("Serialization error")]
|
||||||
|
Serialization(#[from] SerError),
|
||||||
|
|
||||||
|
#[error("Invalid Rtm type (expected {expected:?}, found {found:?})")]
|
||||||
|
WrongType { expected: Rtm, found: Rtm },
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Copy, Clone, Default, Debug)]
|
#[derive(Deserialize, Copy, Clone, Default, Debug)]
|
||||||
pub struct RtnlLinkStats64 {
|
pub struct RtnlLinkStats64 {
|
||||||
pub rx_packets: u64,
|
pub rx_packets: u64,
|
||||||
@@ -43,7 +73,7 @@ pub struct RtnlLinkStats64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct Qdisc {
|
pub struct Qdisc {
|
||||||
ifindex: i32,
|
ifindex: i32,
|
||||||
handle: u32,
|
handle: u32,
|
||||||
@@ -55,13 +85,13 @@ pub struct Qdisc {
|
|||||||
pub struct Netlink {}
|
pub struct Netlink {}
|
||||||
|
|
||||||
impl Netlink {
|
impl Netlink {
|
||||||
fn nl_interface_get(socket: &mut NlSocketHandle, ifname: &str) -> Result<(), Box<dyn Error>> {
|
fn nl_interface_get(socket: &mut NlSocketHandle, ifname: &str) -> Result<(), NetlinkError> {
|
||||||
let mut attrs = RtBuffer::new();
|
let mut attrs = RtBuffer::new();
|
||||||
|
|
||||||
const RTEXT_FILTER_VF: i32 = 1 << 0;
|
const RTEXT_FILTER_VF: i32 = 1 << 0;
|
||||||
|
|
||||||
let attr_ifname = Rtattr::new(None, Ifla::Ifname, ifname).unwrap();
|
let attr_ifname = Rtattr::new(None, Ifla::Ifname, ifname)?;
|
||||||
let attr_ext_mask = Rtattr::new(None, Ifla::ExtMask, RTEXT_FILTER_VF).unwrap();
|
let attr_ext_mask = Rtattr::new(None, Ifla::ExtMask, RTEXT_FILTER_VF)?;
|
||||||
attrs.push(attr_ifname);
|
attrs.push(attr_ifname);
|
||||||
attrs.push(attr_ext_mask);
|
attrs.push(attr_ext_mask);
|
||||||
|
|
||||||
@@ -88,28 +118,7 @@ impl Netlink {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_interface(ifname: &str) -> Result<i32, Box<dyn Error>> {
|
pub fn find_interface(ifname: &str) -> Result<i32, NetlinkError> {
|
||||||
let mut socket = NlSocketHandle::connect(NlFamily::Route, None, &[]).unwrap();
|
|
||||||
|
|
||||||
Self::nl_interface_get(&mut socket, ifname).expect("Wrong when sending intf");
|
|
||||||
|
|
||||||
for response in socket.iter(false) {
|
|
||||||
let header: Nlmsghdr<Rtm, Ifinfomsg> = response?;
|
|
||||||
|
|
||||||
if header.nl_type != Rtm::Newlink {
|
|
||||||
return Err(Box::new(NlError::msg("Netlink error retrieving link")));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let NlPayload::Payload(p) = header.nl_payload {
|
|
||||||
return Ok(p.ifi_index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// we shouldn't reach here
|
|
||||||
Err(Box::new(NlError::msg("Netlink error retrieving link")))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_interface_stats(ifname: &str) -> Result<RtnlLinkStats64, Box<dyn Error>> {
|
|
||||||
let mut socket = NlSocketHandle::connect(NlFamily::Route, None, &[]).unwrap();
|
let mut socket = NlSocketHandle::connect(NlFamily::Route, None, &[]).unwrap();
|
||||||
|
|
||||||
Self::nl_interface_get(&mut socket, ifname)?;
|
Self::nl_interface_get(&mut socket, ifname)?;
|
||||||
@@ -118,7 +127,34 @@ impl Netlink {
|
|||||||
let header: Nlmsghdr<Rtm, Ifinfomsg> = response?;
|
let header: Nlmsghdr<Rtm, Ifinfomsg> = response?;
|
||||||
|
|
||||||
if header.nl_type != Rtm::Newlink {
|
if header.nl_type != Rtm::Newlink {
|
||||||
return Err(Box::new(NlError::msg("Netlink error retrieving link")));
|
return Err(NetlinkError::WrongType {
|
||||||
|
expected: Rtm::Newlink,
|
||||||
|
found: header.nl_type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let NlPayload::Payload(p) = header.nl_payload {
|
||||||
|
return Ok(p.ifi_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we shouldn't reach here
|
||||||
|
Err(NetlinkError::InterfaceNotFound(ifname.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_interface_stats(ifname: &str) -> Result<RtnlLinkStats64, NetlinkError> {
|
||||||
|
let mut socket = NlSocketHandle::connect(NlFamily::Route, None, &[])?;
|
||||||
|
|
||||||
|
Self::nl_interface_get(&mut socket, ifname)?;
|
||||||
|
|
||||||
|
for response in socket.iter(false) {
|
||||||
|
let header: Nlmsghdr<Rtm, Ifinfomsg> = response?;
|
||||||
|
|
||||||
|
if header.nl_type != Rtm::Newlink {
|
||||||
|
return Err(NetlinkError::WrongType {
|
||||||
|
expected: Rtm::Newlink,
|
||||||
|
found: header.nl_type,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if let NlPayload::Payload(p) = header.nl_payload {
|
if let NlPayload::Payload(p) = header.nl_payload {
|
||||||
@@ -126,7 +162,7 @@ impl Netlink {
|
|||||||
if attr.rta_type == Ifla::Stats64 {
|
if attr.rta_type == Ifla::Stats64 {
|
||||||
let buf = attr.rta_payload.as_ref();
|
let buf = attr.rta_payload.as_ref();
|
||||||
|
|
||||||
let stats: RtnlLinkStats64 = deserialize(buf).unwrap();
|
let stats: RtnlLinkStats64 = deserialize(buf)?;
|
||||||
|
|
||||||
return Ok(stats);
|
return Ok(stats);
|
||||||
}
|
}
|
||||||
@@ -134,11 +170,11 @@ impl Netlink {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(Box::new(NlError::msg("Error getting stats")))
|
Err(NetlinkError::NoInterfaceStatsFound(ifname.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn qdisc_from_ifindex(ifindex: i32) -> Result<Qdisc, Box<dyn Error>> {
|
pub fn qdisc_from_ifindex(ifindex: i32) -> Result<Qdisc, NetlinkError> {
|
||||||
let mut socket = NlSocketHandle::connect(NlFamily::Route, None, &[]).unwrap();
|
let mut socket = NlSocketHandle::connect(NlFamily::Route, None, &[])?;
|
||||||
let tc_msg = Tcmsg::new(
|
let tc_msg = Tcmsg::new(
|
||||||
u8::from(RtAddrFamily::Unspecified),
|
u8::from(RtAddrFamily::Unspecified),
|
||||||
0,
|
0,
|
||||||
@@ -157,14 +193,19 @@ impl Netlink {
|
|||||||
NlPayload::Payload(tc_msg),
|
NlPayload::Payload(tc_msg),
|
||||||
);
|
);
|
||||||
|
|
||||||
socket.send(nlhdr)?;
|
if let Err(e) = socket.send(nlhdr) {
|
||||||
|
return Err(NetlinkError::Serialization(e));
|
||||||
|
}
|
||||||
|
|
||||||
for response in socket.iter(false) {
|
for response in socket.iter(false) {
|
||||||
let header: Nlmsghdr<Rtm, Tcmsg> = response?;
|
let header: Nlmsghdr<Rtm, Tcmsg> = response?;
|
||||||
|
|
||||||
if let NlPayload::Payload(p) = header.nl_payload {
|
if let NlPayload::Payload(p) = header.nl_payload {
|
||||||
if header.nl_type != Rtm::Newqdisc {
|
if header.nl_type != Rtm::Newqdisc {
|
||||||
return Err(Box::new(NlError::msg("Netlink error retrieving qdisc")));
|
return Err(NetlinkError::WrongType {
|
||||||
|
expected: Rtm::Newqdisc,
|
||||||
|
found: header.nl_type,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.tcm_ifindex == ifindex {
|
if p.tcm_ifindex == ifindex {
|
||||||
@@ -196,15 +237,15 @@ impl Netlink {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Err(Box::new(NoQdiscFoundError));
|
return Err(NetlinkError::NoQdiscFound(ifindex.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn qdisc_from_ifname(ifname: &str) -> Result<Qdisc, Box<dyn Error>> {
|
pub fn qdisc_from_ifname(ifname: &str) -> Result<Qdisc, NetlinkError> {
|
||||||
let ifindex = Netlink::find_interface(ifname)?;
|
let ifindex = Netlink::find_interface(ifname)?;
|
||||||
Netlink::qdisc_from_ifindex(ifindex)
|
Netlink::qdisc_from_ifindex(ifindex)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_qdisc_rate(qdisc: Qdisc, bandwidth_kbit: u64) -> Result<(), Box<dyn Error>> {
|
pub fn set_qdisc_rate(qdisc: Qdisc, bandwidth_kbit: u64) -> Result<(), NetlinkError> {
|
||||||
let mut socket = NlSocketHandle::connect(NlFamily::Route, None, &[])?;
|
let mut socket = NlSocketHandle::connect(NlFamily::Route, None, &[])?;
|
||||||
let bandwidth = bandwidth_kbit * 1000 / 8;
|
let bandwidth = bandwidth_kbit * 1000 / 8;
|
||||||
|
|
||||||
|
|||||||
+19
-10
@@ -1,17 +1,31 @@
|
|||||||
use crate::MeasurementType;
|
use crate::MeasurementType;
|
||||||
|
use etherparse::ReadError;
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
|
use nix::errno::Errno;
|
||||||
use nix::sys::socket::{
|
use nix::sys::socket::{
|
||||||
recvfrom, sendto, socket, AddressFamily, MsgFlags, SockFlag, SockProtocol, SockType,
|
recvfrom, sendto, socket, AddressFamily, MsgFlags, SockFlag, SockProtocol, SockType,
|
||||||
SockaddrIn, SockaddrIn6, SockaddrLike,
|
SockaddrIn, SockaddrIn6, SockaddrLike,
|
||||||
};
|
};
|
||||||
use std::error::Error;
|
|
||||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
|
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
|
||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use std::thread;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use std::{io, thread};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum PingError {
|
||||||
|
#[error("Couldn't parse number")]
|
||||||
|
InvalidNumber(#[from] io::Error),
|
||||||
|
#[error("Error parsing packet")]
|
||||||
|
InvalidPacket(#[from] ReadError),
|
||||||
|
#[error("Socket error")]
|
||||||
|
Socket(#[from] Errno),
|
||||||
|
#[error("Wrong ICMP identifier (expected {expected:?}, found {found:?})")]
|
||||||
|
WrongID { expected: u16, found: u16 },
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct PingReply {
|
pub struct PingReply {
|
||||||
@@ -58,7 +72,7 @@ pub trait PingListener {
|
|||||||
type_: MeasurementType,
|
type_: MeasurementType,
|
||||||
reflectors_lock: Arc<RwLock<Vec<IpAddr>>>,
|
reflectors_lock: Arc<RwLock<Vec<IpAddr>>>,
|
||||||
stats_sender: Sender<PingReply>,
|
stats_sender: Sender<PingReply>,
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> anyhow::Result<()> {
|
||||||
let socket = open_socket(type_)?;
|
let socket = open_socket(type_)?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@@ -98,12 +112,7 @@ pub trait PingListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_packet(
|
fn parse_packet(&self, id: u16, reflector: IpAddr, buf: &[u8]) -> Result<PingReply, PingError>;
|
||||||
&self,
|
|
||||||
id: u16,
|
|
||||||
reflector: IpAddr,
|
|
||||||
buf: &[u8],
|
|
||||||
) -> Result<PingReply, Box<dyn Error>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PingSender {
|
pub trait PingSender {
|
||||||
@@ -112,7 +121,7 @@ pub trait PingSender {
|
|||||||
id: u16,
|
id: u16,
|
||||||
type_: MeasurementType,
|
type_: MeasurementType,
|
||||||
reflectors_lock: Arc<RwLock<Vec<IpAddr>>>,
|
reflectors_lock: Arc<RwLock<Vec<IpAddr>>>,
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> anyhow::Result<()> {
|
||||||
let socket = open_socket(type_)?;
|
let socket = open_socket(type_)?;
|
||||||
|
|
||||||
let mut seq: u16 = 0;
|
let mut seq: u16 = 0;
|
||||||
|
|||||||
+16
-21
@@ -1,8 +1,6 @@
|
|||||||
use std::error::Error;
|
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
||||||
use crate::error::PingParseError;
|
use crate::pinger::{PingError, PingListener, PingReply, PingSender};
|
||||||
use crate::pinger::{PingListener, PingReply, PingSender};
|
|
||||||
use byteorder::*;
|
use byteorder::*;
|
||||||
use etherparse::TransportSlice::{Icmpv4, Icmpv6};
|
use etherparse::TransportSlice::{Icmpv4, Icmpv6};
|
||||||
use etherparse::{IcmpEchoHeader, Icmpv4Header, Icmpv4Type, SlicedPacket};
|
use etherparse::{IcmpEchoHeader, Icmpv4Header, Icmpv4Type, SlicedPacket};
|
||||||
@@ -15,21 +13,17 @@ pub struct PingerICMPEchoSender {}
|
|||||||
|
|
||||||
impl PingListener for PingerICMPEchoListener {
|
impl PingListener for PingerICMPEchoListener {
|
||||||
// Result: RTT, down time, up time
|
// Result: RTT, down time, up time
|
||||||
fn parse_packet(
|
fn parse_packet(&self, id: u16, reflector: IpAddr, buf: &[u8]) -> Result<PingReply, PingError> {
|
||||||
&self,
|
|
||||||
id: u16,
|
|
||||||
reflector: IpAddr,
|
|
||||||
buf: &[u8],
|
|
||||||
) -> Result<PingReply, Box<dyn Error>> {
|
|
||||||
match SlicedPacket::from_ip(buf) {
|
match SlicedPacket::from_ip(buf) {
|
||||||
Err(value) => println!("Err {:?}", value),
|
Err(err) => return Err(PingError::InvalidPacket(err)),
|
||||||
Ok(value) => match value.transport {
|
Ok(value) => match value.transport {
|
||||||
Some(Icmpv4(icmp)) => match icmp.icmp_type() {
|
Some(Icmpv4(icmp)) => match icmp.icmp_type() {
|
||||||
Icmpv4Type::EchoReply(echo) => {
|
Icmpv4Type::EchoReply(echo) => {
|
||||||
if echo.id != id {
|
if echo.id != id {
|
||||||
return Err(Box::new(PingParseError {
|
return Err(PingError::WrongID {
|
||||||
msg: "Wrong ID".to_string(),
|
expected: id,
|
||||||
}));
|
found: echo.id,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let time_sent = icmp
|
let time_sent = icmp
|
||||||
@@ -57,17 +51,18 @@ impl PingListener for PingerICMPEchoListener {
|
|||||||
+ (time_now.tv_nsec() as f64 / 1e9),
|
+ (time_now.tv_nsec() as f64 / 1e9),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Some(Icmpv6(_)) => {}
|
Some(Icmpv6(_)) => {
|
||||||
Some(_) => {}
|
todo!()
|
||||||
None => {}
|
}
|
||||||
|
_ => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(Box::new(PingParseError {
|
|
||||||
msg: "Reached end of parsing function".to_string(),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-22
@@ -1,13 +1,10 @@
|
|||||||
use std::error::Error;
|
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
||||||
use crate::error::PingParseError;
|
use crate::pinger::{PingError, PingListener, PingReply, PingSender};
|
||||||
use crate::pinger::{PingListener, PingReply, PingSender};
|
use crate::utils::Utils;
|
||||||
use crate::Utils;
|
|
||||||
use etherparse::icmpv4::TimestampMessage;
|
use etherparse::icmpv4::TimestampMessage;
|
||||||
use etherparse::TransportSlice::Icmpv4;
|
use etherparse::TransportSlice::Icmpv4;
|
||||||
use etherparse::{Icmpv4Header, Icmpv4Type, SlicedPacket};
|
use etherparse::{Icmpv4Header, Icmpv4Type, SlicedPacket};
|
||||||
use log::warn;
|
|
||||||
use nix::time::{clock_gettime, ClockId};
|
use nix::time::{clock_gettime, ClockId};
|
||||||
|
|
||||||
pub struct PingerICMPTimestampListener {}
|
pub struct PingerICMPTimestampListener {}
|
||||||
@@ -16,21 +13,17 @@ pub struct PingerICMPTimestampSender {}
|
|||||||
|
|
||||||
impl PingListener for PingerICMPTimestampListener {
|
impl PingListener for PingerICMPTimestampListener {
|
||||||
// Result: RTT, down time, up time
|
// Result: RTT, down time, up time
|
||||||
fn parse_packet(
|
fn parse_packet(&self, id: u16, reflector: IpAddr, buf: &[u8]) -> Result<PingReply, PingError> {
|
||||||
&self,
|
|
||||||
id: u16,
|
|
||||||
reflector: IpAddr,
|
|
||||||
buf: &[u8],
|
|
||||||
) -> Result<PingReply, Box<dyn Error>> {
|
|
||||||
match SlicedPacket::from_ip(buf) {
|
match SlicedPacket::from_ip(buf) {
|
||||||
Err(value) => warn!("Error parsing packet: {:?}", value),
|
Err(err) => return Err(PingError::InvalidPacket(err)),
|
||||||
Ok(value) => match value.transport {
|
Ok(value) => match value.transport {
|
||||||
Some(Icmpv4(icmp)) => match icmp.icmp_type() {
|
Some(Icmpv4(icmp)) => match icmp.icmp_type() {
|
||||||
Icmpv4Type::TimestampReply(reply) => {
|
Icmpv4Type::TimestampReply(reply) => {
|
||||||
if reply.id != id {
|
if reply.id != id {
|
||||||
return Err(Box::new(PingParseError {
|
return Err(PingError::WrongID {
|
||||||
msg: "Wrong ID".to_string(),
|
expected: id,
|
||||||
}));
|
found: reply.id,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let time_now = clock_gettime(ClockId::CLOCK_REALTIME).unwrap();
|
let time_now = clock_gettime(ClockId::CLOCK_REALTIME).unwrap();
|
||||||
@@ -59,16 +52,15 @@ impl PingListener for PingerICMPTimestampListener {
|
|||||||
+ (time_now.tv_nsec() as f64 / 1e9),
|
+ (time_now.tv_nsec() as f64 / 1e9),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Some(_) => {}
|
_ => {
|
||||||
None => {}
|
unimplemented!()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(Box::new(PingParseError {
|
|
||||||
msg: "Reached end of parsing function".to_string(),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+139
-88
@@ -1,5 +1,6 @@
|
|||||||
use crate::netlink::Netlink;
|
use crate::netlink::{Netlink, NetlinkError, Qdisc};
|
||||||
use crate::{Config, ReflectorStats, Utils};
|
use crate::utils::Utils;
|
||||||
|
use crate::{Config, ReflectorStats};
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
@@ -12,6 +13,16 @@ use std::sync::mpsc::Sender;
|
|||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum RatecontrolError {
|
||||||
|
#[error("Netlink error")]
|
||||||
|
Netlink(#[from] NetlinkError),
|
||||||
|
|
||||||
|
#[error("Error sorting")]
|
||||||
|
Sorting,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum StatsDirection {
|
pub enum StatsDirection {
|
||||||
@@ -19,34 +30,25 @@ pub enum StatsDirection {
|
|||||||
TX,
|
TX,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn f64_max(a: f64, b: f64) -> f64 {
|
fn generate_initial_speeds(base_speed: f64, size: u32) -> Vec<f64> {
|
||||||
*[a, b].iter().max_by(|a, b| a.total_cmp(b)).unwrap()
|
let mut rates = Vec::new();
|
||||||
}
|
|
||||||
|
|
||||||
fn f64_min(a: f64, b: f64) -> f64 {
|
for _ in 0..size {
|
||||||
*[a, b].iter().min_by(|a, b| a.total_cmp(b)).unwrap()
|
rates.push((rand::thread_rng().next_u64() as f64 * 0.2 + 0.75) * base_speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
rates
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_interface_stats(
|
fn get_interface_stats(
|
||||||
config: Config,
|
config: Config,
|
||||||
down_direction: StatsDirection,
|
down_direction: StatsDirection,
|
||||||
up_direction: StatsDirection,
|
up_direction: StatsDirection,
|
||||||
) -> (i128, i128) {
|
) -> Result<(i128, i128), RatecontrolError> {
|
||||||
let (down_rx, down_tx) = match Netlink::get_interface_stats(config.download_interface.as_str())
|
let down_stats = Netlink::get_interface_stats(config.download_interface.as_str())?;
|
||||||
{
|
let up_stats = Netlink::get_interface_stats(config.upload_interface.as_str())?;
|
||||||
Ok(stats) => (stats.rx_bytes as i128, stats.tx_bytes as i128),
|
let (down_rx, down_tx) = (down_stats.rx_bytes, down_stats.tx_bytes);
|
||||||
Err(e) => {
|
let (up_rx, up_tx) = (up_stats.rx_bytes, up_stats.tx_bytes);
|
||||||
error!("Error while retrieving stats: {}", e.to_string());
|
|
||||||
(-1, -1)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let (up_rx, up_tx) = match Netlink::get_interface_stats(config.upload_interface.as_str()) {
|
|
||||||
Ok(stats) => (stats.rx_bytes as i128, stats.tx_bytes as i128),
|
|
||||||
Err(e) => {
|
|
||||||
error!("Error while retrieving stats: {}", e.to_string());
|
|
||||||
(-1, -1)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let rx_bytes = match down_direction {
|
let rx_bytes = match down_direction {
|
||||||
StatsDirection::RX => down_rx,
|
StatsDirection::RX => down_rx,
|
||||||
@@ -58,42 +60,106 @@ fn get_interface_stats(
|
|||||||
StatsDirection::TX => up_tx,
|
StatsDirection::TX => up_tx,
|
||||||
};
|
};
|
||||||
|
|
||||||
(rx_bytes, tx_bytes)
|
Ok((rx_bytes.into(), tx_bytes.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct State {
|
||||||
|
current_bytes: i128,
|
||||||
|
current_rate: f64,
|
||||||
|
qdisc: Qdisc,
|
||||||
|
nrate: usize,
|
||||||
|
previous_bytes: i128,
|
||||||
|
safe_rates: Vec<f64>,
|
||||||
|
stats_direction: StatsDirection,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Ratecontroller {
|
pub struct Ratecontroller {
|
||||||
pub(crate) config: Config,
|
config: Config,
|
||||||
pub(crate) owd_baseline: Arc<Mutex<HashMap<IpAddr, ReflectorStats>>>,
|
owd_baseline: Arc<Mutex<HashMap<IpAddr, ReflectorStats>>>,
|
||||||
pub(crate) owd_recent: Arc<Mutex<HashMap<IpAddr, ReflectorStats>>>,
|
owd_recent: Arc<Mutex<HashMap<IpAddr, ReflectorStats>>>,
|
||||||
pub(crate) reflectors_lock: Arc<RwLock<Vec<IpAddr>>>,
|
reflectors_lock: Arc<RwLock<Vec<IpAddr>>>,
|
||||||
pub(crate) reselect_trigger: Sender<bool>,
|
reselect_trigger: Sender<bool>,
|
||||||
|
|
||||||
|
state_dl: State,
|
||||||
|
state_ul: State,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ratecontroller {
|
impl Ratecontroller {
|
||||||
pub fn run(&self, down_direction: StatsDirection, up_direction: StatsDirection) {
|
fn adjust_rate(&self) -> anyhow::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(
|
||||||
|
config: Config,
|
||||||
|
owd_baseline: Arc<Mutex<HashMap<IpAddr, ReflectorStats>>>,
|
||||||
|
owd_recent: Arc<Mutex<HashMap<IpAddr, ReflectorStats>>>,
|
||||||
|
reflectors_lock: Arc<RwLock<Vec<IpAddr>>>,
|
||||||
|
reselect_trigger: Sender<bool>,
|
||||||
|
down_direction: StatsDirection,
|
||||||
|
up_direction: StatsDirection,
|
||||||
|
) -> anyhow::Result<Self> {
|
||||||
|
let dl_qdisc = Netlink::qdisc_from_ifname(config.download_interface.as_str())?;
|
||||||
|
let dl_safe_rates =
|
||||||
|
generate_initial_speeds(config.download_base_kbits, config.speed_hist_size);
|
||||||
|
let ul_qdisc = Netlink::qdisc_from_ifname(config.upload_interface.as_str())?;
|
||||||
|
let ul_safe_rates =
|
||||||
|
generate_initial_speeds(config.upload_base_kbits, config.speed_hist_size);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
config,
|
||||||
|
owd_baseline,
|
||||||
|
owd_recent,
|
||||||
|
reflectors_lock,
|
||||||
|
reselect_trigger,
|
||||||
|
state_dl: State {
|
||||||
|
current_bytes: 0,
|
||||||
|
current_rate: 0.0,
|
||||||
|
qdisc: dl_qdisc,
|
||||||
|
nrate: 0,
|
||||||
|
previous_bytes: 0,
|
||||||
|
safe_rates: dl_safe_rates,
|
||||||
|
stats_direction: down_direction,
|
||||||
|
},
|
||||||
|
state_ul: State {
|
||||||
|
current_bytes: 0,
|
||||||
|
current_rate: 0.0,
|
||||||
|
qdisc: ul_qdisc,
|
||||||
|
nrate: 0,
|
||||||
|
previous_bytes: 0,
|
||||||
|
safe_rates: ul_safe_rates,
|
||||||
|
stats_direction: up_direction,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(
|
||||||
|
&self,
|
||||||
|
down_direction: StatsDirection,
|
||||||
|
up_direction: StatsDirection,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
let sleep_time_s = self.config.min_change_interval.floor() as u64;
|
let sleep_time_s = self.config.min_change_interval.floor() as u64;
|
||||||
let sleep_time_ns = ((self.config.min_change_interval % 1.0) * 1e9) as u32;
|
let sleep_time_ns = ((self.config.min_change_interval % 1.0) * 1e9) as u32;
|
||||||
let sleep_time = Duration::new(sleep_time_s, sleep_time_ns);
|
let sleep_time = Duration::new(sleep_time_s, sleep_time_ns);
|
||||||
|
|
||||||
let (start_s, _) = Utils::get_current_time();
|
let (start_s, _) = Utils::get_current_time()?;
|
||||||
let (mut lastchg_s, mut lastchg_ns) = Utils::get_current_time();
|
let (mut lastchg_s, mut lastchg_ns) = Utils::get_current_time()?;
|
||||||
let mut lastchg_t = lastchg_s - start_s + lastchg_ns / 1e9;
|
let mut lastchg_t = lastchg_s - start_s + lastchg_ns / 1e9;
|
||||||
let mut lastdump_t = lastchg_t - 310.0;
|
let mut lastdump_t = lastchg_t - 310.0;
|
||||||
|
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
let down_qdisc =
|
let down_qdisc = Netlink::qdisc_from_ifname(self.config.download_interface.as_str())?;
|
||||||
Netlink::qdisc_from_ifname(self.config.download_interface.as_str()).unwrap();
|
let up_qdisc = Netlink::qdisc_from_ifname(self.config.upload_interface.as_str())?;
|
||||||
let up_qdisc = Netlink::qdisc_from_ifname(self.config.upload_interface.as_str()).unwrap();
|
|
||||||
|
|
||||||
// set qdisc rates to 60% of base rate to make sure we start with sane baselines
|
// set qdisc rates to 60% of base rate to make sure we start with sane baselines
|
||||||
let mut cur_dl_rate: f64 = self.config.download_base_kbits * 0.6;
|
let mut cur_dl_rate: f64 = self.config.download_base_kbits * 0.6;
|
||||||
let mut cur_ul_rate: f64 = self.config.upload_base_kbits * 0.6;
|
let mut cur_ul_rate: f64 = self.config.upload_base_kbits * 0.6;
|
||||||
Netlink::set_qdisc_rate(down_qdisc, cur_dl_rate.floor() as u64).unwrap();
|
Netlink::set_qdisc_rate(down_qdisc, cur_dl_rate.round() as u64)?;
|
||||||
Netlink::set_qdisc_rate(up_qdisc, cur_ul_rate.floor() as u64).unwrap();
|
Netlink::set_qdisc_rate(up_qdisc, cur_ul_rate.round() as u64)?;
|
||||||
|
|
||||||
let (mut prev_rx_bytes, mut prev_tx_bytes) =
|
let (mut prev_rx_bytes, mut prev_tx_bytes) =
|
||||||
get_interface_stats(self.config.clone(), down_direction, up_direction);
|
get_interface_stats(self.config.clone(), down_direction, up_direction)?;
|
||||||
|
|
||||||
if prev_rx_bytes == -1 || prev_tx_bytes == -1 {
|
if prev_rx_bytes == -1 || prev_tx_bytes == -1 {
|
||||||
panic!("Couldn't retrieve stats from interface.");
|
panic!("Couldn't retrieve stats from interface.");
|
||||||
@@ -101,18 +167,10 @@ impl Ratecontroller {
|
|||||||
|
|
||||||
let mut t_prev_bytes = lastchg_t;
|
let mut t_prev_bytes = lastchg_t;
|
||||||
|
|
||||||
let mut safe_dl_rates: Vec<f64> = Vec::new();
|
let mut safe_dl_rates: Vec<f64> =
|
||||||
let mut safe_ul_rates: Vec<f64> = Vec::new();
|
generate_initial_speeds(self.config.download_base_kbits, self.config.speed_hist_size);
|
||||||
for _ in 0..self.config.speed_hist_size {
|
let mut safe_ul_rates: Vec<f64> =
|
||||||
safe_dl_rates.push(
|
generate_initial_speeds(self.config.upload_base_kbits, self.config.speed_hist_size);
|
||||||
(rand::thread_rng().next_u32() as f64 * 0.2 + 0.75)
|
|
||||||
* self.config.download_base_kbits,
|
|
||||||
);
|
|
||||||
safe_ul_rates.push(
|
|
||||||
(rand::thread_rng().next_u32() as f64 * 0.2 + 0.75)
|
|
||||||
* self.config.download_base_kbits,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut nrate_up = 0;
|
let mut nrate_up = 0;
|
||||||
let mut nrate_down = 0;
|
let mut nrate_down = 0;
|
||||||
@@ -126,35 +184,28 @@ impl Ratecontroller {
|
|||||||
speed_hist_fd_inner = File::options()
|
speed_hist_fd_inner = File::options()
|
||||||
.create(true)
|
.create(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.open(self.config.speed_hist_file.as_str())
|
.open(self.config.speed_hist_file.as_str())?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
speed_hist_fd_inner
|
speed_hist_fd_inner.write("time,counter,upspeed,downspeed\n".as_bytes())?;
|
||||||
.write("time,counter,upspeed,downspeed\n".as_bytes())
|
speed_hist_fd_inner.flush()?;
|
||||||
.unwrap();
|
|
||||||
speed_hist_fd_inner.flush().unwrap();
|
|
||||||
|
|
||||||
speed_hist_fd = Some(speed_hist_fd_inner);
|
speed_hist_fd = Some(speed_hist_fd_inner);
|
||||||
|
|
||||||
stats_fd_inner = File::options()
|
stats_fd_inner = File::options()
|
||||||
.create(true)
|
.create(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.open(self.config.stats_file.as_str())
|
.open(self.config.stats_file.as_str())?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
stats_fd_inner
|
stats_fd_inner.write(
|
||||||
.write(
|
"times,timens,rxload,txload,deltadelaydown,deltadelayup,dlrate,uprate\n".as_bytes(),
|
||||||
"times,timens,rxload,txload,deltadelaydown,deltadelayup,dlrate,uprate\n"
|
)?;
|
||||||
.as_bytes(),
|
stats_fd_inner.flush()?;
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
stats_fd_inner.flush().unwrap();
|
|
||||||
|
|
||||||
stats_fd = Some(stats_fd_inner);
|
stats_fd = Some(stats_fd_inner);
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let (mut now_s, now_ns) = Utils::get_current_time();
|
let (mut now_s, now_ns) = Utils::get_current_time()?;
|
||||||
let now_abstime = now_s + now_ns / 1e9;
|
let now_abstime = now_s + now_ns / 1e9;
|
||||||
now_s = now_s - start_s;
|
now_s = now_s - start_s;
|
||||||
let now_t = now_s + now_ns / 1e9;
|
let now_t = now_s + now_ns / 1e9;
|
||||||
@@ -221,7 +272,7 @@ impl Ratecontroller {
|
|||||||
next_ul_rate = cur_ul_rate;
|
next_ul_rate = cur_ul_rate;
|
||||||
|
|
||||||
let (cur_rx_bytes, cur_tx_bytes) =
|
let (cur_rx_bytes, cur_tx_bytes) =
|
||||||
get_interface_stats(self.config.clone(), down_direction, up_direction);
|
get_interface_stats(self.config.clone(), down_direction, up_direction)?;
|
||||||
if cur_rx_bytes == -1 || cur_tx_bytes == -1 {
|
if cur_rx_bytes == -1 || cur_tx_bytes == -1 {
|
||||||
warn!("One or both Netlink stats could not be read. Skipping rate control algorithm");
|
warn!("One or both Netlink stats could not be read. Skipping rate control algorithm");
|
||||||
} else if down_deltas.len() < 3 || up_deltas.len() < 3 {
|
} else if down_deltas.len() < 3 || up_deltas.len() < 3 {
|
||||||
@@ -259,7 +310,7 @@ impl Ratecontroller {
|
|||||||
let max_dl =
|
let max_dl =
|
||||||
safe_dl_rates.iter().max_by(|a, b| a.total_cmp(b)).unwrap();
|
safe_dl_rates.iter().max_by(|a, b| a.total_cmp(b)).unwrap();
|
||||||
next_dl_rate = cur_dl_rate
|
next_dl_rate = cur_dl_rate
|
||||||
* (1.0 + 0.1 * f64_max(0.0, 1.0 - cur_dl_rate / max_dl))
|
* (1.0 + 0.1 * (1.0 - cur_dl_rate / max_dl).max(0.0))
|
||||||
+ (self.config.download_base_kbits * 0.03);
|
+ (self.config.download_base_kbits * 0.03);
|
||||||
nrate_down = nrate_down + 1;
|
nrate_down = nrate_down + 1;
|
||||||
nrate_down = nrate_down % self.config.speed_hist_size as usize;
|
nrate_down = nrate_down % self.config.speed_hist_size as usize;
|
||||||
@@ -273,31 +324,31 @@ impl Ratecontroller {
|
|||||||
let max_ul =
|
let max_ul =
|
||||||
safe_ul_rates.iter().max_by(|a, b| a.total_cmp(b)).unwrap();
|
safe_ul_rates.iter().max_by(|a, b| a.total_cmp(b)).unwrap();
|
||||||
next_ul_rate = cur_ul_rate
|
next_ul_rate = cur_ul_rate
|
||||||
* (1.0 + 0.1 * f64_max(0.0, 1.0 - cur_ul_rate / max_ul))
|
* (1.0 + 0.1 * (1.0 - cur_ul_rate / max_ul).max(0.0))
|
||||||
+ (self.config.download_base_kbits * 0.03);
|
+ (self.config.download_base_kbits * 0.03);
|
||||||
nrate_up = nrate_up + 1;
|
nrate_up = nrate_up + 1;
|
||||||
nrate_up = nrate_up % self.config.speed_hist_size as usize;
|
nrate_up = nrate_up % self.config.speed_hist_size as usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
if down_delta_stat > self.config.download_delay_ms {
|
if down_delta_stat > self.config.download_delay_ms {
|
||||||
if safe_dl_rates.len() > 0 {
|
match safe_dl_rates.choose(&mut rng) {
|
||||||
next_dl_rate = f64_min(
|
Some(rnd_rate) => {
|
||||||
0.9 * cur_dl_rate * rx_load,
|
next_dl_rate = rnd_rate.min(0.9 * cur_dl_rate * rx_load);
|
||||||
*safe_dl_rates.choose(&mut rng).unwrap(),
|
}
|
||||||
)
|
None => {
|
||||||
} else {
|
next_dl_rate = 0.9 * cur_dl_rate * rx_load;
|
||||||
next_dl_rate = 0.9 * cur_dl_rate * rx_load;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if up_delta_stat > self.config.upload_delay_ms {
|
if up_delta_stat > self.config.upload_delay_ms {
|
||||||
if safe_ul_rates.len() > 0 {
|
match safe_ul_rates.choose(&mut rng) {
|
||||||
next_ul_rate = f64_min(
|
Some(rnd_rate) => {
|
||||||
0.9 * cur_ul_rate * tx_load,
|
next_ul_rate = rnd_rate.min(0.9 * cur_ul_rate * tx_load);
|
||||||
*safe_ul_rates.choose(&mut rng).unwrap(),
|
}
|
||||||
)
|
None => {
|
||||||
} else {
|
next_ul_rate = 0.9 * cur_ul_rate * tx_load;
|
||||||
next_ul_rate = 0.9 * cur_ul_rate * tx_load;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -307,8 +358,8 @@ impl Ratecontroller {
|
|||||||
prev_rx_bytes = cur_rx_bytes;
|
prev_rx_bytes = cur_rx_bytes;
|
||||||
prev_tx_bytes = cur_tx_bytes;
|
prev_tx_bytes = cur_tx_bytes;
|
||||||
|
|
||||||
next_dl_rate = f64_max(self.config.download_min_kbits, next_dl_rate).floor();
|
next_dl_rate = next_dl_rate.max(self.config.download_min_kbits).round();
|
||||||
next_ul_rate = f64_max(self.config.upload_min_kbits, next_ul_rate).floor();
|
next_ul_rate = next_ul_rate.max(self.config.upload_min_kbits).round();
|
||||||
|
|
||||||
if next_dl_rate != cur_dl_rate || next_ul_rate != cur_ul_rate {
|
if next_dl_rate != cur_dl_rate || next_ul_rate != cur_ul_rate {
|
||||||
info!(
|
info!(
|
||||||
@@ -318,17 +369,17 @@ impl Ratecontroller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if next_dl_rate != cur_dl_rate {
|
if next_dl_rate != cur_dl_rate {
|
||||||
Netlink::set_qdisc_rate(down_qdisc, next_dl_rate as u64).unwrap();
|
Netlink::set_qdisc_rate(down_qdisc, next_dl_rate as u64)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if next_ul_rate != cur_ul_rate {
|
if next_ul_rate != cur_ul_rate {
|
||||||
Netlink::set_qdisc_rate(up_qdisc, next_ul_rate as u64).unwrap();
|
Netlink::set_qdisc_rate(up_qdisc, next_ul_rate as u64)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_dl_rate = next_dl_rate;
|
cur_dl_rate = next_dl_rate;
|
||||||
cur_ul_rate = next_ul_rate;
|
cur_ul_rate = next_ul_rate;
|
||||||
|
|
||||||
(lastchg_s, lastchg_ns) = Utils::get_current_time();
|
(lastchg_s, lastchg_ns) = Utils::get_current_time()?;
|
||||||
|
|
||||||
if rx_load > 0.0
|
if rx_load > 0.0
|
||||||
&& tx_load > 0.0
|
&& tx_load > 0.0
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ pub struct ReflectorSelector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ReflectorSelector {
|
impl ReflectorSelector {
|
||||||
pub fn run(&self) {
|
pub fn run(&self) -> anyhow::Result<()> {
|
||||||
let mut selector_sleep_time = Duration::new(30, 0);
|
let mut selector_sleep_time = Duration::new(30, 0);
|
||||||
let mut reselection_count = 0;
|
let mut reselection_count = 0;
|
||||||
let baseline_sleep_time = Duration::new(
|
let baseline_sleep_time = Duration::new(
|
||||||
|
|||||||
+12
-4
@@ -1,6 +1,14 @@
|
|||||||
|
use nix::errno::Errno;
|
||||||
use nix::time::{clock_gettime, ClockId};
|
use nix::time::{clock_gettime, ClockId};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
pub(crate) struct Utils {}
|
#[derive(Debug, Error)]
|
||||||
|
pub enum UtilError {
|
||||||
|
#[error("libc error")]
|
||||||
|
Libc(#[from] Errno),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Utils {}
|
||||||
|
|
||||||
impl Utils {
|
impl Utils {
|
||||||
pub(crate) fn a_else_b(a: f64, b: f64) -> f64 {
|
pub(crate) fn a_else_b(a: f64, b: f64) -> f64 {
|
||||||
@@ -9,9 +17,9 @@ impl Utils {
|
|||||||
pub(crate) fn ewma_factor(tick: f64, dur: f64) -> f64 {
|
pub(crate) fn ewma_factor(tick: f64, dur: f64) -> f64 {
|
||||||
((0.5_f64).ln() / (dur / tick)).exp()
|
((0.5_f64).ln() / (dur / tick)).exp()
|
||||||
}
|
}
|
||||||
pub(crate) fn get_current_time() -> (f64, f64) {
|
pub(crate) fn get_current_time() -> Result<(f64, f64), UtilError> {
|
||||||
let time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap();
|
let time = clock_gettime(ClockId::CLOCK_MONOTONIC)?;
|
||||||
(time.tv_sec() as f64, time.tv_nsec() as f64)
|
Ok((time.tv_sec() as f64, time.tv_nsec() as f64))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_endian = "big")]
|
#[cfg(target_endian = "big")]
|
||||||
|
|||||||
Reference in new issue
Block a user