diff --git a/src/rust/lqos_topology/src/lib.rs b/src/rust/lqos_topology/src/lib.rs index b91f368e..a0baabd5 100644 --- a/src/rust/lqos_topology/src/lib.rs +++ b/src/rust/lqos_topology/src/lib.rs @@ -1796,11 +1796,23 @@ pub fn publish_topology_runtime_error_status( } fn parse_probe_ip(raw: &str) -> Option { - raw.trim() - .split('/') - .next() - .filter(|value| !value.is_empty()) - .and_then(|value| value.parse::().ok()) + let raw = raw.trim(); + if raw.is_empty() { + return None; + } + let Some((addr, prefix)) = raw.split_once('/') else { + return raw.parse::().ok(); + }; + if addr.is_empty() || prefix.is_empty() || prefix.contains('/') { + return None; + } + let ip = addr.parse::().ok()?; + let prefix = prefix.parse::().ok()?; + match ip { + IpAddr::V4(_) if prefix <= 32 => Some(ip), + IpAddr::V6(_) if prefix <= 128 => Some(ip), + _ => None, + } } /// Returns the runtime stale cutoff in seconds for topology attachment health. @@ -4597,7 +4609,7 @@ mod tests { apply_health_to_option, auto_attachment_option, build_effective_topology_artifacts, build_effective_topology_artifacts_from_canonical, build_shaping_inputs, collect_direct_circuit_node_ids, collect_direct_circuit_node_names, - compute_effective_state, publish_effective_topology_artifacts, + compute_effective_state, parse_probe_ip, publish_effective_topology_artifacts, publish_topology_runtime_error_status, ranked_auto_attachment_id, validate_effective_topology_network, }; @@ -4629,6 +4641,17 @@ mod tests { path } + #[test] + fn parse_probe_ip_rejects_invalid_cidr_suffixes() { + assert!(parse_probe_ip("192.0.2.1").is_some()); + assert!(parse_probe_ip("192.0.2.1/32").is_some()); + assert!(parse_probe_ip("2001:db8::1/128").is_some()); + assert!(parse_probe_ip("192.0.2.1/not-a-prefix").is_none()); + assert!(parse_probe_ip("192.0.2.1/33").is_none()); + assert!(parse_probe_ip("2001:db8::1/129").is_none()); + assert!(parse_probe_ip("192.0.2.1/24/extra").is_none()); + } + fn apply_effective_topology_to_network_json( config: &Config, canonical_network: &Value, diff --git a/src/rust/lqos_topology/src/runtime/health.rs b/src/rust/lqos_topology/src/runtime/health.rs index 47dc28a4..d8dc9432 100644 --- a/src/rust/lqos_topology/src/runtime/health.rs +++ b/src/rust/lqos_topology/src/runtime/health.rs @@ -7,9 +7,7 @@ use std::collections::HashMap; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; -use crate::{AttachmentProbeSpec, is_health_state_fresh}; - -use super::probe::parse_probe_ip; +use crate::{AttachmentProbeSpec, is_health_state_fresh, parse_probe_ip}; pub(super) fn now_unix() -> Option { lqos_utils::unix_time::unix_now().ok() diff --git a/src/rust/lqos_topology/src/runtime/probe.rs b/src/rust/lqos_topology/src/runtime/probe.rs index 2d624376..13adbbbb 100644 --- a/src/rust/lqos_topology/src/runtime/probe.rs +++ b/src/rust/lqos_topology/src/runtime/probe.rs @@ -2,12 +2,11 @@ use anyhow::Result; use lqos_bus::{BusReply, BusRequest, BusResponse}; use lqos_probe::{ProbeClass, ProbeObservation, ProbeRequest}; use std::collections::HashMap; -use std::net::IpAddr; use std::time::{Duration, Instant}; use tokio::sync::mpsc::error::TrySendError; use tokio::sync::oneshot::error::TryRecvError; -use crate::AttachmentProbeSpec; +use crate::{AttachmentProbeSpec, parse_probe_ip}; use super::TopologyBusSender; @@ -94,14 +93,6 @@ fn bus_call( } } -pub(super) fn parse_probe_ip(raw: &str) -> Option { - raw.trim() - .split('/') - .next() - .filter(|value| !value.is_empty()) - .and_then(|value| value.parse::().ok()) -} - pub(super) fn probe_specs( bus_tx: TopologyBusSender, specs: &[AttachmentProbeSpec],