Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a1411fd27 | ||
|
|
6d4849899e | ||
|
|
7254aa5cf9 | ||
|
|
5b0854920d | ||
|
|
4c9fe15201 | ||
|
|
5872f40d3e |
No files matched your search
+2
-2
@@ -2,7 +2,7 @@
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use log::{Level, LevelFilter, Metadata, Record, SetLoggerError};
|
||||
use log::{Level, Metadata, Record, SetLoggerError};
|
||||
use rustix::time::ClockId;
|
||||
|
||||
use crate::time::Time;
|
||||
@@ -36,5 +36,5 @@ impl log::Log for SimpleLogger {
|
||||
|
||||
pub fn init(level: Level) -> Result<(), SetLoggerError> {
|
||||
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 {}
|
||||
|
||||
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 {
|
||||
// Result: RTT, down time, up time
|
||||
fn parse_packet(
|
||||
@@ -46,12 +59,18 @@ impl PingListener for PingerICMPTimestampListener {
|
||||
});
|
||||
}
|
||||
|
||||
let time_now = Time::new(ClockId::Realtime);
|
||||
let time_since_midnight = time_now.get_time_since_midnight();
|
||||
if receive & 0x8000_0000 != 0 || transmit & 0x8000_0000 != 0 {
|
||||
return Err(PingError::InvalidPacket(
|
||||
"non-standard RFC 792 timestamp".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let rtt = (time_since_midnight - originate as i64) as f64;
|
||||
let dl_time = (time_since_midnight - transmit as i64) as f64;
|
||||
let ul_time = (receive as i64 - originate as i64) as f64;
|
||||
let time_since_midnight =
|
||||
Time::new(ClockId::Realtime).get_time_since_midnight();
|
||||
|
||||
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 {
|
||||
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 {
|
||||
Ok(resolved) => resolved,
|
||||
|
||||
Err(_) if !self.required => {
|
||||
Err(SourceError::NotFound(_)) if !self.required => {
|
||||
return Ok(Map::new());
|
||||
}
|
||||
|
||||
@@ -285,3 +285,29 @@ impl Source for UciSource {
|
||||
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) {
|
||||
(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) => {
|
||||
bail!("reflector {address} has inconsistent IP version {version}");
|
||||
}
|
||||
|
||||
@@ -153,7 +153,12 @@ impl NetworkSettings {
|
||||
if download_base_valid && download_percent_valid {
|
||||
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(
|
||||
"network.download_min_percent",
|
||||
format!(
|
||||
@@ -168,7 +173,12 @@ impl NetworkSettings {
|
||||
if upload_base_valid && upload_percent_valid {
|
||||
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(
|
||||
"network.upload_min_percent",
|
||||
format!(
|
||||
@@ -376,3 +386,38 @@ fn validate_percent(report: &mut ValidationReport, path: &'static str, value: f6
|
||||
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