Implement missing features, some cleanup

This commit is contained in:
Nils Andreas Svee committed 2022-09-28 00:25:35 +02:00
1 parent c06078fccb
commit e3b7b7e9ba
12 files changed
+703 -173

No files matched your search

Generated
+158 -24
View File
@@ -109,10 +109,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clang-sys" name = "cfg_aliases"
version = "1.3.3" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a050e2153c5be08febd6734e29298e844fdb0fa21aeddd63b4eb7baa106c69b" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]]
name = "clang-sys"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3"
dependencies = [ dependencies = [
"glob", "glob",
"libc", "libc",
@@ -145,9 +151,9 @@ dependencies = [
[[package]] [[package]]
name = "either" name = "either"
version = "1.7.0" version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]] [[package]]
name = "env_logger" name = "env_logger"
@@ -203,6 +209,21 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]]
name = "itoa"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@@ -217,9 +238,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.132" version = "0.2.133"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
[[package]] [[package]]
name = "libloading" name = "libloading"
@@ -241,6 +262,16 @@ dependencies = [
"cmake", "cmake",
] ]
[[package]]
name = "lock_api"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.17" version = "0.4.17"
@@ -312,6 +343,40 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "num_threads"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
dependencies = [
"libc",
]
[[package]]
name = "parking_lot"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
dependencies = [
"cfg-if",
"instant",
"libc",
"redox_syscall",
"smallvec",
"winapi",
]
[[package]] [[package]]
name = "peeking_take_while" name = "peeking_take_while"
version = "0.1.2" version = "0.1.2"
@@ -332,9 +397,9 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.43" version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@@ -371,13 +436,22 @@ dependencies = [
[[package]] [[package]]
name = "rand_core" name = "rand_core"
version = "0.6.3" version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [ dependencies = [
"getrandom", "getrandom",
] ]
[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.6.0" version = "1.6.0"
@@ -413,19 +487,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]] [[package]]
name = "serde" name = "scopeguard"
version = "1.0.144" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.144" version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -438,6 +518,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
[[package]]
name = "smallvec"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
[[package]] [[package]]
name = "sqm-autorate-rust" name = "sqm-autorate-rust"
version = "0.1.0" version = "0.1.0"
@@ -445,12 +531,42 @@ dependencies = [
"bincode", "bincode",
"byteorder", "byteorder",
"etherparse", "etherparse",
"libc", "log",
"neli", "neli",
"nix", "nix",
"rand", "rand",
"rust-uci", "rust-uci",
"serde", "serde",
"static_init",
"time",
]
[[package]]
name = "static_init"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a2a1c578e98c1c16fc3b8ec1328f7659a500737d7a0c6d625e73e830ff9c1f6"
dependencies = [
"bitflags",
"cfg_aliases",
"libc",
"parking_lot",
"parking_lot_core",
"static_init_macro",
"winapi",
]
[[package]]
name = "static_init_macro"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70a2595fc3aa78f2d0e45dd425b22282dd863273761cc77780914b2cf3003acf"
dependencies = [
"cfg_aliases",
"memchr",
"proc-macro2",
"quote",
"syn",
] ]
[[package]] [[package]]
@@ -461,9 +577,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.99" version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -489,16 +605,34 @@ dependencies = [
] ]
[[package]] [[package]]
name = "unicode-ident" name = "time"
version = "1.0.3" version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" checksum = "3c3f9a28b618c3a6b9251b6908e9c99e04b9e5c02e6581ccbb67d59c34ef7f9b"
dependencies = [
"itoa",
"libc",
"num_threads",
"time-macros",
]
[[package]]
name = "time-macros"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
[[package]]
name = "unicode-ident"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.1.9" version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]] [[package]]
name = "vec_map" name = "vec_map"
+17 -3
View File
@@ -16,12 +16,26 @@ strip = "symbols"
bincode = "1.3.3" bincode = "1.3.3"
byteorder = "1.4.3" byteorder = "1.4.3"
etherparse = "0.12.0" etherparse = "0.12.0"
libc = "0.2.132"
neli = "0.6.2" neli = "0.6.2"
nix = { version = "0.25.0", features = ["net", "socket", "time"] }
rand = "0.8.5" rand = "0.8.5"
rust-uci = { version = "0.1.5", optional = true } rust-uci = { version = "0.1.5", optional = true }
serde = { version = "1.0.144", features = ["derive"] } static_init = "1.0.3"
[dependencies.log]
features = ["std"]
version = "0.4.17"
[dependencies.nix]
features = ["net", "socket", "time"]
version = "0.25.0"
[dependencies.serde]
features = ["derive"]
version = "1.0.144"
[dependencies.time]
features = ["formatting", "macros"]
version = "0.3.14"
[features] [features]
uci = ["dep:rust-uci"] uci = ["dep:rust-uci"]
+11 -6
View File
@@ -1,5 +1,6 @@
use crate::pinger::PingReply; use crate::pinger::PingReply;
use crate::{Config, Utils}; use crate::{Config, Utils};
use log::info;
use std::collections::HashMap; use std::collections::HashMap;
use std::net::IpAddr; use std::net::IpAddr;
use std::sync::mpsc::{Receiver, Sender}; use std::sync::mpsc::{Receiver, Sender};
@@ -30,8 +31,8 @@ impl Baseliner {
* aren't bloat related, with less sensitivity (bigger numbers) we smooth through quick spikes * aren't bloat related, with less sensitivity (bigger numbers) we smooth through quick spikes
* but take longer to respond to real bufferbloat * but take longer to respond to real bufferbloat
*/ */
let slow_factor = Utils::ewma_factor(self.config.tick_duration, 135.0); let slow_factor = Utils::ewma_factor(self.config.tick_interval, 135.0);
let fast_factor = Utils::ewma_factor(self.config.tick_duration, 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().unwrap();
@@ -39,13 +40,13 @@ impl Baseliner {
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();
let mut owd_baseline_new = ReflectorStats { let owd_baseline_new = ReflectorStats {
down_ewma: time_data.down_time, down_ewma: time_data.down_time,
up_ewma: time_data.down_time, up_ewma: time_data.down_time,
last_receive_time_s: time_data.last_receive_time_s, last_receive_time_s: time_data.last_receive_time_s,
}; };
let mut owd_recent_new = ReflectorStats { let owd_recent_new = ReflectorStats {
down_ewma: time_data.down_time, down_ewma: time_data.down_time,
up_ewma: time_data.down_time, up_ewma: time_data.down_time,
last_receive_time_s: time_data.last_receive_time_s, last_receive_time_s: time_data.last_receive_time_s,
@@ -79,6 +80,10 @@ impl Baseliner {
{ {
owd_baseline.last_receive_time_s = time_data.last_receive_time_s - 60.0; owd_baseline.last_receive_time_s = time_data.last_receive_time_s - 60.0;
owd_recent.last_receive_time_s = time_data.last_receive_time_s - 60.0; owd_recent.last_receive_time_s = time_data.last_receive_time_s - 60.0;
info!(
"Reflector {} has OWD > 5 seconds more than baseline, triggering reselection",
time_data.reflector
);
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
@@ -100,11 +105,11 @@ impl Baseliner {
} }
} }
println!( info!(
"Reflector {} up baseline = {} down baseline = {}", "Reflector {} up baseline = {} down baseline = {}",
time_data.reflector, owd_baseline.up_ewma, owd_baseline.down_ewma time_data.reflector, owd_baseline.up_ewma, owd_baseline.down_ewma
); );
println!( info!(
"Reflector {} up recent = {} down recent = {}", "Reflector {} up recent = {} down recent = {}",
time_data.reflector, owd_recent.up_ewma, owd_recent.down_ewma time_data.reflector, owd_recent.up_ewma, owd_recent.down_ewma
); );
+190 -35
View File
@@ -1,3 +1,5 @@
use crate::error::{ConfigParseError, MissingConfigError};
use log::Level;
#[cfg(feature = "uci")] #[cfg(feature = "uci")]
use rust_uci::Uci; use rust_uci::Uci;
use std::error::Error; use std::error::Error;
@@ -16,55 +18,208 @@ where
Ok(io::BufReader::new(file).lines()) Ok(io::BufReader::new(file).lines())
} }
#[allow(dead_code)] #[derive(Clone, Debug)]
#[derive(Clone)] pub(crate) enum AutorateType {
ICMP = 1,
ICMPTimestamps,
NTP,
TCPTimestamps,
}
#[derive(Clone, Debug)]
pub(crate) struct Config { pub(crate) struct Config {
// Network section
pub(crate) download_interface: String, pub(crate) download_interface: String,
pub(crate) upload_interface: String, pub(crate) upload_interface: String,
pub(crate) download_base_kbits: f64,
pub(crate) download_min_kbits: f64,
pub(crate) upload_base_kbits: f64,
pub(crate) upload_min_kbits: f64,
pub(crate) reflector_list_file: String, // Output section
pub(crate) log_level: Level,
pub(crate) speed_hist_file: String,
pub(crate) stats_file: String,
pub(crate) suppress_statistics: bool,
// Advanced section
pub(crate) download_delay_ms: f64,
pub(crate) high_load_level: f64,
pub(crate) min_change_interval: f64, pub(crate) min_change_interval: f64,
pub(crate) num_reflectors: u8, pub(crate) num_reflectors: u8,
pub(crate) tick_duration: f64, pub(crate) reflector_list_file: String,
pub(crate) speed_hist_size: u32,
pub(crate) tick_interval: f64,
pub(crate) upload_delay_ms: f64,
} }
impl Config { impl Config {
pub fn new() -> Self { pub fn new() -> Result<Self, Box<dyn Error>> {
// First try to load from UCI (if enabled), if nothing is returned, use the environment instead. Ok(Self {
match Self::load_from_uci() { // Network section
Some(config) => config, download_base_kbits: Self::get::<f64>(
None => Self { "SQMA_DOWNLOAD_BASE_KBITS",
download_interface: env::var("DOWNLOAD_INTERFACE") "sqm-autorate.@network[0].download_base_kbits",
.expect("A download interface is required"), None,
upload_interface: env::var("UPLOAD_INTERFACE") )?,
.expect("A upload interface is required"), download_interface: Self::get::<String>(
reflector_list_file: env::var("REFLECTOR_LIST_FILE") "SQMA_DOWNLOAD_INTERFACE",
.expect("A reflector list is required"), "sqm-autorate.@network[0].download_interface",
min_change_interval: 0.5, None,
num_reflectors: 5, )?,
tick_duration: 0.5, download_min_kbits: Self::get::<f64>(
}, "SQMA_DOWNLOAD_MIN_KBITS",
} "sqm-autorate.@network[0].download_min_kbits",
} None,
)?,
#[cfg(feature = "uci")] upload_base_kbits: Self::get::<f64>(
fn load_from_uci() -> Option<Self> { "SQMA_UPLOAD_BASE_KBITS",
let uci: Uci = Uci::new().expect("Couldn't create UCI instance"); "sqm-autorate.@network[0].upload_base_kbits",
None,
Some(Self { )?,
download_interface: "wan".to_string(), upload_interface: Self::get::<String>(
upload_interface: "wan".to_string(), "SQMA_UPLOAD_INTERFACE",
reflector_list_file: "".to_string(), "sqm-autorate.@network[0].upload_interface",
min_change_interval: 0.5, None,
num_reflectors: 5, )?,
tick_duration: 0.5, upload_min_kbits: Self::get::<f64>(
"SQMA_UPLOAD_MIN_KBITS",
"sqm-autorate.@network[0].upload_min_kbits",
None,
)?,
// Output section
log_level: Self::get::<Level>(
"SQMA_LOG_LEVEL",
"sqm-autorate.@output[0].log_level",
Some(Level::Error),
)?,
speed_hist_file: Self::get::<String>(
"SQMA_SPEED_HIST_FILE",
"sqm-autorate.@output[0].speed_hist_file",
Some("/tmp/sqm-speedhist.csv".parse()?),
)?,
stats_file: Self::get::<String>(
"SQMA_STATS_FILE",
"sqm-autorate.@output[0].stats_file",
Some("/tmp/sqm-autorate.csv".parse()?),
)?,
suppress_statistics: Self::get::<bool>(
"SQMA_SUPPRESS_STATISTICS",
"sqm-autorate.@output[0].suppress_statistics",
Some(false),
)?,
// Advanced section
download_delay_ms: Self::get::<f64>(
"SQMA_DOWNLOAD_DELAY_MS",
"sqm-autorate.@advanced_settings[0].download_delay_ms",
Some(15.0),
)?,
high_load_level: Self::get::<f64>(
"SQMA_HIGH_LOAD_LEVEL",
"sqm-autorate.@advanced_settings[0].high_load_level",
Some(0.8),
)?,
min_change_interval: Self::get::<f64>(
"SQMA_MIN_CHANGE_INTERVAL",
"sqm-autorate.@advanced_settings[0].min_change_interval",
Some(0.5),
)?,
num_reflectors: Self::get::<u8>(
"SQMA_NUM_REFLECTORS",
"sqm-autorate.@advanced_settings[0].num_reflectors",
Some(5),
)?,
reflector_list_file: Self::get::<String>(
"SQMA_REFLECTOR_LIST_FILE",
"sqm-autorate.@advanced_settings[0].reflector_list_file",
Some("/etc/sqm-autorate/reflectors-icmp.csv".parse()?),
)?,
speed_hist_size: Self::get::<u32>(
"SQMA_SPEED_HIST_SIZE",
"sqm-autorate.@advanced_settings[0].speed_hist_size",
Some(100),
)?,
tick_interval: Self::get::<f64>(
"SQMA_TICK_INTERVAL",
"sqm-autorate.@advanced_settings[0].tick_interval",
Some(0.5),
)?,
upload_delay_ms: Self::get::<f64>(
"SQMA_UPLOAD_DELAY_MS",
"sqm-autorate.@advanced_settings[0].upload_delay_ms",
Some(15.0),
)?,
}) })
} }
fn get<T: std::str::FromStr>(
env_key: &str,
uci_key: &str,
default: Option<T>,
) -> Result<T, Box<dyn Error>> {
return match Self::get_value(env_key, uci_key, None) {
Ok(val) => match val.parse::<T>() {
Ok(parsed_val) => Ok(parsed_val),
// 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
Err(_) => Err(Box::new(ConfigParseError {
config_key: env_key.to_string(),
})),
},
Err(e) => {
return match default {
Some(val) => Ok(val),
None => Err(e),
}
}
};
}
fn get_value(
env_key: &str,
uci_key: &str,
default: Option<String>,
) -> Result<String, Box<dyn Error>> {
let mut err: Option<Box<dyn Error>>;
match env::var(env_key) {
Ok(val) => return Ok(val),
Err(e) => err = Some(Box::new(e)),
};
match Self::get_from_uci(uci_key) {
Ok(opt) => match opt {
Some(val) => return Ok(val),
None => {}
},
Err(e) => err = Some(e),
};
match default {
Some(val) => return Ok(val),
None => {
err = Some(Box::new(MissingConfigError {
config_key: env_key.to_string(),
}))
}
}
Err(err.unwrap())
}
#[cfg(feature = "uci")]
fn get_from_uci(key: &str) -> Result<Option<String>, Box<dyn Error>> {
let mut uci: Uci = Uci::new()?;
return match uci.get(key) {
Ok(val) => Ok(Some(val)),
Err(e) => Err(Box::new(e)),
};
}
#[cfg(not(feature = "uci"))] #[cfg(not(feature = "uci"))]
fn load_from_uci() -> Option<Self> { fn get_from_uci(_: &str) -> Result<Option<String>, Box<dyn Error>> {
None Ok(None)
} }
pub fn load_reflectors(&self) -> Result<Vec<IpAddr>, Box<dyn Error>> { pub fn load_reflectors(&self) -> Result<Vec<IpAddr>, Box<dyn Error>> {
+44 -2
View File
@@ -2,12 +2,55 @@ use std::error::Error;
use std::fmt; use std::fmt;
use std::fmt::Display; 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 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)] #[derive(Default, Debug)]
pub struct NoQdiscFoundError; pub struct NoQdiscFoundError;
impl Error for NoQdiscFoundError { impl Error for NoQdiscFoundError {
fn source(&self) -> Option<&(dyn Error + 'static)> { fn source(&self) -> Option<&(dyn Error + 'static)> {
// The compiler transparently casts `&sqlx::Error` into a `&dyn Error`
None None
} }
} }
@@ -24,7 +67,6 @@ pub struct PingParseError;
impl Error for PingParseError { impl Error for PingParseError {
fn source(&self) -> Option<&(dyn Error + 'static)> { fn source(&self) -> Option<&(dyn Error + 'static)> {
// The compiler transparently casts `&sqlx::Error` into a `&dyn Error`
None None
} }
} }
+55
View File
@@ -0,0 +1,55 @@
use static_init::dynamic;
use std::error::Error;
use std::fmt;
use std::str::FromStr;
use std::sync::{Arc, RwLock};
use std::time::SystemTime;
use log::{Level, LevelFilter, Metadata, Record, SetLoggerError};
use time::format_description::FormatItem;
use time::formatting::Formattable;
use time::macros::format_description;
use time::OffsetDateTime;
const LOG_DATETIME_FORMAT: &[FormatItem] = format_description!(
"[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour \
sign:mandatory]:[offset_minute]:[offset_second]"
);
#[derive(Clone, Copy)]
pub struct SimpleLogger {
pub level: log::Level,
}
fn time_format<T>(dt: T, format: &(impl Formattable + ?Sized)) -> String
where
T: Into<OffsetDateTime>,
{
dt.into().format(format).unwrap()
}
impl log::Log for SimpleLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Info
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
println!(
"{} {:5} {}:{}: {}",
time_format(SystemTime::now(), &LOG_DATETIME_FORMAT),
record.level(),
record.file().unwrap(),
record.line().unwrap(),
record.args()
);
}
}
fn flush(&self) {}
}
pub fn init(level: Level) -> Result<(), SetLoggerError> {
log::set_boxed_logger(Box::new(SimpleLogger { level }))
.map(|()| log::set_max_level(LevelFilter::Trace))
}
+59 -22
View File
@@ -4,6 +4,7 @@ mod baseliner;
mod cake; mod cake;
mod config; mod config;
mod error; mod error;
mod log;
mod netlink; mod netlink;
mod pinger; mod pinger;
mod pinger_icmp; mod pinger_icmp;
@@ -13,7 +14,9 @@ mod reflector_selector;
mod utils; mod utils;
use crate::baseliner::{Baseliner, ReflectorStats}; use crate::baseliner::{Baseliner, ReflectorStats};
use ::log::{error, info, Level, LevelFilter};
use std::collections::HashMap; use std::collections::HashMap;
use std::error::Error;
use std::net::IpAddr; use std::net::IpAddr;
use std::process::ExitCode; use std::process::ExitCode;
use std::str::FromStr; use std::str::FromStr;
@@ -21,25 +24,39 @@ use std::sync::mpsc::channel;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::thread::sleep; use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
use std::{process, thread}; use std::{panic, process, thread};
use crate::config::Config; use crate::config::Config;
use crate::netlink::Netlink; use crate::log::SimpleLogger;
use crate::netlink::{Netlink, Qdisc};
use crate::pinger::{PingListener, PingSender, SocketType}; use crate::pinger::{PingListener, PingSender, SocketType};
use crate::pinger_icmp::{PingerICMPEchoListener, PingerICMPEchoSender}; use crate::pinger_icmp::{PingerICMPEchoListener, PingerICMPEchoSender};
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; use crate::utils::Utils;
const VERSION: &str = "0.0.1";
fn main() -> ExitCode { fn main() -> ExitCode {
println!("starting up"); println!("Starting sqm-autorate version {}", VERSION);
let config = match Config::new() {
Ok(val) => val,
Err(e) => {
println!(
"Something went wrong while getting config: {}",
e.to_string()
);
return ExitCode::FAILURE;
}
};
crate::log::init(config.log_level).expect("Couldn't initialize logger");
let config = Config::new();
let mut reflectors = match config.load_reflectors() { let mut reflectors = match config.load_reflectors() {
Ok(refl) => refl, Ok(refl) => refl,
Err(e) => { Err(e) => {
println!("Couldn't load reflectors: {}", e.to_string()); info!("Couldn't load reflector list: {}", e.to_string());
return ExitCode::FAILURE; return ExitCode::FAILURE;
} }
}; };
@@ -49,9 +66,9 @@ fn main() -> ExitCode {
let id = (process::id() & 0xFFFF) as u16; let id = (process::id() & 0xFFFF) as u16;
// Create data structures shared by different threads // Create data structures shared by different threads
let mut owd_baseline = Arc::new(Mutex::new(HashMap::<IpAddr, ReflectorStats>::new())); let owd_baseline = Arc::new(Mutex::new(HashMap::<IpAddr, ReflectorStats>::new()));
let mut owd_recent = Arc::new(Mutex::new(HashMap::<IpAddr, ReflectorStats>::new())); let owd_recent = Arc::new(Mutex::new(HashMap::<IpAddr, ReflectorStats>::new()));
let mut reflector_peers_lock = Arc::new(Mutex::new(Vec::<IpAddr>::new())); let reflector_peers_lock = Arc::new(Mutex::new(Vec::<IpAddr>::new()));
let mut reflector_pool = Vec::<IpAddr>::new(); let mut reflector_pool = Vec::<IpAddr>::new();
let reflector_pool_size = reflectors.len(); let reflector_pool_size = reflectors.len();
@@ -84,7 +101,7 @@ fn main() -> ExitCode {
let mut pinger_receiver = PingerICMPEchoListener::new(id); let mut pinger_receiver = PingerICMPEchoListener::new(id);
let mut pinger_sender = PingerICMPEchoSender::new(id); let mut pinger_sender = PingerICMPEchoSender::new(id);
let mut baseliner = Baseliner { let baseliner = Baseliner {
config: config.clone(), config: config.clone(),
owd_baseline: owd_baseline.clone(), owd_baseline: owd_baseline.clone(),
owd_recent: owd_recent.clone(), owd_recent: owd_recent.clone(),
@@ -92,12 +109,20 @@ fn main() -> ExitCode {
stats_receiver: baseliner_stats_receiver, stats_receiver: baseliner_stats_receiver,
}; };
let dl_intf = config.clone().download_interface; let down_qdisc = match Netlink::qdisc_from_ifname(config.download_interface.as_str()) {
let ul_intf = config.clone().upload_interface; Ok(qdisc) => qdisc,
let down_ifindex = Netlink::find_interface(dl_intf.as_str()).unwrap(); Err(e) => {
let up_ifindex = Netlink::find_interface(ul_intf.as_str()).unwrap(); error!("Couldn't find download qdisc: {}", e);
let down_qdisc = Netlink::find_qdisc(down_ifindex).unwrap(); return ExitCode::FAILURE;
let up_qdisc = Netlink::find_qdisc(up_ifindex).unwrap(); }
};
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
@@ -111,11 +136,17 @@ fn main() -> ExitCode {
let receiver_handle = thread::Builder::new() let receiver_handle = thread::Builder::new()
.name("receiver".to_string()) .name("receiver".to_string())
.spawn(move || { .spawn(move || {
pinger_receiver.listen( match pinger_receiver.listen(
SocketType::ICMP, SocketType::ICMP,
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"); .expect("Couldn't spawn ping receiver thread");
let baseliner_handle = thread::Builder::new() let baseliner_handle = thread::Builder::new()
@@ -125,7 +156,15 @@ fn main() -> ExitCode {
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()) .name("sender".to_string())
.spawn(move || pinger_sender.send(SocketType::ICMP, reflector_peers_lock_clone)) .spawn(
move || match pinger_sender.send(SocketType::ICMP, reflector_peers_lock_clone) {
Ok(_) => {}
Err(e) => {
error!("Error occured in sender thread: {}", e);
panic!();
}
},
)
.expect("Couldn't spawn ping sender thread"); .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];
@@ -133,7 +172,6 @@ fn main() -> ExitCode {
if reflector_pool_size > 5 { if reflector_pool_size > 5 {
let reflector_selector = ReflectorSelector { let reflector_selector = ReflectorSelector {
config: config.clone(), config: config.clone(),
owd_baseline: owd_baseline.clone(),
owd_recent: owd_recent.clone(), owd_recent: owd_recent.clone(),
reflector_peers_lock: reflector_peers_lock.clone(), reflector_peers_lock: reflector_peers_lock.clone(),
reflector_pool, reflector_pool,
@@ -159,13 +197,12 @@ 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(StatsDirection::TX, StatsDirection::RX)) .spawn(move || ratecontroller.run(StatsDirection::RX, StatsDirection::TX))
.expect("Couldn't spawn ratecontroller thread"); .expect("Couldn't spawn ratecontroller thread");
threads.push(ratecontroller_handle); threads.push(ratecontroller_handle);
for thread in threads { for thread in threads {
println!("thread: {}", thread.thread().name().unwrap());
thread.join().unwrap(); thread.join().unwrap();
} }
+8 -3
View File
@@ -137,7 +137,7 @@ impl Netlink {
Err(Box::new(NlError::msg("Error getting stats"))) Err(Box::new(NlError::msg("Error getting stats")))
} }
pub fn find_qdisc(ifindex: i32) -> Result<Qdisc, Box<dyn Error>> { pub fn qdisc_from_ifindex(ifindex: i32) -> Result<Qdisc, Box<dyn Error>> {
let mut socket = NlSocketHandle::connect(NlFamily::Route, None, &[]).unwrap(); let mut socket = NlSocketHandle::connect(NlFamily::Route, None, &[]).unwrap();
let tc_msg = Tcmsg::new( let tc_msg = Tcmsg::new(
u8::from(RtAddrFamily::Unspecified), u8::from(RtAddrFamily::Unspecified),
@@ -199,9 +199,14 @@ impl Netlink {
return Err(Box::new(NoQdiscFoundError)); return Err(Box::new(NoQdiscFoundError));
} }
pub fn set_qdisc_rate(qdisc: Qdisc, bandwidth: u64) -> Result<(), Box<dyn Error>> { pub fn qdisc_from_ifname(ifname: &str) -> Result<Qdisc, Box<dyn Error>> {
let ifindex = Netlink::find_interface(ifname)?;
Netlink::qdisc_from_ifindex(ifindex)
}
pub fn set_qdisc_rate(qdisc: Qdisc, bandwidth_kbit: u64) -> Result<(), Box<dyn Error>> {
let mut socket = NlSocketHandle::connect(NlFamily::Route, None, &[]).unwrap(); let mut socket = NlSocketHandle::connect(NlFamily::Route, None, &[]).unwrap();
let bandwidth = bandwidth * 1000 / 8; let bandwidth = bandwidth_kbit * 1000 / 8;
let mut attrs = RtBuffer::new(); let mut attrs = RtBuffer::new();
+18 -10
View File
@@ -1,3 +1,4 @@
use log::error;
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,
@@ -16,6 +17,7 @@ pub enum SocketType {
UDP, UDP,
} }
#[allow(dead_code)]
pub struct PingReply { pub struct PingReply {
pub(crate) reflector: IpAddr, pub(crate) reflector: IpAddr,
pub(crate) seq: u16, pub(crate) seq: u16,
@@ -29,9 +31,9 @@ pub struct PingReply {
pub(crate) last_receive_time_s: f64, pub(crate) last_receive_time_s: f64,
} }
fn open_socket(type_: SocketType) -> RawFd { fn open_socket(type_: SocketType) -> nix::Result<RawFd> {
match type_ { match type_ {
ICMP => { SocketType::ICMP => {
socket( socket(
AddressFamily::Inet, AddressFamily::Inet,
SockType::Raw, SockType::Raw,
@@ -39,7 +41,7 @@ fn open_socket(type_: SocketType) -> RawFd {
SockProtocol::ICMP, SockProtocol::ICMP,
) )
} }
UDP => { SocketType::UDP => {
socket( socket(
AddressFamily::Inet, AddressFamily::Inet,
SockType::Datagram, SockType::Datagram,
@@ -48,7 +50,6 @@ fn open_socket(type_: SocketType) -> RawFd {
) )
} }
} }
.expect("Couldn't open socket")
} }
pub trait PingListener { pub trait PingListener {
@@ -60,8 +61,8 @@ pub trait PingListener {
type_: SocketType, type_: SocketType,
reflectors_lock: Arc<Mutex<Vec<IpAddr>>>, reflectors_lock: Arc<Mutex<Vec<IpAddr>>>,
stats_sender: Sender<PingReply>, stats_sender: Sender<PingReply>,
) { ) -> Result<(), Box<dyn Error>> {
let socket = open_socket(type_); let socket = open_socket(type_)?;
loop { loop {
let mut v: Vec<u8> = vec![0; 40]; let mut v: Vec<u8> = vec![0; 40];
@@ -87,12 +88,15 @@ pub trait PingListener {
let reply = match Self::parse_packet(addr, buf, size) { let reply = match Self::parse_packet(addr, buf, size) {
Ok(val) => val, Ok(val) => val,
Err(e) => { Err(e) => {
println!("Shit went to hell: {}", e.to_string()); error!(
"Something went wrong while parsing packet: {}",
e.to_string()
);
continue; continue;
} }
}; };
println!("Type: {:4} | Reflector IP: {:>15} | Seq: {:5} | Current time: {:8} | Originate: {:8} | Received time: {:8} | Transmit time : {:8} | RTT: {:8} | UL time: {:5} | DL time: {:5}", "ICMP", addr.to_string(), reply.seq, reply.current_time, reply.originate_timestamp, reply.receive_timestamp, reply.transmit_timestamp, reply.rtt, reply.up_time, reply.down_time); //debug!("Type: {:4} | Reflector IP: {:>15} | Seq: {:5} | Current time: {:8} | Originate: {:8} | Received time: {:8} | Transmit time : {:8} | RTT: {:8} | UL time: {:5} | DL time: {:5}", "ICMP", addr.to_string(), reply.seq, reply.current_time, reply.originate_timestamp, reply.receive_timestamp, reply.transmit_timestamp, reply.rtt, reply.up_time, reply.down_time);
stats_sender.send(reply).unwrap(); stats_sender.send(reply).unwrap();
} }
} }
@@ -105,8 +109,12 @@ pub trait PingSender {
fn new(id: u16) -> Self; fn new(id: u16) -> Self;
fn get_id(&self) -> u16; fn get_id(&self) -> u16;
fn send(&mut self, type_: SocketType, reflectors_lock: Arc<Mutex<Vec<IpAddr>>>) { fn send(
let socket = open_socket(type_); &mut self,
type_: SocketType,
reflectors_lock: Arc<Mutex<Vec<IpAddr>>>,
) -> Result<(), Box<dyn Error>> {
let socket = open_socket(type_)?;
let mut seq: u16 = 0; let mut seq: u16 = 0;
let tick_duration_ms: u16 = 500; let tick_duration_ms: u16 = 500;
+3 -13
View File
@@ -1,6 +1,7 @@
use std::error::Error; use std::error::Error;
use std::net::IpAddr; use std::net::IpAddr;
use crate::error::PingParseError;
use crate::pinger::{PingListener, PingReply, PingSender}; use crate::pinger::{PingListener, PingReply, PingSender};
use byteorder::*; use byteorder::*;
use etherparse::TransportSlice::{Icmpv4, Icmpv6}; use etherparse::TransportSlice::{Icmpv4, Icmpv6};
@@ -63,24 +64,13 @@ impl PingListener for PingerICMPEchoListener {
} }
_ => {} _ => {}
}, },
Some(Icmpv6(icmp)) => {} Some(Icmpv6(_)) => {}
Some(_) => {} Some(_) => {}
None => {} None => {}
}, },
} }
Ok(PingReply { Err(Box::new(PingParseError {}))
reflector: IpAddr::from([0, 0, 0, 0]),
seq: 0,
rtt: 0,
current_time: 0,
down_time: 0.0,
up_time: 0.0,
originate_timestamp: 0,
receive_timestamp: 0,
transmit_timestamp: 0,
last_receive_time_s: 0.0,
})
} }
} }
+127 -45
View File
@@ -1,9 +1,12 @@
use crate::netlink::Netlink; use crate::netlink::Netlink;
use crate::{Config, ReflectorStats, Utils}; use crate::{Config, ReflectorStats, Utils};
use log::{debug, error, info, warn};
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use rand::thread_rng; use rand::thread_rng;
use rand::RngCore; use rand::RngCore;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::net::IpAddr; use std::net::IpAddr;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@@ -33,26 +36,26 @@ fn get_interface_stats(
{ {
Ok(stats) => (stats.rx_bytes as i128, stats.tx_bytes as i128), Ok(stats) => (stats.rx_bytes as i128, stats.tx_bytes as i128),
Err(e) => { Err(e) => {
println!("Error while retrieving stats: {}", e.to_string()); error!("Error while retrieving stats: {}", e.to_string());
(-1, -1) (-1, -1)
} }
}; };
let (up_rx, up_tx) = match Netlink::get_interface_stats(config.upload_interface.as_str()) { 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), Ok(stats) => (stats.rx_bytes as i128, stats.tx_bytes as i128),
Err(e) => { Err(e) => {
println!("Error while retrieving stats: {}", e.to_string()); error!("Error while retrieving stats: {}", e.to_string());
(-1, -1) (-1, -1)
} }
}; };
let rx_bytes = match down_direction { let rx_bytes = match down_direction {
RX => down_rx, StatsDirection::RX => down_rx,
TX => down_tx, StatsDirection::TX => down_tx,
}; };
let tx_bytes = match up_direction { let tx_bytes = match up_direction {
RX => up_rx, StatsDirection::RX => up_rx,
TX => up_tx, StatsDirection::TX => up_tx,
}; };
(rx_bytes, tx_bytes) (rx_bytes, tx_bytes)
@@ -79,26 +82,13 @@ impl Ratecontroller {
let mut rng = thread_rng(); let mut rng = thread_rng();
let base_dl_rate = 45000.0; let down_qdisc =
let base_ul_rate = 15000.0; Netlink::qdisc_from_ifname(self.config.download_interface.as_str()).unwrap();
let min_dl_rate = 5000.0; let up_qdisc = Netlink::qdisc_from_ifname(self.config.upload_interface.as_str()).unwrap();
let min_ul_rate = 1000.0;
let histsize = 100;
let dl_max_delta_owd = 15.0;
let ul_max_delta_owd = 15.0;
let high_load_level = 0.8;
let down_ifindex =
Netlink::find_interface(self.config.download_interface.as_str()).unwrap();
let up_ifindex = Netlink::find_interface(self.config.upload_interface.as_str()).unwrap();
let down_qdisc = Netlink::find_qdisc(down_ifindex).unwrap();
let up_qdisc = Netlink::find_qdisc(up_ifindex).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 = base_dl_rate * 0.6; let mut cur_dl_rate: f64 = self.config.download_base_kbits * 0.6;
let mut cur_ul_rate: f64 = base_ul_rate * 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.floor() as u64).unwrap();
Netlink::set_qdisc_rate(up_qdisc, cur_ul_rate.floor() as u64).unwrap(); Netlink::set_qdisc_rate(up_qdisc, cur_ul_rate.floor() as u64).unwrap();
@@ -113,14 +103,56 @@ impl Ratecontroller {
let mut safe_dl_rates: Vec<f64> = Vec::new(); let mut safe_dl_rates: Vec<f64> = Vec::new();
let mut safe_ul_rates: Vec<f64> = Vec::new(); let mut safe_ul_rates: Vec<f64> = Vec::new();
for i in 0..histsize { for _ in 0..self.config.speed_hist_size {
safe_dl_rates.push((rand::thread_rng().next_u32() as f64 * 0.2 + 0.75) * base_dl_rate); safe_dl_rates.push(
safe_ul_rates.push((rand::thread_rng().next_u32() as f64 * 0.2 + 0.75) * base_dl_rate); (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;
let mut speed_hist_fd: Option<File> = None;
let mut speed_hist_fd_inner: File;
let mut stats_fd: Option<File> = None;
let mut stats_fd_inner: File;
if !self.config.suppress_statistics {
speed_hist_fd_inner = File::options()
.create(true)
.write(true)
.open(self.config.speed_hist_file.as_str())
.unwrap();
speed_hist_fd_inner
.write("time,counter,upspeed,downspeed\n".as_bytes())
.unwrap();
speed_hist_fd_inner.flush().unwrap();
speed_hist_fd = Some(speed_hist_fd_inner);
stats_fd_inner = File::options()
.create(true)
.write(true)
.open(self.config.stats_file.as_str())
.unwrap();
stats_fd_inner
.write(
"times,timens,rxload,txload,deltadelaydown,deltadelayup,dlrate,uprate\n"
.as_bytes(),
)
.unwrap();
stats_fd_inner.flush().unwrap();
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;
@@ -128,6 +160,8 @@ impl Ratecontroller {
let now_t = now_s + now_ns / 1e9; let now_t = now_s + now_ns / 1e9;
if now_t - lastchg_t > self.config.min_change_interval { if now_t - lastchg_t > self.config.min_change_interval {
// if it's been long enough, and the stats indicate needing to change speeds
// change speeds here
let owd_baseline = self.owd_baseline.lock().unwrap(); let owd_baseline = self.owd_baseline.lock().unwrap();
let owd_recent = self.owd_recent.lock().unwrap(); let owd_recent = self.owd_recent.lock().unwrap();
let reflectors = self.reflectors_lock.lock().unwrap(); let reflectors = self.reflectors_lock.lock().unwrap();
@@ -135,6 +169,8 @@ impl Ratecontroller {
let mut down_delta_stat: f64; let mut down_delta_stat: f64;
let mut up_delta_stat: f64; let mut up_delta_stat: f64;
// If we have no reflector peers to iterate over, don't attempt any rate changes.
// This will occur under normal operation when the reflector peers table is updated.
if reflectors.len() > 0 { if reflectors.len() > 0 {
let mut down_deltas = Vec::new(); let mut down_deltas = Vec::new();
let mut up_deltas = Vec::new(); let mut up_deltas = Vec::new();
@@ -153,10 +189,11 @@ impl Ratecontroller {
up_delta_stat = 0.0; up_delta_stat = 0.0;
for reflector in reflectors.iter() { for reflector in reflectors.iter() {
// only consider this data if it's less than 2 * tick_duration seconds old
if owd_baseline.contains_key(reflector) if owd_baseline.contains_key(reflector)
&& owd_recent.contains_key(reflector) && owd_recent.contains_key(reflector)
&& owd_recent[reflector].last_receive_time_s && owd_recent[reflector].last_receive_time_s
> now_abstime - 2.0 * self.config.tick_duration > now_abstime - 2.0 * self.config.tick_interval
{ {
down_deltas.push( down_deltas.push(
owd_recent[reflector].down_ewma - owd_baseline[reflector].down_ewma, owd_recent[reflector].down_ewma - owd_baseline[reflector].down_ewma,
@@ -164,11 +201,19 @@ impl Ratecontroller {
up_deltas.push( up_deltas.push(
owd_recent[reflector].up_ewma - owd_baseline[reflector].up_ewma, owd_recent[reflector].up_ewma - owd_baseline[reflector].up_ewma,
); );
debug!(
"Reflector: {} down_delay: {} up_delay: {}",
reflector,
down_deltas.last().unwrap(),
up_deltas.last().unwrap()
);
} }
} }
if down_deltas.len() < 5 || up_deltas.len() < 5 { if down_deltas.len() < 5 || up_deltas.len() < 5 {
// trigger reselection // trigger reselection
warn!("Not enough delta values, triggering reselection");
let _ = self.reselect_trigger.send(true); let _ = self.reselect_trigger.send(true);
} }
@@ -178,10 +223,10 @@ impl Ratecontroller {
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 {
println!("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 {
next_dl_rate = min_dl_rate; next_dl_rate = self.config.download_min_kbits;
next_ul_rate = min_ul_rate; next_ul_rate = self.config.upload_min_kbits;
} else { } else {
down_deltas.sort_by(|a, b| a.partial_cmp(b).unwrap()); down_deltas.sort_by(|a, b| a.partial_cmp(b).unwrap());
up_deltas.sort_by(|a, b| a.partial_cmp(b).unwrap()); up_deltas.sort_by(|a, b| a.partial_cmp(b).unwrap());
@@ -207,34 +252,34 @@ impl Ratecontroller {
next_dl_rate = cur_dl_rate; next_dl_rate = cur_dl_rate;
if down_delta_stat > 0.0 if down_delta_stat > 0.0
&& down_delta_stat < dl_max_delta_owd && down_delta_stat < self.config.download_delay_ms
&& rx_load > high_load_level && rx_load > self.config.high_load_level
{ {
safe_dl_rates[nrate_down] = (cur_dl_rate * rx_load).floor(); safe_dl_rates[nrate_down] = (cur_dl_rate * rx_load).floor();
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 * f64_max(0.0, 1.0 - cur_dl_rate / max_dl))
+ (base_dl_rate * 0.03); + (self.config.download_base_kbits * 0.03);
nrate_down = nrate_down + 1; nrate_down = nrate_down + 1;
nrate_down = nrate_down % histsize; nrate_down = nrate_down % self.config.speed_hist_size as usize;
} }
if up_delta_stat > 0.0 if up_delta_stat > 0.0
&& up_delta_stat < dl_max_delta_owd && up_delta_stat < self.config.download_delay_ms
&& tx_load > high_load_level && tx_load > self.config.high_load_level
{ {
safe_ul_rates[nrate_up] = (cur_ul_rate * tx_load).floor(); safe_ul_rates[nrate_up] = (cur_ul_rate * tx_load).floor();
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 * f64_max(0.0, 1.0 - cur_ul_rate / max_ul))
+ (base_dl_rate * 0.03); + (self.config.download_base_kbits * 0.03);
nrate_up = nrate_up + 1; nrate_up = nrate_up + 1;
nrate_up = nrate_up % histsize; nrate_up = nrate_up % self.config.speed_hist_size as usize;
} }
if down_delta_stat > dl_max_delta_owd { if down_delta_stat > self.config.download_delay_ms {
if safe_dl_rates.len() > 0 { if safe_dl_rates.len() > 0 {
next_dl_rate = f64_min( next_dl_rate = f64_min(
0.9 * cur_dl_rate * rx_load, 0.9 * cur_dl_rate * rx_load,
@@ -245,7 +290,7 @@ impl Ratecontroller {
} }
} }
if up_delta_stat > ul_max_delta_owd { if up_delta_stat > self.config.upload_delay_ms {
if safe_ul_rates.len() > 0 { if safe_ul_rates.len() > 0 {
next_ul_rate = f64_min( next_ul_rate = f64_min(
0.9 * cur_ul_rate * tx_load, 0.9 * cur_ul_rate * tx_load,
@@ -262,11 +307,11 @@ 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(min_dl_rate, next_dl_rate).floor(); next_dl_rate = f64_max(self.config.download_min_kbits, next_dl_rate).floor();
next_ul_rate = f64_max(min_ul_rate, next_ul_rate).floor(); next_ul_rate = f64_max(self.config.upload_min_kbits, next_ul_rate).floor();
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 {
println!( info!(
"next_ul_rate {} next_dl_rate {}", "next_ul_rate {} next_dl_rate {}",
next_ul_rate, next_dl_rate next_ul_rate, next_dl_rate
); );
@@ -290,7 +335,7 @@ impl Ratecontroller {
&& down_delta_stat > 0.0 && down_delta_stat > 0.0
&& up_delta_stat > 0.0 && up_delta_stat > 0.0
{ {
println!( debug!(
"{},{},{},{},{},{},{},{}", "{},{},{},{},{},{},{},{}",
lastchg_s, lastchg_s,
lastchg_ns, lastchg_ns,
@@ -301,6 +346,25 @@ impl Ratecontroller {
cur_dl_rate, cur_dl_rate,
cur_ul_rate cur_ul_rate
); );
if let Some(ref mut fd) = stats_fd {
if let Err(e) = fd.write(
format!(
"{},{},{},{},{},{},{},{}\n",
lastchg_s,
lastchg_ns,
rx_load,
tx_load,
down_delta_stat,
up_delta_stat,
cur_dl_rate,
cur_ul_rate
)
.as_bytes(),
) {
warn!("Failed to write statistics: {}", e);
}
}
} }
lastchg_s = lastchg_s - start_s; lastchg_s = lastchg_s - start_s;
@@ -308,6 +372,24 @@ impl Ratecontroller {
} }
} }
if let Some(ref mut fd) = speed_hist_fd {
if now_t - lastdump_t > 300.0 {
for i in 0..self.config.speed_hist_size as usize {
if let Err(e) = fd.write(
format!(
"{},{},{},{}\n",
now_t, i, safe_ul_rates[i], safe_dl_rates[i]
)
.as_bytes(),
) {
warn!("Failed to write speed history file: {}", e);
}
}
lastdump_t = now_t;
}
}
sleep(sleep_time); sleep(sleep_time);
} }
} }
+13 -10
View File
@@ -1,4 +1,5 @@
use crate::{Config, ReflectorStats}; use crate::{Config, ReflectorStats};
use log::{debug, info};
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use std::collections::HashMap; use std::collections::HashMap;
@@ -10,7 +11,6 @@ use std::time::Duration;
pub struct ReflectorSelector { pub struct ReflectorSelector {
pub(crate) config: Config, pub(crate) config: Config,
pub(crate) owd_baseline: Arc<Mutex<HashMap<IpAddr, ReflectorStats>>>,
pub(crate) owd_recent: Arc<Mutex<HashMap<IpAddr, ReflectorStats>>>, pub(crate) owd_recent: Arc<Mutex<HashMap<IpAddr, ReflectorStats>>>,
pub(crate) reflector_peers_lock: Arc<Mutex<Vec<IpAddr>>>, pub(crate) reflector_peers_lock: Arc<Mutex<Vec<IpAddr>>>,
pub(crate) reflector_pool: Vec<IpAddr>, pub(crate) reflector_pool: Vec<IpAddr>,
@@ -22,8 +22,8 @@ impl ReflectorSelector {
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(
(self.config.tick_duration * std::f64::consts::PI) as u64, (self.config.tick_interval * std::f64::consts::PI) as u64,
(((self.config.tick_duration * std::f64::consts::PI) % 1.0) * 1e9) as u32, (((self.config.tick_interval * std::f64::consts::PI) % 1.0) * 1e9) as u32,
); );
let mut rng = thread_rng(); let mut rng = thread_rng();
@@ -42,6 +42,7 @@ impl ReflectorSelector {
.recv_timeout(selector_sleep_time) .recv_timeout(selector_sleep_time)
.unwrap_or(true); .unwrap_or(true);
reselection_count += 1; reselection_count += 1;
info!("Starting reselection [#{}]", reselection_count);
// After 40 reselections, slow down to every 15 minutes // After 40 reselections, slow down to every 15 minutes
if reselection_count > 40 { if reselection_count > 40 {
@@ -53,11 +54,13 @@ impl ReflectorSelector {
// Include all current peers // Include all current peers
for reflector in reflectors_peers.iter() { for reflector in reflectors_peers.iter() {
debug!("Current peer: {}", reflector.to_string());
next_peers.push(*reflector); next_peers.push(*reflector);
} }
for i in 1..20 { for _ in 1..20 {
let next_candidate = self.reflector_pool.choose(&mut rng).unwrap(); let next_candidate = self.reflector_pool.choose(&mut rng).unwrap();
debug!("Next candidate: {}", next_candidate.to_string());
next_peers.push(*next_candidate); next_peers.push(*next_candidate);
} }
@@ -68,6 +71,7 @@ impl ReflectorSelector {
// until the guard goes out of scope // until the guard goes out of scope
drop(reflectors_peers); drop(reflectors_peers);
debug!("Waiting for candidates to be baselined");
// Wait for several seconds to allow all reflectors to be re-baselined // Wait for several seconds to allow all reflectors to be re-baselined
sleep(baseline_sleep_time); sleep(baseline_sleep_time);
@@ -76,16 +80,15 @@ impl ReflectorSelector {
reflectors_peers.len(); reflectors_peers.len();
let mut candidates = Vec::new(); let mut candidates = Vec::new();
let owd_baseline = self.owd_baseline.lock().unwrap();
let owd_recent = self.owd_recent.lock().unwrap(); let owd_recent = self.owd_recent.lock().unwrap();
for peer in next_peers { for peer in next_peers {
if owd_recent.contains_key(&peer) { if owd_recent.contains_key(&peer) {
let rtt = (owd_recent[&peer].down_ewma + owd_recent[&peer].up_ewma) as u64; let rtt = (owd_recent[&peer].down_ewma + owd_recent[&peer].up_ewma) as u64;
candidates.push((peer, rtt)); candidates.push((peer, rtt));
println!("Candidate reflector: {} RTT: {}", peer.to_string(), rtt); info!("Candidate reflector: {} RTT: {}", peer.to_string(), rtt);
} else { } else {
println!( info!(
"No data found from candidate reflector: {} - skipping", "No data found from candidate reflector: {} - skipping",
peer.to_string() peer.to_string()
); );
@@ -101,7 +104,7 @@ impl ReflectorSelector {
candidates = candidates[0..candidate_pool_num - 1].to_vec(); candidates = candidates[0..candidate_pool_num - 1].to_vec();
for (candidate, rtt) in candidates.iter() { for (candidate, rtt) in candidates.iter() {
println!("Fastest candidate {}: {}", candidate, rtt); info!("Fastest candidate {}: {}", candidate, rtt);
} }
// Shuffle the deck so we avoid overwhelming good reflectors (Fisher-Yates) // Shuffle the deck so we avoid overwhelming good reflectors (Fisher-Yates)
@@ -115,9 +118,9 @@ impl ReflectorSelector {
} }
let mut new_peers = Vec::new(); let mut new_peers = Vec::new();
for i in 1..num_reflectors { for i in 0..num_reflectors {
new_peers.push(candidates[i as usize].0); new_peers.push(candidates[i as usize].0);
println!( info!(
"New selected peer: {}", "New selected peer: {}",
candidates[i as usize].0.to_string() candidates[i as usize].0.to_string()
); );