Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c49ad3838d | ||
|
|
6d4849899e | ||
|
|
7254aa5cf9 | ||
|
|
5b0854920d | ||
|
|
4c9fe15201 | ||
|
|
5872f40d3e |
No files matched your search
+2
-2
@@ -2,7 +2,7 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
use log::{Level, LevelFilter, Metadata, Record, SetLoggerError};
|
use log::{Level, Metadata, Record, SetLoggerError};
|
||||||
use rustix::time::ClockId;
|
use rustix::time::ClockId;
|
||||||
|
|
||||||
use crate::time::Time;
|
use crate::time::Time;
|
||||||
@@ -36,5 +36,5 @@ impl log::Log for SimpleLogger {
|
|||||||
|
|
||||||
pub fn init(level: Level) -> Result<(), SetLoggerError> {
|
pub fn init(level: Level) -> Result<(), SetLoggerError> {
|
||||||
log::set_boxed_logger(Box::new(SimpleLogger { level }))
|
log::set_boxed_logger(Box::new(SimpleLogger { level }))
|
||||||
.map(|()| log::set_max_level(LevelFilter::Trace))
|
.map(|()| log::set_max_level(level.to_level_filter()))
|
||||||
}
|
}
|
||||||
+48
-5
@@ -19,6 +19,19 @@ pub struct PingerICMPTimestampListener {}
|
|||||||
|
|
||||||
pub struct PingerICMPTimestampSender {}
|
pub struct PingerICMPTimestampSender {}
|
||||||
|
|
||||||
|
const MS_PER_DAY: i64 = 86_400_000;
|
||||||
|
|
||||||
|
fn timestamp_delta(later: i64, earlier: i64) -> i64 {
|
||||||
|
let delta = later - earlier;
|
||||||
|
if delta < -MS_PER_DAY / 2 {
|
||||||
|
delta + MS_PER_DAY
|
||||||
|
} else if delta > MS_PER_DAY / 2 {
|
||||||
|
delta - MS_PER_DAY
|
||||||
|
} else {
|
||||||
|
delta
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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(
|
||||||
@@ -46,12 +59,18 @@ impl PingListener for PingerICMPTimestampListener {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let time_now = Time::new(ClockId::Realtime);
|
if receive & 0x8000_0000 != 0 || transmit & 0x8000_0000 != 0 {
|
||||||
let time_since_midnight = time_now.get_time_since_midnight();
|
return Err(PingError::InvalidPacket(
|
||||||
|
"non-standard RFC 792 timestamp".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let rtt = (time_since_midnight - originate as i64) as f64;
|
let time_since_midnight =
|
||||||
let dl_time = (time_since_midnight - transmit as i64) as f64;
|
Time::new(ClockId::Realtime).get_time_since_midnight();
|
||||||
let ul_time = (receive as i64 - originate as i64) as f64;
|
|
||||||
|
let rtt = timestamp_delta(time_since_midnight, originate as i64) as f64;
|
||||||
|
let dl_time = timestamp_delta(time_since_midnight, transmit as i64) as f64;
|
||||||
|
let ul_time = timestamp_delta(receive as i64, originate as i64) as f64;
|
||||||
|
|
||||||
Ok(PingReply {
|
Ok(PingReply {
|
||||||
reflector,
|
reflector,
|
||||||
@@ -88,3 +107,27 @@ impl PingSender for PingerICMPTimestampSender {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use icmp_socket2::packet::WithTimestampReply;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn timestamps_wrap_at_midnight_in_both_directions() {
|
||||||
|
assert_eq!(timestamp_delta(20, 86_399_900), 120);
|
||||||
|
assert_eq!(timestamp_delta(86_399_900, 20), -120);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn non_standard_rfc_792_timestamps_are_rejected() {
|
||||||
|
let packet = Icmpv4Packet::with_timestamp_reply(7, 1, 10, 0x8000_0001, 20).unwrap();
|
||||||
|
let result = PingerICMPTimestampListener {}.parse_packet(
|
||||||
|
7,
|
||||||
|
"192.0.2.1".parse().unwrap(),
|
||||||
|
MeasurementType::IcmpTimestamps,
|
||||||
|
packet,
|
||||||
|
);
|
||||||
|
assert!(matches!(result, Err(PingError::InvalidPacket(_))));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -262,7 +262,7 @@ impl Source for UciSource {
|
|||||||
let (_uci, packages) = match resolved {
|
let (_uci, packages) = match resolved {
|
||||||
Ok(resolved) => resolved,
|
Ok(resolved) => resolved,
|
||||||
|
|
||||||
Err(_) if !self.required => {
|
Err(SourceError::NotFound(_)) if !self.required => {
|
||||||
return Ok(Map::new());
|
return Ok(Map::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,3 +285,29 @@ impl Source for UciSource {
|
|||||||
Ok(values)
|
Ok(values)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::settings::Settings;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn malformed_optional_package_is_an_error() {
|
||||||
|
let root = std::env::temp_dir().join(format!("sqma-uci-{}", std::process::id()));
|
||||||
|
let config_dir = root.join("config");
|
||||||
|
let save_dir = root.join("save");
|
||||||
|
fs::create_dir_all(&config_dir).unwrap();
|
||||||
|
fs::create_dir_all(&save_dir).unwrap();
|
||||||
|
let config_file = config_dir.join("sqm-autorate-rust");
|
||||||
|
fs::write(&config_file, "config network 'unterminated\n").unwrap();
|
||||||
|
|
||||||
|
let source = UciSource::from_schema::<Settings>()
|
||||||
|
.with_directories(&config_dir, &save_dir)
|
||||||
|
.required(false);
|
||||||
|
let result = source.collect();
|
||||||
|
|
||||||
|
fs::remove_dir_all(root).unwrap();
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-1
@@ -192,7 +192,10 @@ impl Settings {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
match (record.ip_version, record.reflector_ip) {
|
match (record.ip_version, record.reflector_ip) {
|
||||||
(4, IpAddr::V4(_)) | (6, IpAddr::V6(_)) => {}
|
(4, IpAddr::V4(_)) => {}
|
||||||
|
(6, address @ IpAddr::V6(_)) => {
|
||||||
|
bail!("reflector {address} is IPv6; only IPv4 reflectors are supported");
|
||||||
|
}
|
||||||
(version, address) => {
|
(version, address) => {
|
||||||
bail!("reflector {address} has inconsistent IP version {version}");
|
bail!("reflector {address} has inconsistent IP version {version}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,7 +153,12 @@ impl NetworkSettings {
|
|||||||
if download_base_valid && download_percent_valid {
|
if download_base_valid && download_percent_valid {
|
||||||
let minimum = self.download_min_kbits();
|
let minimum = self.download_min_kbits();
|
||||||
|
|
||||||
if minimum < WARN_DOWNLOAD_MIN_KBITS {
|
if minimum < 1.0 {
|
||||||
|
report.error(
|
||||||
|
"network.download_min_percent",
|
||||||
|
"minimum download rate floors to zero",
|
||||||
|
);
|
||||||
|
} else if minimum < WARN_DOWNLOAD_MIN_KBITS {
|
||||||
report.warning(
|
report.warning(
|
||||||
"network.download_min_percent",
|
"network.download_min_percent",
|
||||||
format!(
|
format!(
|
||||||
@@ -168,7 +173,12 @@ impl NetworkSettings {
|
|||||||
if upload_base_valid && upload_percent_valid {
|
if upload_base_valid && upload_percent_valid {
|
||||||
let minimum = self.upload_min_kbits();
|
let minimum = self.upload_min_kbits();
|
||||||
|
|
||||||
if minimum < WARN_UPLOAD_MIN_KBITS {
|
if minimum < 1.0 {
|
||||||
|
report.error(
|
||||||
|
"network.upload_min_percent",
|
||||||
|
"minimum upload rate floors to zero",
|
||||||
|
);
|
||||||
|
} else if minimum < WARN_UPLOAD_MIN_KBITS {
|
||||||
report.warning(
|
report.warning(
|
||||||
"network.upload_min_percent",
|
"network.upload_min_percent",
|
||||||
format!(
|
format!(
|
||||||
@@ -376,3 +386,38 @@ fn validate_percent(report: &mut ValidationReport, path: &'static str, value: f6
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn network(download_base_kbits: f64, upload_base_kbits: f64) -> NetworkSettings {
|
||||||
|
NetworkSettings {
|
||||||
|
download_interface: "ifb0".into(),
|
||||||
|
upload_interface: "eth0".into(),
|
||||||
|
download_base_kbits,
|
||||||
|
download_min_percent: 1.0,
|
||||||
|
upload_base_kbits,
|
||||||
|
upload_min_percent: 1.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn minimum_rates_that_floor_to_zero_are_errors_for_both_directions() {
|
||||||
|
let mut report = ValidationReport::default();
|
||||||
|
network(50.0, 50.0).validate(&mut report);
|
||||||
|
let error = report.finish().unwrap_err().to_string();
|
||||||
|
|
||||||
|
assert!(error.contains("network.download_min_percent"));
|
||||||
|
assert!(error.contains("network.upload_min_percent"));
|
||||||
|
assert!(error.contains("floors to zero"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn representable_low_minimum_rates_remain_warnings() {
|
||||||
|
let mut report = ValidationReport::default();
|
||||||
|
network(10_000.0, 10_000.0).validate(&mut report);
|
||||||
|
|
||||||
|
assert!(report.finish().is_ok());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user