fix(probe): reject unsupported and undersized reflector sets
This commit is contained in:
3 files changed
+38
-12
No files matched your search
+23
@@ -191,6 +191,7 @@ fn restore_shaper<T: TrafficControlBackend>(
|
|||||||
fn setup_reflectors(settings: &Settings) -> anyhow::Result<ReflectorSetup> {
|
fn setup_reflectors(settings: &Settings) -> anyhow::Result<ReflectorSetup> {
|
||||||
let configured = settings.load_reflectors()?;
|
let configured = settings.load_reflectors()?;
|
||||||
let configured_count = configured.len();
|
let configured_count = configured.len();
|
||||||
|
let requested_count = settings.advanced_settings.num_reflectors as usize;
|
||||||
|
|
||||||
let default_reflectors = [
|
let default_reflectors = [
|
||||||
IpAddr::from_str("9.9.9.9")?,
|
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")?,
|
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 reselection_enabled = configured_count > settings.advanced_settings.num_reflectors as usize;
|
||||||
let (peers, pool) = if reselection_enabled {
|
let (peers, pool) = if reselection_enabled {
|
||||||
(default_reflectors.to_vec(), configured)
|
(default_reflectors.to_vec(), configured)
|
||||||
@@ -512,6 +524,17 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let setup = setup_reflectors(&settings).unwrap();
|
let setup = setup_reflectors(&settings).unwrap();
|
||||||
let configured = settings.load_reflectors().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();
|
std::fs::remove_file(path).unwrap();
|
||||||
assert!(!setup.reselection_enabled);
|
assert!(!setup.reselection_enabled);
|
||||||
assert_eq!(*setup.peers.read().unwrap(), configured);
|
assert_eq!(*setup.peers.read().unwrap(), configured);
|
||||||
|
|||||||
+11
-11
@@ -14,7 +14,7 @@ use icmp_socket2::socket::IcmpSocket;
|
|||||||
use icmp_socket2::{IcmpSocket4, Icmpv4Packet};
|
use icmp_socket2::{IcmpSocket4, Icmpv4Packet};
|
||||||
use log::{debug, info, warn};
|
use log::{debug, info, warn};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::net::{IpAddr, Ipv4Addr};
|
use std::net::IpAddr;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
@@ -185,10 +185,15 @@ pub trait PingSender {
|
|||||||
info!("Ping sender shutting down");
|
info!("Ping sender shutting down");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
// Clone the reflectors vec and drop the read lock immediately —
|
// Release the read lock before sending so reselection can update peers.
|
||||||
// holding it for the entire tick would starve the reflector selector's write lock.
|
|
||||||
let reflectors_unlocked = reflectors_lock.read_anyhow()?;
|
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);
|
drop(reflectors_unlocked);
|
||||||
|
|
||||||
if reflectors.is_empty() {
|
if reflectors.is_empty() {
|
||||||
@@ -202,15 +207,10 @@ pub trait PingSender {
|
|||||||
|
|
||||||
let sleep_duration = Duration::from_secs_f64(tick_interval / reflectors.len() as f64);
|
let sleep_duration = Duration::from_secs_f64(tick_interval / reflectors.len() as f64);
|
||||||
|
|
||||||
for reflector in reflectors.iter() {
|
for &addr in &reflectors {
|
||||||
let addr: Ipv4Addr = match reflector {
|
|
||||||
IpAddr::V4(ipv4) => *ipv4,
|
|
||||||
IpAddr::V6(_) => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
let (packet, originate_timestamp) = self.craft_packet(id, seq);
|
let (packet, originate_timestamp) = self.craft_packet(id, seq);
|
||||||
let sent_at = Instant::now();
|
let sent_at = Instant::now();
|
||||||
let key = (*reflector, type_, seq);
|
let key = (IpAddr::V4(addr), type_, seq);
|
||||||
inflight.lock_anyhow()?.insert(
|
inflight.lock_anyhow()?.insert(
|
||||||
key,
|
key,
|
||||||
InFlightProbe {
|
InFlightProbe {
|
||||||
|
|||||||
+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}");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user