fix(probe): reject unsupported and undersized reflector sets
Build / build (pull_request) Failing after 4s
DevSkim / DevSkim (pull_request) Failing after 1m26s
rust-clippy analyze / Run rust-clippy analyzing (pull_request) Failing after 1m48s

This commit is contained in:
Nils Andreas Svee committed 2026-09-24 23:11:09 +07:00
1 parent eef16ed23d
commit 33a0ab1503
3 files changed
+38 -12

No files matched your search

+23
View File
@@ -191,6 +191,7 @@ fn restore_shaper<T: TrafficControlBackend>(
fn setup_reflectors(settings: &Settings) -> anyhow::Result<ReflectorSetup> {
let configured = settings.load_reflectors()?;
let configured_count = configured.len();
let requested_count = settings.advanced_settings.num_reflectors as usize;
let default_reflectors = [
IpAddr::from_str("9.9.9.9")?,
@@ -201,6 +202,17 @@ fn setup_reflectors(settings: &Settings) -> anyhow::Result<ReflectorSetup> {
IpAddr::from_str("94.140.14.14")?,
];
let available_count = if configured.is_empty() {
default_reflectors.len()
} else {
configured_count
};
if available_count < requested_count {
anyhow::bail!(
"only {available_count} reflectors available, fewer than the configured {requested_count}"
);
}
let reselection_enabled = configured_count > settings.advanced_settings.num_reflectors as usize;
let (peers, pool) = if reselection_enabled {
(default_reflectors.to_vec(), configured)
@@ -512,6 +524,17 @@ mod tests {
};
let setup = setup_reflectors(&settings).unwrap();
let configured = settings.load_reflectors().unwrap();
std::fs::write(
&path,
"reflector_ip,ip_version,description\n192.0.2.1,4,test\n192.0.2.2,4,test\n192.0.2.3,4,test\n192.0.2.4,4,test\n",
).unwrap();
assert!(setup_reflectors(&settings).is_err());
std::fs::write(
&path,
"reflector_ip,ip_version,description\n2001:db8::1,6,test\n",
)
.unwrap();
assert!(setup_reflectors(&settings).is_err());
std::fs::remove_file(path).unwrap();
assert!(!setup.reselection_enabled);
assert_eq!(*setup.peers.read().unwrap(), configured);
+11 -11
View File
@@ -14,7 +14,7 @@ use icmp_socket2::socket::IcmpSocket;
use icmp_socket2::{IcmpSocket4, Icmpv4Packet};
use log::{debug, info, warn};
use std::collections::HashMap;
use std::net::{IpAddr, Ipv4Addr};
use std::net::IpAddr;
use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex, RwLock};
use std::time::{Duration, Instant};
@@ -185,10 +185,15 @@ pub trait PingSender {
info!("Ping sender shutting down");
return Ok(());
}
// Clone the reflectors vec and drop the read lock immediately —
// holding it for the entire tick would starve the reflector selector's write lock.
// Release the read lock before sending so reselection can update peers.
let reflectors_unlocked = reflectors_lock.read_anyhow()?;
let reflectors = reflectors_unlocked.clone();
let reflectors: Vec<_> = reflectors_unlocked
.iter()
.filter_map(|reflector| match reflector {
IpAddr::V4(addr) => Some(*addr),
IpAddr::V6(_) => None,
})
.collect();
drop(reflectors_unlocked);
if reflectors.is_empty() {
@@ -202,15 +207,10 @@ pub trait PingSender {
let sleep_duration = Duration::from_secs_f64(tick_interval / reflectors.len() as f64);
for reflector in reflectors.iter() {
let addr: Ipv4Addr = match reflector {
IpAddr::V4(ipv4) => *ipv4,
IpAddr::V6(_) => continue,
};
for &addr in &reflectors {
let (packet, originate_timestamp) = self.craft_packet(id, seq);
let sent_at = Instant::now();
let key = (*reflector, type_, seq);
let key = (IpAddr::V4(addr), type_, seq);
inflight.lock_anyhow()?.insert(
key,
InFlightProbe {
+4 -1
View File
@@ -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}");
}