Skip to content

Commit fa6e9d3

Browse files
committed
Fix: Add namespace isolation to prevent cross-namespace route creation
When a bridge is configured with a non-root namespace (e.g., "/bot1"), it was incorrectly creating local DDS entities for remote announcements from different namespaces (e.g., "bot2/chatter"). This caused topics to appear in `ros2 topic list` but fail when subscribed, due to Zenoh key expression mismatch during data routing. This fix adds namespace validation in all Announced event handlers: - AnnouncedMsgPub/Sub - AnnouncedServiceSrv/Cli - AnnouncedActionSrv/Cli New behavior: - Bridges with namespace="/bot1" only handle routes for "bot1/*" keys - Bridges with namespace="/" (default) accept all routes (backward compatible) - Remote announcements from other namespaces are ignored with debug logging This enforces proper namespace isolation, ensuring bridges only create routes for their own namespace scope.
1 parent 881e90e commit fa6e9d3

2 files changed

Lines changed: 74 additions & 1 deletion

File tree

zenoh-plugin-ros2dds/src/ros2_utils.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,19 @@ pub fn ros2_name_to_key_expr(ros2_name: &str, config: &Config) -> OwnedKeyExpr {
111111
}
112112
}
113113

114+
/// Check if a Zenoh key expression belongs to this bridge's namespace.
115+
/// Returns true if:
116+
/// - namespace is "/" (default, accepts all)
117+
/// - key_expr starts with the configured namespace prefix
118+
pub fn is_in_namespace(key_expr: &keyexpr, config: &Config) -> bool {
119+
if config.namespace == "/" {
120+
true // Default namespace accepts everything
121+
} else {
122+
// Check if key_expr starts with namespace prefix (without leading '/')
123+
key_expr.as_str().starts_with(&config.namespace[1..])
124+
}
125+
}
126+
114127
/// Convert a Zenoh key expression to a ROS2 full interface name,
115128
/// removing "namespace" prefix if configured and present in the key expr
116129
pub fn key_expr_to_ros2_name(key_expr: &keyexpr, config: &Config) -> String {

zenoh-plugin-ros2dds/src/routes_mgr.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use crate::{
3434
discovered_entities::DiscoveredEntities,
3535
events::{ROS2AnnouncementEvent, ROS2DiscoveryEvent},
3636
qos_helpers::{adapt_reader_qos_for_writer, adapt_writer_qos_for_reader},
37-
ros2_utils::{key_expr_to_ros2_name, ros2_name_to_key_expr},
37+
ros2_utils::{is_in_namespace, key_expr_to_ros2_name, ros2_name_to_key_expr},
3838
ros_discovery::RosDiscoveryInfoMgr,
3939
route_action_cli::RouteActionCli,
4040
route_action_srv::RouteActionSrv,
@@ -327,6 +327,16 @@ impl RoutesMgr {
327327
keyless,
328328
writer_qos,
329329
} => {
330+
// Only handle announcements from our namespace (unless using default "/")
331+
if !is_in_namespace(&zenoh_key_expr, &self.context.config) {
332+
tracing::debug!(
333+
"Ignoring remote Publisher {} - not in namespace {}",
334+
zenoh_key_expr,
335+
self.context.config.namespace
336+
);
337+
return Ok(());
338+
}
339+
330340
let mut qos = writer_qos.clone();
331341
qos.ignore_local = Some(IgnoreLocal {
332342
kind: cyclors::qos::IgnoreLocalKind::PARTICIPANT,
@@ -370,6 +380,16 @@ impl RoutesMgr {
370380
keyless,
371381
reader_qos,
372382
} => {
383+
// Only handle announcements from our namespace (unless using default "/")
384+
if !is_in_namespace(&zenoh_key_expr, &self.context.config) {
385+
tracing::debug!(
386+
"Ignoring remote Subscriber {} - not in namespace {}",
387+
zenoh_key_expr,
388+
self.context.config.namespace
389+
);
390+
return Ok(());
391+
}
392+
373393
let mut qos = reader_qos.clone();
374394
qos.ignore_local = Some(IgnoreLocal {
375395
kind: cyclors::qos::IgnoreLocalKind::PARTICIPANT,
@@ -411,6 +431,16 @@ impl RoutesMgr {
411431
zenoh_key_expr,
412432
ros2_type,
413433
} => {
434+
// Only handle announcements from our namespace (unless using default "/")
435+
if !is_in_namespace(&zenoh_key_expr, &self.context.config) {
436+
tracing::debug!(
437+
"Ignoring remote Service Server {} - not in namespace {}",
438+
zenoh_key_expr,
439+
self.context.config.namespace
440+
);
441+
return Ok(());
442+
}
443+
414444
// On remote Service Server route announcement, prepare a Service Client route
415445
// with a associated DDS Reader/Writer allowing local ROS2 Nodes to discover it
416446
let route = self
@@ -446,6 +476,16 @@ impl RoutesMgr {
446476
zenoh_key_expr,
447477
ros2_type,
448478
} => {
479+
// Only handle announcements from our namespace (unless using default "/")
480+
if !is_in_namespace(&zenoh_key_expr, &self.context.config) {
481+
tracing::debug!(
482+
"Ignoring remote Service Client {} - not in namespace {}",
483+
zenoh_key_expr,
484+
self.context.config.namespace
485+
);
486+
return Ok(());
487+
}
488+
449489
// On remote Service Client route announcement, prepare a Service Server route
450490
// with a associated DDS Reader/Writer allowing local ROS2 Nodes to discover it
451491
let route = self
@@ -481,6 +521,16 @@ impl RoutesMgr {
481521
zenoh_key_expr,
482522
ros2_type,
483523
} => {
524+
// Only handle announcements from our namespace (unless using default "/")
525+
if !is_in_namespace(&zenoh_key_expr, &self.context.config) {
526+
tracing::debug!(
527+
"Ignoring remote Action Server {} - not in namespace {}",
528+
zenoh_key_expr,
529+
self.context.config.namespace
530+
);
531+
return Ok(());
532+
}
533+
484534
// On remote Action Server route announcement, prepare a Action Client route
485535
// with a associated DDS Reader/Writer allowing local ROS2 Nodes to discover it
486536
let route = self
@@ -515,6 +565,16 @@ impl RoutesMgr {
515565
zenoh_key_expr,
516566
ros2_type,
517567
} => {
568+
// Only handle announcements from our namespace (unless using default "/")
569+
if !is_in_namespace(&zenoh_key_expr, &self.context.config) {
570+
tracing::debug!(
571+
"Ignoring remote Action Client {} - not in namespace {}",
572+
zenoh_key_expr,
573+
self.context.config.namespace
574+
);
575+
return Ok(());
576+
}
577+
518578
// On remote Action Client route announcement, prepare a Action Server route
519579
// with a associated DDS Reader/Writer allowing local ROS2 Nodes to discover it
520580
let route = self

0 commit comments

Comments
 (0)