ZeroDDS UDS Transport 1.0 — Spec Coverage
Source: this document is also the spec. For Unix domain sockets OMG
standardizes nothing beyond the vendor-reserved locator kind
(DDSI-RTPS 2.5 §9.4.5); path resolution, wire format and cleanup are
vendor-specific. Cyclone DDS and FastDDS have no official UDS transport.
The normative requirements live in the ## sections §2 through §6; every
### item below carries a spec citation, a repo anchor, a test and a
status.
Implementation: crates/transport-uds/src/lib.rs — SOCK_DGRAM over
filesystem sockets; crates/transport-uds/src/abstract_dgram.rs — the
Linux abstract namespace, #[cfg(target_os = "linux")].
Open items: zerodds-uds-transport-1.0.open.en.md.
§1 Scope and spec status
§1.1 Vendor-reserved locator kind
Spec: DDSI-RTPS 2.5 §9.3.2.4 — 0x00000003 to 0x01ffffff are reserved
for vendor-specific locator kinds. ZeroDDS takes 0x01F0_0002 for UDS. The port
field is meaningless and MUST be 0; the 16 address bytes carry the
endpoint id.
Repo: crates/rtps/src/wire_types.rs (LocatorKind::Uds,
Locator::uds); bound in crates/transport-uds/src/lib.rs
(UdsTransport::local_locator, the kind check in Transport::send).
Tests: crates/transport-uds/src/lib.rs::tests::locator_kind_is_the_vendor_reserved_uds_value
(asserts the numeric value 0x01F0_0002, the position in the range 0x00000003 to 0x01ffffff and
port == 0),
crates/transport-uds/src/lib.rs::tests::local_locator_reflects_bind_id,
crates/transport-uds/src/lib.rs::tests::send_rejects_non_uds_locator,
crates/rtps/src/wire_types.rs::tests::locator_uds_layout,
crates/rtps/src/wire_types.rs::tests::locator_kind_roundtrip.
Status: done
§1.2 What OMG does not standardize for UDS
Spec: DDSI-RTPS 2.5 knows no path scheme, no UDS wire format and no cleanup protocol. Cyclone DDS and FastDDS have no official UDS transport; they cover the same use case through iceoryx or POSIX SHM. The section records the scope and states no implementation requirement.
Repo: —
Tests: —
Status: n/a (informative) — scope statement; the requirements are the items from §2 on.
§2 Path resolution
§2.1 Filesystem mode (default)
Spec: §2 — the 16-byte locator address resolves to
<base_dir>/<lowercase-hex32>.sock. The default base_dir is
/tmp/zerodds/uds. The hex string is the address in 32 lowercase
characters, without separators.
Repo: crates/transport-uds/src/lib.rs (socket_path,
DEFAULT_BASE_DIR, parse_hex_id, source_locator as the reverse
direction).
Tests: crates/transport-uds/src/lib.rs::tests::socket_path_is_hex_under_base_dir
(checks prefix and .sock suffix on an address with edge bytes set),
crates/transport-uds/src/lib.rs::tests::parse_hex_id_roundtrip,
crates/transport-uds/src/lib.rs::tests::parse_hex_id_rejects_wrong_length,
crates/transport-uds/src/lib.rs::tests::parse_hex_id_rejects_non_hex.
Status: done
§2.2 Base directory: lazy, user-private, TOCTOU-safe
Spec: §2 — the base directory is created on bind when it is missing
and then carries mode 0700. An existing path that is a symlink or not a
directory MUST be refused rather than chmodded — otherwise a planted
symlink could set foreign paths to 0700. No mode is set on an existing
foreign directory.
Repo: crates/transport-uds/src/lib.rs (ensure_base_dir —
symlink_metadata instead of exists, refusal of symlink and
non-directory, create_dir_all plus set_permissions(0o700) with a
re-check).
Tests: crates/transport-uds/src/lib.rs::tests::ensure_base_dir_creates_dir_with_mode_0700
(reads the mode back and compares it against 0o700),
crates/transport-uds/src/lib.rs::tests::ensure_base_dir_rejects_symlink,
crates/transport-uds/src/lib.rs::tests::ensure_base_dir_rejects_file_path,
crates/transport-uds/src/lib.rs::tests::ensure_base_dir_creates_missing_dir.
Status: done
§2.3 Abstract-namespace mode (Linux)
Spec: §2 — on Linux there is the alternative without a filesystem
inode: the socket name starts with a null byte, followed by
<prefix>-<lowercase-hex32>. No cleanup needed, no inode permissions, no
cross-mount volume. The name fits into sun_path (108 bytes, byte 0 is
the null prefix).
Repo: crates/transport-uds/src/abstract_dgram.rs
(UdsAbstractDgramTransport, UdsAddress::Abstract,
MAX_ABSTRACT_NAME = 107, UdsAddress::abstract_default); the module is
declared in crates/transport-uds/src/lib.rs under
#[cfg(target_os = "linux")].
Tests: crates/transport-uds/src/abstract_dgram.rs::tests::abstract_bind_does_not_create_file,
crates/transport-uds/src/abstract_dgram.rs::tests::abstract_send_recv_roundtrip,
crates/transport-uds/src/abstract_dgram.rs::tests::abstract_preserves_message_boundaries,
crates/transport-uds/src/abstract_dgram.rs::tests::local_locator_reflects_bind_id.
These tests run 0 cases on macOS: the module sits behind
#[cfg(target_os = "linux")] and is not compiled there. They were
measured on the Linux test host against the same, unchanged
abstract_dgram.rs (command cargo test -p zerodds-transport-uds) — 11
cases from abstract_dgram::tests green, 0 failed.
Status: done
§2.4 Filesystem mode inside the abstract transport (A/B comparison)
Spec: §2 — the same transport MUST also do filesystem addressing so abstract and filesystem mode can be measured against each other with identical socket code. The difference is one config line.
Repo: crates/transport-uds/src/abstract_dgram.rs
(UdsAddress::Filesystem, UdsAddress::filesystem_default, fs_path).
Tests: crates/transport-uds/src/abstract_dgram.rs::tests::filesystem_bind_creates_socket_file
(the socket file appears on bind and is gone after drop),
crates/transport-uds/src/abstract_dgram.rs::tests::filesystem_send_recv_roundtrip.
These tests too are #[cfg(target_os = "linux")]-gated and run 0 cases
on macOS; measured on the Linux test host in the same run as §2.3.
Status: done
§3 Wire format
§3.1 SOCK_DGRAM with preserved message boundaries
Spec: §3 — one datagram message per RTPS frame. The kernel preserves the message boundaries, unlike a TCP stream; no framing layer on top is needed. Two sends MUST arrive as two receives with the original lengths.
Repo: crates/transport-uds/src/lib.rs (UdsTransport over
std::os::unix::net::UnixDatagram, Transport::send with send_to,
Transport::recv with recv_from).
Tests: crates/transport-uds/src/lib.rs::tests::preserves_message_boundaries_two_datagrams
(two sends of different lengths, two receives with exactly those
lengths),
crates/transport-uds/src/lib.rs::tests::send_and_recv_roundtrip_same_process,
crates/transport-uds/tests/l1_cross_process.rs::l1_datagram_crosses_process_boundary
(the same delivery across a real process boundary).
Status: done
§3.2 Sender locator from the socket address
Spec: §3 — the receiver MUST recover the locator from the sender
address so RTPS can answer. If the address is unnamed (an anonymous
client that did not bind) or unresolvable, Locator::INVALID MUST be
returned — not a guess.
Repo: crates/transport-uds/src/lib.rs (source_locator,
parse_hex_id, Locator::INVALID).
Tests: crates/transport-uds/src/lib.rs::tests::send_and_recv_roundtrip_same_process
(checks got.source against the sender’s locator),
crates/transport-uds/src/lib.rs::tests::preserves_message_boundaries_two_datagrams,
crates/transport-uds/tests/l1_cross_process.rs::l1_datagram_crosses_process_boundary.
Status: done
§3.3 Datagram upper bound
Spec: §3 — max_datagram (default 65 536) bounds what is sent and
what is read into the receive buffer. A larger payload MUST be refused
with PayloadTooLarge; a silent truncation is not acceptable, because at
the RTPS level it would be indistinguishable from a short message.
Repo: crates/transport-uds/src/lib.rs (DEFAULT_MAX_DATAGRAM, the
length check in Transport::send, the buffer size in Transport::recv).
Tests: crates/transport-uds/src/lib.rs::tests::send_rejects_oversize_payload
(a payload above the configured limit is refused with
PayloadTooLarge),
crates/transport-uds/src/lib.rs::tests::oversized_for_the_kernel_fails_loudly_and_never_truncates
(1 KiB arrives untruncated; at max_datagram either the delivery is
whole or the error names EMSGSIZE — and then nothing partial is in the
queue).
Status: done
§3.4 Kernel bound versus configured bound
Spec: §3 — the real per-message bound is the kernel’s: Linux via
wmem_max (default 212 992), macOS via net.local.dgram.maxdgram
(default 2048). The transport MUST reconcile the configured bound with
the operating system’s, surface it at bind time or size the socket
buffers accordingly — otherwise a sample between the two bounds fails
only at runtime.
Repo: crates/transport-uds/src/lib.rs (size_buffers: send
buffer at twice, receive buffer at eight times max_datagram, called in
UdsTransport::bind and UdsAbstractDgramTransport::bind).
Tests: crates/transport-uds/src/lib.rs::tests::a_datagram_near_the_configured_limit_arrives_whole
(four datagrams of max_datagram − 512 bytes arrive whole; measured on
macOS 26, where anything above 2048 bytes failed with EMSGSIZE before),
crates/transport-uds/src/lib.rs::tests::oversized_for_the_kernel_fails_loudly_and_never_truncates.
Status: done
§4 Cleanup semantics
§4.1 Filesystem mode: cleanup on drop, takeover on bind
Spec: §4 — bind creates the socket file, drop removes it best-effort
(an unmounted container volume must not make the drop fail). If a bind
finds a leftover file from a crashed predecessor, it MUST remove it and
rebind: UnixDatagram::bind cannot bind over an existing file and
otherwise reports EADDRINUSE.
Repo: crates/transport-uds/src/lib.rs (UdsTransport::bind with
remove_file on an existing path, Drop for UdsTransport).
Tests: crates/transport-uds/src/lib.rs::tests::bind_creates_socket_file
(the file exists after bind and is gone after drop),
crates/transport-uds/src/lib.rs::tests::bind_reuses_path_after_stale_leftover
(a leftover file with foreign content does not block the bind).
Status: done
§4.2 Abstract mode: no cleanup needed
Spec: §4 — the abstract namespace has no inode; Linux releases the name once the last file descriptor closes. A crash-cleanup path is therefore not required, and a bind must not create a file.
Repo: crates/transport-uds/src/abstract_dgram.rs
(UdsAbstractDgramTransport::bind in the Abstract branch without
filesystem access, Drop removing only in the Filesystem branch).
Tests: crates/transport-uds/src/abstract_dgram.rs::tests::abstract_bind_does_not_create_file,
crates/transport-uds/src/abstract_dgram.rs::tests::filesystem_bind_creates_socket_file
(the counter-check: in the filesystem branch the file appears and
disappears). Both #[cfg(target_os = "linux")]-gated, 0 cases on macOS;
measured on the Linux test host as in §2.3.
Status: done
§4.3 Differentiated send-error classification
Spec: §4 — a send error MUST name its cause: a missing destination
socket, a denied permission, a full kernel buffer, a refused connection
and an exceeded message bound are different operational states. A
catch-all Io makes them indistinguishable.
Repo: crates/transport-uds/src/lib.rs (classify_send_error —
NotFound, PermissionDenied, WouldBlock, ConnectionRefused,
EMSGSIZE via raw_os_error).
Tests: crates/transport-uds/src/lib.rs::tests::classify_send_error_maps_kinds
(every named io::ErrorKind value against its message),
crates/transport-uds/src/lib.rs::tests::send_to_missing_peer_is_io_error,
crates/transport-uds/src/lib.rs::tests::oversized_for_the_kernel_fails_loudly_and_never_truncates
(the EMSGSIZE branch, where the kernel refuses the message).
Status: done
§4.4 Idle behaviour of recv
Spec: §4 — with recv_timeout set, an empty socket MUST yield
RecvError::Timeout after it elapses, not an Io error. Without a timeout
recv blocks.
Repo: crates/transport-uds/src/lib.rs (UdsConfig::recv_timeout,
set_read_timeout in bind, the WouldBlock/TimedOut branch in
Transport::recv).
Tests: crates/transport-uds/src/lib.rs::tests::recv_times_out_when_idle,
crates/transport-uds/src/abstract_dgram.rs::tests::recv_times_out_when_idle
(the same evidence in the abstract transport, Linux-gated).
Status: done
§5 Container use case
§5.1 Volume mount as the permission boundary
Spec: §5 — the Docker and Kubernetes pattern is a shared mounted
volume under base_dir; every container binds its own 16-byte locator as
a socket file and the kernel routes between the file descriptors. UDS is
the choice there when multicast is blocked by a cluster network policy
and POSIX SHM is impractical cross-container (UID mapping, /dev/shm
visibility, SELinux profiles).
Repo: crates/transport-uds/src/lib.rs (UdsConfig::base_dir as a
free path, socket_path below it; the drop cleanup is best-effort so an
unmounted volume does not make the drop fail).
Tests: crates/transport-uds/tests/l1_cross_process.rs::l1_datagram_crosses_process_boundary
(two OS processes over a shared directory — the same topology as two
containers over a shared volume, without the container-runtime part),
crates/transport-uds/src/lib.rs::tests::send_and_recv_roundtrip_same_process.
Status: partial — the process boundary is shown, the container boundary is not: no test starts two containers with a mounted volume and actually exercises the UID-mapping and SELinux questions. The missing evidence is carried as open item §5.2.
§5.2 Evidence across a real container boundary
Spec: §5 — that the pattern of §5.1 holds under a container runtime MUST be shown by a test with two containers and one mounted volume: different UID mappings on both sides, the socket file on the volume, a datagram in each direction.
Repo: —
Tests: —
Status: open — no test starts two containers with a mounted volume and different UID mappings.
§6 Platform support
§6.1 Linux and macOS
Spec: §6 — Linux is the primary platform with both modes (filesystem
and abstract namespace), macOS supports the filesystem mode. The crate as
a whole is #[cfg(unix)].
Repo: crates/transport-uds/src/lib.rs (#![cfg(unix)],
#[cfg(target_os = "linux")] pub mod abstract_dgram).
Tests: crates/transport-uds/src/lib.rs::tests::send_and_recv_roundtrip_same_process
and crates/transport-uds/tests/l1_cross_process.rs::l1_datagram_crosses_process_boundary
(measured on both platforms: macOS in this audit, Linux on the Linux test host),
crates/transport-uds/src/abstract_dgram.rs::tests::abstract_send_recv_roundtrip
(Linux only).
Status: done
§6.2 Windows
Spec: §6 — Unix domain sockets are Unix-specific. On Windows the
crate compiles to an empty library; consumers gate their UDS usage with
#[cfg(unix)]. The obvious analogy would be named pipes — a different
protocol with different addressing, and not part of this spec.
Repo: —
Tests: —
Status: open — on Windows the crate compiles to an empty library
(crates/transport-uds/src/lib.rs: #![cfg(unix)]).
Windows offers sockets of the family AF_UNIX since Windows 10 (build 17063)
(Microsoft’s statement, not checked here); the transport does not use them.
§6.3 Cross-vendor interop
Spec: §6 — UDS is intra-container and intra-host IPC. Cross-vendor interop with Cyclone DDS or FastDDS stays in the UDP, TCP and SHM domains; neither vendor has a UDS transport a wire comparison could run against. The section records the scope and states no implementation requirement.
Repo: —
Tests: —
Status: n/a (informative) — neither Cyclone DDS nor FastDDS has a UDS transport against which a wire comparison would be possible.
Audit-Status
14 done / 1 partial / 2 open / 0 blocked / 2 n/a (informative).
Test run: cargo test -p zerodds-transport-uds — on macOS 15 (Rust host
aarch64-apple-darwin) 20 tests (lib) + 1 test
(tests/l1_cross_process.rs) green, 0 failed. The
#[cfg(target_os = "linux")]-gated abstract_dgram module runs 0 cases
there; the same command on the Linux test host against the unchanged
abstract_dgram.rs — 11 cases from abstract_dgram::tests green, 0
failed.
Open items: zerodds-uds-transport-1.0.open.en.md.
ZeroDDS-UDS-Transport 1.0 — Spec-Coverage
Quelle: dieses Dokument ist zugleich die Spec. Für Unix-Domain-Sockets
normiert OMG nichts außer dem vendor-reservierten Locator-Kind
(DDSI-RTPS 2.5 §9.4.5); Pfad-Auflösung, Wire-Format und Cleanup sind
vendor-spezifisch. Cyclone DDS und FastDDS haben keinen offiziellen
UDS-Transport. Die normativen Anforderungen stehen in den ##-Sektionen
§2 bis §6; jedes ###-Item darunter trägt Spec-Zitat, Repo-Anker, Test
und Status.
Implementation: crates/transport-uds/src/lib.rs — SOCK_DGRAM über
Filesystem-Sockets; crates/transport-uds/src/abstract_dgram.rs —
Linux-Abstract-Namespace, #[cfg(target_os = "linux")].
Offene Punkte: zerodds-uds-transport-1.0.open.md.
§1 Scope und Spec-Status
§1.1 Vendor-reservierter Locator-Kind
Spec: DDSI-RTPS 2.5 §9.3.2.4 — 0x00000003 bis 0x01ffffff sind für
vendor-spezifische Locator-Kinds reserviert. ZeroDDS belegt 0x01F0_0002 für
UDS. Das Port-Feld ist bedeutungslos und MUSS 0 sein; die 16
Adress-Bytes tragen die Endpoint-ID.
Repo: crates/rtps/src/wire_types.rs (LocatorKind::Uds,
Locator::uds); gebunden in crates/transport-uds/src/lib.rs
(UdsTransport::local_locator, Kind-Prüfung in Transport::send).
Tests: crates/transport-uds/src/lib.rs::tests::locator_kind_is_the_vendor_reserved_uds_value
(prüft den Zahlwert 0x01F0_0002, die Lage im Bereich 0x00000003 bis 0x01ffffff und port == 0),
crates/transport-uds/src/lib.rs::tests::local_locator_reflects_bind_id,
crates/transport-uds/src/lib.rs::tests::send_rejects_non_uds_locator,
crates/rtps/src/wire_types.rs::tests::locator_uds_layout,
crates/rtps/src/wire_types.rs::tests::locator_kind_roundtrip.
Status: done
§1.2 Was OMG für UDS nicht normiert
Spec: DDSI-RTPS 2.5 kennt kein Pfad-Schema, kein UDS-Wire-Format und kein Cleanup-Protokoll. Cyclone DDS und FastDDS haben keinen offiziellen UDS-Transport; sie decken denselben Anwendungsfall über iceoryx oder POSIX-SHM ab. Die Sektion hält den Scope fest und stellt keine Implementierungs-Anforderung.
Repo: —
Tests: —
Status: n/a (informative) — Abgrenzung des Geltungsbereichs; die Anforderungen stehen in den Items ab §2.
§2 Pfad-Auflösung
§2.1 Filesystem-Modus (Default)
Spec: §2 — die 16-Byte-Locator-Adresse wird als
<base_dir>/<lowercase-hex32>.sock aufgelöst. Default-base_dir ist
/tmp/zerodds/uds. Der Hex-String ist die Adresse in 32 Kleinbuchstaben,
ohne Trenner.
Repo: crates/transport-uds/src/lib.rs (socket_path,
DEFAULT_BASE_DIR, parse_hex_id, source_locator als Rückrichtung).
Tests: crates/transport-uds/src/lib.rs::tests::socket_path_is_hex_under_base_dir
(prüft Präfix und .sock-Endung an einer Adresse mit gesetzten
Randbytes),
crates/transport-uds/src/lib.rs::tests::parse_hex_id_roundtrip,
crates/transport-uds/src/lib.rs::tests::parse_hex_id_rejects_wrong_length,
crates/transport-uds/src/lib.rs::tests::parse_hex_id_rejects_non_hex.
Status: done
§2.2 Basis-Verzeichnis: lazy, user-privat, TOCTOU-fest
Spec: §2 — das Basis-Verzeichnis wird beim Bind angelegt, wenn es
fehlt, und trägt dann Mode 0700. Ein bestehender Pfad, der ein Symlink
oder keine Verzeichnis ist, MUSS abgelehnt werden, statt ihn zu
chmodden — sonst könnte ein untergeschobener Symlink fremde Pfade auf
0700 setzen. Auf ein bestehendes fremdes Verzeichnis wird kein Mode
gesetzt.
Repo: crates/transport-uds/src/lib.rs (ensure_base_dir —
symlink_metadata statt exists, Ablehnung von Symlink und
Nicht-Verzeichnis, create_dir_all plus set_permissions(0o700) mit
Wiederholungs-Prüfung).
Tests: crates/transport-uds/src/lib.rs::tests::ensure_base_dir_creates_dir_with_mode_0700
(liest den Mode zurück und vergleicht ihn gegen 0o700),
crates/transport-uds/src/lib.rs::tests::ensure_base_dir_rejects_symlink,
crates/transport-uds/src/lib.rs::tests::ensure_base_dir_rejects_file_path,
crates/transport-uds/src/lib.rs::tests::ensure_base_dir_creates_missing_dir.
Status: done
§2.3 Abstract-Namespace-Modus (Linux)
Spec: §2 — auf Linux gibt es die Alternative ohne Filesystem-Inode:
der Socket-Name beginnt mit einem Null-Byte, gefolgt von
<prefix>-<lowercase-hex32>. Kein Cleanup nötig, keine
Inode-Permissions, kein Cross-Mount-Volume. Der Name passt in
sun_path (108 Byte, Byte 0 ist das Null-Präfix).
Repo: crates/transport-uds/src/abstract_dgram.rs
(UdsAbstractDgramTransport, UdsAddress::Abstract,
MAX_ABSTRACT_NAME = 107, UdsAddress::abstract_default); Modul
deklariert in crates/transport-uds/src/lib.rs unter
#[cfg(target_os = "linux")].
Tests: crates/transport-uds/src/abstract_dgram.rs::tests::abstract_bind_does_not_create_file,
crates/transport-uds/src/abstract_dgram.rs::tests::abstract_send_recv_roundtrip,
crates/transport-uds/src/abstract_dgram.rs::tests::abstract_preserves_message_boundaries,
crates/transport-uds/src/abstract_dgram.rs::tests::local_locator_reflects_bind_id.
Diese Tests fahren auf macOS 0 Fälle: das Modul steht hinter
#[cfg(target_os = "linux")] und wird dort nicht übersetzt. Gemessen
wurden sie auf dem Linux-Testhost gegen dieselbe, unveränderte
abstract_dgram.rs (Kommando cargo test -p zerodds-transport-uds) —
11 Fälle aus abstract_dgram::tests grün, 0 failed.
Status: done
§2.4 Filesystem-Modus im Abstract-Transport (A/B-Vergleich)
Spec: §2 — derselbe Transport MUSS auch Filesystem-Adressierung können, damit Abstract- und Filesystem-Modus mit identischem Socket-Code gegeneinander gemessen werden können. Der Unterschied ist eine Config-Zeile.
Repo: crates/transport-uds/src/abstract_dgram.rs
(UdsAddress::Filesystem, UdsAddress::filesystem_default, fs_path).
Tests: crates/transport-uds/src/abstract_dgram.rs::tests::filesystem_bind_creates_socket_file
(Socket-Datei entsteht beim Bind und verschwindet beim Drop),
crates/transport-uds/src/abstract_dgram.rs::tests::filesystem_send_recv_roundtrip.
Auch diese Tests sind #[cfg(target_os = "linux")]-gated und fahren auf
macOS 0 Fälle; gemessen auf dem Linux-Testhost im selben Lauf wie §2.3.
Status: done
§3 Wire-Format
§3.1 SOCK_DGRAM mit erhaltenen Message-Grenzen
Spec: §3 — eine Datagram-Message pro RTPS-Frame. Der Kernel erhält die Message-Grenzen, anders als bei einem TCP-Stream; es braucht deshalb kein Framing-Layer darüber. Zwei Sends MÜSSEN als zwei Receives mit den ursprünglichen Längen ankommen.
Repo: crates/transport-uds/src/lib.rs (UdsTransport über
std::os::unix::net::UnixDatagram, Transport::send mit send_to,
Transport::recv mit recv_from).
Tests: crates/transport-uds/src/lib.rs::tests::preserves_message_boundaries_two_datagrams
(zwei Sends unterschiedlicher Länge, zwei Receives mit genau diesen
Längen),
crates/transport-uds/src/lib.rs::tests::send_and_recv_roundtrip_same_process,
crates/transport-uds/tests/l1_cross_process.rs::l1_datagram_crosses_process_boundary
(dieselbe Zustellung über eine echte Prozessgrenze).
Status: done
§3.2 Absender-Locator aus der Socket-Adresse
Spec: §3 — der Empfänger MUSS aus der Absender-Adresse den
Locator zurückgewinnen, damit RTPS antworten kann. Ist die Adresse
unbenannt (anonymer Client ohne Bind) oder nicht auflösbar, MUSS
Locator::INVALID zurückgegeben werden — nicht geraten.
Repo: crates/transport-uds/src/lib.rs (source_locator,
parse_hex_id, Locator::INVALID).
Tests: crates/transport-uds/src/lib.rs::tests::send_and_recv_roundtrip_same_process
(prüft got.source gegen den Locator des Senders),
crates/transport-uds/src/lib.rs::tests::preserves_message_boundaries_two_datagrams,
crates/transport-uds/tests/l1_cross_process.rs::l1_datagram_crosses_process_boundary.
Status: done
§3.3 Datagram-Obergrenze
Spec: §3 — max_datagram (Default 65 536) begrenzt, was gesendet und
was in den Empfangspuffer gelesen wird. Eine größere Nutzlast MUSS mit
PayloadTooLarge abgelehnt werden; eine stille Kürzung ist nicht
zulässig, weil sie auf RTPS-Ebene von einer kurzen Nachricht nicht zu
unterscheiden wäre.
Repo: crates/transport-uds/src/lib.rs (DEFAULT_MAX_DATAGRAM,
Längenprüfung in Transport::send, Puffergröße in Transport::recv).
Tests: crates/transport-uds/src/lib.rs::tests::send_rejects_oversize_payload
(Nutzlast über dem konfigurierten Limit wird mit PayloadTooLarge
abgelehnt),
crates/transport-uds/src/lib.rs::tests::oversized_for_the_kernel_fails_loudly_and_never_truncates
(1 KiB kommt unverkürzt an; bei max_datagram gilt entweder
unverkürzte Zustellung oder ein Fehler, der EMSGSIZE benennt — und dann
liegt nichts Teilweises in der Queue).
Status: done
§3.4 Kernel-Obergrenze gegen konfigurierte Obergrenze
Spec: §3 — die tatsächliche Obergrenze pro Message setzt der Kernel:
Linux über wmem_max (Default 212 992), macOS über
net.local.dgram.maxdgram (Default 2048). Der Transport MUSS die
konfigurierte Grenze gegen die des Betriebssystems abgleichen, sie beim
Bind sichtbar machen oder die Socket-Puffer entsprechend setzen — sonst
scheitert ein Sample zwischen beiden Grenzen erst zur Laufzeit.
Repo: crates/transport-uds/src/lib.rs (size_buffers: Sendepuffer
auf das Doppelte, Empfangspuffer auf das Achtfache von max_datagram,
aufgerufen in UdsTransport::bind und
UdsAbstractDgramTransport::bind).
Tests: crates/transport-uds/src/lib.rs::tests::a_datagram_near_the_configured_limit_arrives_whole
(vier Datagramme zu max_datagram − 512 Byte kommen ganz an; auf macOS
26 gemessen, vorher dort EMSGSIZE über 2048 Byte),
crates/transport-uds/src/lib.rs::tests::oversized_for_the_kernel_fails_loudly_and_never_truncates.
Status: done
§4 Cleanup-Semantik
§4.1 Filesystem-Modus: Cleanup beim Drop, Übernahme beim Bind
Spec: §4 — der Bind legt die Socket-Datei an, der Drop entfernt sie
best-effort (ein abgehängtes Container-Volume darf den Drop nicht
scheitern lassen). Findet ein Bind eine Restdatei eines abgestürzten
Vorgängers, MUSS er sie entfernen und neu binden: UnixDatagram::bind
kann nicht über eine bestehende Datei binden und meldet sonst
EADDRINUSE.
Repo: crates/transport-uds/src/lib.rs (UdsTransport::bind mit
remove_file bei bestehendem Pfad, Drop for UdsTransport).
Tests: crates/transport-uds/src/lib.rs::tests::bind_creates_socket_file
(Datei existiert nach dem Bind und ist nach dem Drop weg),
crates/transport-uds/src/lib.rs::tests::bind_reuses_path_after_stale_leftover
(eine Restdatei mit fremdem Inhalt blockiert den Bind nicht).
Status: done
§4.2 Abstract-Modus: kein Cleanup nötig
Spec: §4 — im Abstract-Namespace gibt es keinen Inode; Linux gibt den Namen frei, sobald der letzte Filedeskriptor schließt. Ein Crash-Cleanup-Pfad ist deshalb nicht erforderlich, und ein Bind darf keine Datei anlegen.
Repo: crates/transport-uds/src/abstract_dgram.rs
(UdsAbstractDgramTransport::bind im Abstract-Zweig ohne
Filesystem-Zugriff, Drop entfernt nur im Filesystem-Zweig).
Tests: crates/transport-uds/src/abstract_dgram.rs::tests::abstract_bind_does_not_create_file,
crates/transport-uds/src/abstract_dgram.rs::tests::filesystem_bind_creates_socket_file
(die Gegenprobe: im Filesystem-Zweig entsteht und verschwindet die
Datei). Beide #[cfg(target_os = "linux")]-gated, auf macOS 0 Fälle;
gemessen auf dem Linux-Testhost wie §2.3.
Status: done
§4.3 Differenzierte Fehlerklassifikation beim Senden
Spec: §4 — ein Sendefehler MUSS seine Ursache benennen: fehlender
Ziel-Socket, verweigerte Permission, voller Kernel-Puffer, abgelehnte
Verbindung und überschrittene Message-Grenze sind unterschiedliche
Betriebszustände. Ein Sammel-Io macht sie ununterscheidbar.
Repo: crates/transport-uds/src/lib.rs (classify_send_error —
NotFound, PermissionDenied, WouldBlock, ConnectionRefused,
EMSGSIZE über raw_os_error).
Tests: crates/transport-uds/src/lib.rs::tests::classify_send_error_maps_kinds
(alle benannten io::ErrorKind-Werte auf ihre Meldung),
crates/transport-uds/src/lib.rs::tests::send_to_missing_peer_is_io_error,
crates/transport-uds/src/lib.rs::tests::oversized_for_the_kernel_fails_loudly_and_never_truncates
(der EMSGSIZE-Zweig, sofern der Kernel die Message ablehnt).
Status: done
§4.4 Leerlauf-Verhalten von recv
Spec: §4 — mit gesetztem recv_timeout MUSS ein leerer Socket nach
Ablauf RecvError::Timeout liefern, nicht einen Io-Fehler. Ohne Timeout
blockiert recv.
Repo: crates/transport-uds/src/lib.rs (UdsConfig::recv_timeout,
set_read_timeout im Bind, WouldBlock/TimedOut-Zweig in
Transport::recv).
Tests: crates/transport-uds/src/lib.rs::tests::recv_times_out_when_idle,
crates/transport-uds/src/abstract_dgram.rs::tests::recv_times_out_when_idle
(derselbe Nachweis im Abstract-Transport, Linux-gated).
Status: done
§5 Container-Anwendungsfall
§5.1 Volume-Mount als Permission-Grenze
Spec: §5 — das Muster für Docker und Kubernetes ist ein gemeinsam
gemountetes Volume unter base_dir; jeder Container bindet seinen
eigenen 16-Byte-Locator als Socket-Datei, der Kernel routet zwischen den
Filedeskriptoren. UDS ist dort die Wahl, wenn Multicast durch eine
Cluster-Network-Policy blockiert ist und POSIX-SHM cross-Container
unpraktisch ist (UID-Mapping, /dev/shm-Sichtbarkeit, SELinux-Profile).
Repo: crates/transport-uds/src/lib.rs (UdsConfig::base_dir als
freier Pfad, socket_path unterhalb davon; Drop-Cleanup ist
best-effort, damit ein abgehängtes Volume den Drop nicht scheitern
lässt).
Tests: crates/transport-uds/tests/l1_cross_process.rs::l1_datagram_crosses_process_boundary
(zwei OS-Prozesse über ein gemeinsames Verzeichnis — dieselbe Topologie
wie zwei Container über ein gemeinsames Volume, ohne den
Container-Runtime-Anteil),
crates/transport-uds/src/lib.rs::tests::send_and_recv_roundtrip_same_process.
Status: partial — die Prozessgrenze ist belegt, die Container-Grenze nicht: es gibt keinen Test, der zwei Container mit gemountetem Volume startet und die UID-Mapping- und SELinux-Fragen tatsächlich durchspielt. Der fehlende Nachweis ist als §5.2 offen geführt.
§5.2 Nachweis über eine echte Container-Grenze
Spec: §5 — dass das Muster aus §5.1 unter einer Container-Runtime trägt, MUSS ein Test mit zwei Containern und einem gemounteten Volume belegen: unterschiedliche UID-Mappings auf beiden Seiten, Socket-Datei auf dem Volume, Datagram in beide Richtungen.
Repo: —
Tests: —
Status: open — kein Test startet zwei Container mit gemountetem Volume und unterschiedlichen UID-Mappings.
§6 Plattform-Support
§6.1 Linux und macOS
Spec: §6 — Linux ist die primäre Plattform mit beiden Modi
(Filesystem und Abstract-Namespace), macOS unterstützt den
Filesystem-Modus. Der Crate ist als Ganzes #[cfg(unix)].
Repo: crates/transport-uds/src/lib.rs (#![cfg(unix)],
#[cfg(target_os = "linux")] pub mod abstract_dgram).
Tests: crates/transport-uds/src/lib.rs::tests::send_and_recv_roundtrip_same_process
und crates/transport-uds/tests/l1_cross_process.rs::l1_datagram_crosses_process_boundary
(auf beiden Plattformen gemessen: macOS in diesem Audit, Linux auf
Linux-Testhost), crates/transport-uds/src/abstract_dgram.rs::tests::abstract_send_recv_roundtrip
(nur Linux).
Status: done
§6.2 Windows
Spec: §6 — Unix-Domain-Sockets sind Unix-spezifisch. Auf Windows
übersetzt der Crate zu einer leeren Bibliothek; Konsumenten schalten ihre
UDS-Nutzung mit #[cfg(unix)]. Die naheliegende Analogie wären
Named Pipes — ein anderes Protokoll mit anderer Adressierung, das nicht
Teil dieser Spec ist.
Repo: —
Tests: —
Status: open — auf Windows übersetzt der Crate zu einer leeren
Bibliothek (crates/transport-uds/src/lib.rs: #![cfg(unix)]). Windows bietet seit Windows 10 (Build 17063) Sockets der Familie
AF_UNIX an (Angabe von Microsoft, hier nicht geprüft); der Transport nutzt
sie nicht.
§6.3 Cross-Vendor-Interop
Spec: §6 — UDS ist Intra-Container- und Intra-Host-IPC. Cross-Vendor- Interop mit Cyclone DDS oder FastDDS bleibt in der UDP-, TCP- und SHM-Domäne; beide Vendoren haben keinen UDS-Transport, mit dem sich ein Wire-Vergleich führen ließe. Die Sektion hält den Scope fest und stellt keine Implementierungs-Anforderung.
Repo: —
Tests: —
Status: n/a (informative) — weder Cyclone DDS noch FastDDS hat einen UDS-Transport, gegen den ein Wire-Vergleich möglich wäre.
Audit-Status
14 done / 1 partial / 2 open / 0 blocked / 2 n/a (informative).
Test-Lauf: cargo test -p zerodds-transport-uds — auf macOS 15
(Rust-Host aarch64-apple-darwin) 20 Tests (lib) + 1 Test
(tests/l1_cross_process.rs) grün, 0 failed. Der
#[cfg(target_os = "linux")]-gated abstract_dgram-Modul fährt dort 0
Fälle; derselbe Befehl auf dem Linux-Testhost gegen die unveränderte
abstract_dgram.rs — 11 abstract_dgram::tests-Fälle grün, 0 failed.
Offene Punkte: zerodds-uds-transport-1.0.open.md.