Author SHA1 Message Date
renovate Bot e0a5b1f9f4 chore(deps): update rust crate syn to v3.0.6
Build / build (pull_request) Failing after 4s
DevSkim / DevSkim (pull_request) Failing after 1m20s
rust-clippy analyze / Run rust-clippy analyzing (pull_request) Failing after 1m56s
2026-09-25 10:26:36 +00:00
Nils Andreas Svee 6d4849899e fix(settings): reject zero-floor minimum rates
Build / build (push) Failing after 4s
DevSkim / DevSkim (push) Failing after 1m22s
rust-clippy analyze / Run rust-clippy analyzing (push) Failing after 1m35s
2026-09-25 17:20:08 +07:00
Nils Andreas Svee 7254aa5cf9 fix(uci): propagate malformed optional sources 2026-09-25 17:20:08 +07:00
Nils Andreas Svee 5b0854920d fix(settings): reject IPv6 reflectors 2026-09-25 17:20:07 +07:00
Nils Andreas Svee 4c9fe15201 fix(log): honor configured maximum level 2026-09-25 17:20:07 +07:00
Nils Andreas Svee 5872f40d3e fix(icmp): handle RFC 792 timestamp rollover 2026-09-25 17:20:06 +07:00
renovate Bot 8fb051d48f chore(deps): update rust crate log to v0.4.34
Build / build (pull_request) Successful in 1m7s
DevSkim / DevSkim (pull_request) Failing after 1m25s
Build / build (push) Successful in 1m5s
rust-clippy analyze / Run rust-clippy analyzing (pull_request) Failing after 2m2s
DevSkim / DevSkim (push) Failing after 1m18s
rust-clippy analyze / Run rust-clippy analyzing (push) Failing after 2m7s
2026-08-29 14:29:43 +00:00
renovate Bot a0535357c7 chore(deps): update google/osv-scanner-action action to v2.5.1
Build / build (pull_request) Successful in 1m8s
DevSkim / DevSkim (pull_request) Failing after 1m26s
rust-clippy analyze / Run rust-clippy analyzing (pull_request) Failing after 2m8s
Build / build (push) Successful in 1m7s
DevSkim / DevSkim (push) Failing after 1m22s
rust-clippy analyze / Run rust-clippy analyzing (push) Failing after 1m59s
2026-08-29 14:27:59 +00:00
renovate Bot 91d773c77e chore(deps): update rust crate syn to v3.0.4
Build / build (pull_request) Successful in 1m6s
DevSkim / DevSkim (pull_request) Failing after 1m26s
rust-clippy analyze / Run rust-clippy analyzing (pull_request) Failing after 2m3s
Build / build (push) Successful in 1m6s
DevSkim / DevSkim (push) Failing after 1m22s
rust-clippy analyze / Run rust-clippy analyzing (push) Failing after 1m55s
2026-08-29 14:12:05 +00:00
Lochnair 083d464067 Merge pull request 'chore(deps): update netlink dependencies to v0.3.4' (#76) from renovate/netlink-dependencies into master
Build / build (push) Successful in 1m5s
DevSkim / DevSkim (push) Failing after 1m21s
rust-clippy analyze / Run rust-clippy analyzing (push) Failing after 2m1s
Reviewed-on: #76
2026-08-29 14:11:36 +00:00
renovate Bot f0eae4b2ce chore(deps): update netlink dependencies to v0.3.4
Build / build (pull_request) Successful in 1m4s
DevSkim / DevSkim (pull_request) Failing after 1m32s
rust-clippy analyze / Run rust-clippy analyzing (pull_request) Failing after 2m5s
2026-08-29 14:10:14 +00:00
Lochnair a6067910ad chore: make renovate combine netlink deps
Build / build (push) Successful in 1m4s
DevSkim / DevSkim (push) Failing after 1m18s
rust-clippy analyze / Run rust-clippy analyzing (push) Failing after 1m57s
2026-08-28 09:15:20 +00:00
Lochnair ffd7b0beb7 fix: remove unused import
DevSkim / DevSkim (push) Failing after 4s
Build / build (push) Successful in 57s
rust-clippy analyze / Run rust-clippy analyzing (push) Failing after 2m10s
2026-08-23 17:23:10 +00:00
Lochnair 377fb3be2d chore(ci): install latest rust
DevSkim / DevSkim (push) Failing after 4s
rust-clippy analyze / Run rust-clippy analyzing (push) Failing after 1m42s
Build / build (push) Failing after 50s
2026-08-23 17:12:17 +00:00
Lochnair d765bef002 chore(ci): add workflow_dispatch to build workflow
Build / build (push) Failing after 2s
DevSkim / DevSkim (push) Failing after 4s
rust-clippy analyze / Run rust-clippy analyzing (push) Failing after 5s
2026-08-23 16:50:26 +00:00
renovate[bot] 86b13f948e chore(deps): update rust crate thiserror to v2.0.20 2026-08-14 16:44:07 +00:00
10 changed files with 164 additions and 30 deletions

No files matched your search

+4
View File
@@ -5,6 +5,7 @@ on:
branches: [ "master" ] branches: [ "master" ]
pull_request: pull_request:
branches: [ "master" ] branches: [ "master" ]
workflow_dispatch:
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
@@ -18,5 +19,8 @@ jobs:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- name: Install Mold - name: Install Mold
run: sudo apt install mold run: sudo apt install mold
- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@166cdcfd11aee3cb47222f9ddb555ce30ddb9659 # v1
- name: Build - name: Build
run: cargo build --verbose run: cargo build --verbose
+2 -2
View File
@@ -34,7 +34,7 @@ jobs:
actions: read actions: read
if: ${{ github.event_name == 'push' || github.event_name == 'schedule' }} if: ${{ github.event_name == 'push' || github.event_name == 'schedule' }}
# If you want to copy this config, highly suggest pinning this version to a release rather than tracking the main branch # If you want to copy this config, highly suggest pinning this version to a release rather than tracking the main branch
uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@v2.5.0" uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@v2.5.1"
with: with:
# Just scan the root directory and docs, since everything else is fixtures # Just scan the root directory and docs, since everything else is fixtures
scan-args: |- scan-args: |-
@@ -47,7 +47,7 @@ jobs:
actions: read actions: read
if: ${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }} if: ${{ github.event_name == 'pull_request' || github.event_name == 'merge_group' }}
# If you want to copy this config, highly suggest pinning this version to a release rather than tracking the main branch # If you want to copy this config, highly suggest pinning this version to a release rather than tracking the main branch
uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml@v2.5.0" uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml@v2.5.1"
with: with:
# Just scan the root directory and docs, since everything else is fixtures # Just scan the root directory and docs, since everything else is fixtures
scan-args: |- scan-args: |-
Generated
+15 -15
View File
@@ -286,9 +286,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.33" version = "0.4.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
[[package]] [[package]]
name = "maybe-async" name = "maybe-async"
@@ -315,15 +315,15 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]] [[package]]
name = "netlink-bindings" name = "netlink-bindings"
version = "0.3.3" version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "114816d03a633cef7cfa951721dafbda16bb1ff6af83393ee568ec2d4e4ded2d" checksum = "2a2a0dd8beef757878b977fd3d662c82a93dac4bf7632f42577ca8f8f4d701a0"
[[package]] [[package]]
name = "netlink-socket2" name = "netlink-socket2"
version = "0.3.3" version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11193969f046efafdc9d8670d395a6ddcf9b806169f504e520a35dc95cab3241" checksum = "5c7ddb0ea7aa692947d5105160acb7c75c17bc5ec6d5a6263440ca7db4be9a34"
dependencies = [ dependencies = [
"libc", "libc",
"maybe-async", "maybe-async",
@@ -490,7 +490,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.6",
] ]
[[package]] [[package]]
@@ -580,9 +580,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "3.0.3" version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -606,22 +606,22 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "2.0.19" version = "2.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "2.0.19" version = "2.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.6",
] ]
[[package]] [[package]]
@@ -684,7 +684,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.6",
"trybuild", "trybuild",
] ]
+14 -1
View File
@@ -2,5 +2,18 @@
"$schema": "https://docs.renovatebot.com/renovate-schema.json", "$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [ "extends": [
"config:recommended" "config:recommended"
],
"packageRules": [
{
"description": "Keep netlink-bindings and netlink-socket2 updates together",
"matchManagers": [
"cargo"
],
"matchPackageNames": [
"netlink-bindings",
"netlink-socket2"
],
"groupName": "netlink dependencies"
}
] ]
} }
+2 -2
View File
@@ -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()))
} }
+1 -1
View File
@@ -30,7 +30,7 @@ use crate::ratecontroller::Ratecontroller;
use crate::reflector_selector::ReflectorSelector; use crate::reflector_selector::ReflectorSelector;
use crate::settings::MeasurementType; use crate::settings::MeasurementType;
use crate::settings::Settings; use crate::settings::Settings;
use ::log::{debug, info, warn}; use ::log::{info, warn};
use flume::RecvTimeoutError; use flume::RecvTimeoutError;
use std::collections::HashMap; use std::collections::HashMap;
use std::net::IpAddr; use std::net::IpAddr;
+48 -5
View File
@@ -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(_))));
}
}
+27 -1
View File
@@ -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
View File
@@ -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}");
} }
+47 -2
View File
@@ -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());
}
}