Skip to content

fix(discovery): announce all participant locators on multi-homed hosts - #35

Merged
SandraK82 merged 6 commits into
mainfrom
fix/issue27-multilocator-discovery
Aug 3, 2026
Merged

fix(discovery): announce all participant locators on multi-homed hosts#35
SandraK82 merged 6 commits into
mainfrom
fix/issue27-multilocator-discovery

Conversation

@SandraK82

Copy link
Copy Markdown
Contributor

Summary

Fixes participant discovery on multi-homed systems where multicast traffic and the OS-selected unicast route use different network interfaces.

Addresses #27. Related to #28.

Root cause

ZeroDDS announced only one participant unicast locator, selected through an OS route probe.

On the reproduced Windows 11 multi-adapter setup:

  • SPDP multicast discovery succeeded;
  • the CycloneDDS participant was discovered;
  • ZeroDDS announced a unicast locator belonging to an isolated interface;
  • CycloneDDS therefore could not reach ZeroDDS for SEDP endpoint discovery;
  • the endpoints never matched.

This explains the observed state where CycloneDDS Insight sees the Cyclone writer but not the ZeroDDS reader.

The problem is independent of XCDR, extensibility, and DataRepresentation.

Changes

  • Enumerate eligible non-loopback IPv4 interfaces deterministically.
  • Announce every eligible participant unicast locator in automatic mode.
  • Preserve ZERODDS_INTERFACE as a deterministic single-interface override.
  • Change participant locator fields from Option<Locator> to Vec<Locator>.
  • Encode and decode repeated locator PIDs, including big-endian parameter lists.
  • Retain all remote participant locators instead of reducing them to one.
  • Fan out metatraffic to every deduplicated usable peer locator.
  • Add multi-locator wire, selection, and fallback regression tests.

Multi-interface multicast transmission is intentionally not part of this change. Packet captures showed that multicast used the correct interface in the reproduced failure; the incorrect announced unicast locator was the failing operation.

Breaking API change

The locator fields in ParticipantBuiltinTopicData now use Vec<Locator> instead of Option<Locator> because the RTPS fields are locator lists and repeated locator PIDs are valid on the wire.

Migration guidance is included in the changelog.

Validation

  • Full GitLab pipeline: 53/53 jobs passed.
  • Windows 11 multi-adapter reproduction:
    • before: discovered=1 matched=0 samples=0
    • after: discovered=1 matched=1 samples=25/26
  • Explicit-interface and single-interface controls pass.
  • CycloneDDS XTypes help(interop): interoperability with CycloneDDS #27 matrix: 5/5.
  • ZeroDDS interoperability self-suite: 48/48.
  • Security discovery capabilities: 9/9.
  • Security discovery tokens: 5/5.
  • CycloneDDS SEDP replay: 3/3.

…overy

Multi-homed hosts can announce an unreachable discovery locator when the
OS default-route probe diverges from the peer's segment (#27). Add
deterministic enumeration of eligible unicast IPv4 interfaces (UP,
multicast-capable, non-loopback; addresses excluding loopback, link-local,
unspecified, broadcast, multicast), de-duplicated and stably ordered by
ip/name/index, for the DCPS announce path to fan out over.

Interface flags via pnet_datalink (already resolved in the workspace lock);
std-gated behind the crate's std feature.
ParticipantBuiltinTopicData's four locator fields change from
Option<Locator> to Vec<Locator> — every field that is semantically a
locator list (DDSI-RTPS 2.5 §9.6.1: a *_LOCATOR PID may repeat). No
parallel compatibility fields.

- Empty list = no locator announced.
- Encode emits one PID per locator, wire order preserved, exact
  duplicates removed.
- Decode retains ALL valid locators for zero/one/repeated PIDs, in LE
  and BE (new Locator::from_bytes_be closes the prior BE-decode gap).
- No routing/usability filtering during decode — that moves to
  DCPS/transport. pick_routable_locator is removed from the decode path;
  locator_looks_routable stays as a pub predicate for DCPS to order the
  retained list.
- Small primary_* helpers and metatraffic_or_default_unicast_locators()
  for call sites; ZERODDS_INTERFACE behaviour is untouched here.

DCPS/discovery send sites are adapted to the new type but keep their
current single-locator behaviour; the announce-all + fan-out change
follows in a separate commit.

Tests: multi-locator round-trip (retain-all, order, LE+BE), dedup on
encode, zero-locator empty lists, locator_looks_routable predicate.
)

Automatic discovery now announces the FULL set of eligible unicast
interface addresses, not just the single default-route probe result.
A peer on any shared segment therefore always learns a reachable
locator — closing the #27 half-completed discovery (participant
discovered, endpoints never matched) on multi-homed hosts.

- announce_locators() enumerates eligible interfaces (Commit 1) at the
  socket's bound port; automatic mode announces all, an explicit
  ZERODDS_INTERFACE keeps its deterministic single-interface behaviour,
  non-UDPv4 and empty-enumeration fall back to the single probe. Never
  announces an empty list.
- Participant SPDP default/metatraffic unicast locators and every SEDP
  endpoint's unicast_locators carry the full set (user_announce_locators).
- Send fan-out: UDP cannot detect a dead destination, so metatraffic is
  sent to EVERY deduplicated usable peer locator (WLP pulse; SEDP/user
  data already fan out via the reader-proxy locator list + send_to_all).

Tests: reliable_writer fans out to every unicast locator of a proxy
(first advertised unreachable, second reachable → the second is
targeted); announce_locators pins to a single interface and is
non-empty + probe-inclusive in automatic mode.
…erral

The #27 interop reader now reports discovered_participants_count
separately from matched/samples, so a half-completed discovery (peer
participant discovered, endpoints never matched) is visible in the
RESULT line — the exact #27 symptom, and the signal the Windows
multi-NIC A/B keys on.

Docs: OPEN-ITEMS records the deferred multi-interface multicast join/TX
hardening (not required for #27 — the capture proved multicast TX/RX was
never the failing operation); the follow-up spells out the gate to
implement it (an independent reproduction where the OS mis-selects the
multicast interface, not just the unicast source probe).
…ink (#27)

pnet_datalink pulls pnet_sys, which links Npcap's Packet.lib — absent on
a stock Windows toolchain, so reader.exe (and the GitHub windows-smoke
CI) fails to link with LNK1181 "cannot open input file Packet.lib".

if-addrs enumerates interfaces via getifaddrs / GetAdaptersAddresses with
no pcap dependency, so it links everywhere. The filter/order/dedup logic
and its tests are unchanged; only the flag source differs. get_if_addrs
returns only operational (UP) interfaces and does not expose the
multicast flag, which is assumed here — the announced set drives unicast
locators, and the multicast-flag-dependent per-interface join/TX is
deferred hardening.
…a locator-list change

Workspace + zerodds-rtps CHANGELOG: the multi-homed discovery fix (#27)
and the breaking Option<Locator> -> Vec<Locator> field change with
migration notes (construction, primary_* accessors, decode filtering
moved to DCPS, no wire-format break).
@SandraK82
SandraK82 merged commit 80663c0 into main Aug 3, 2026
10 checks passed
@SandraK82
SandraK82 deleted the fix/issue27-multilocator-discovery branch August 3, 2026 20:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant