Linux client RX timestamps now use SO_TIMESTAMPING RX_SOFTWARE instead of
SO_TIMESTAMPNS;
ReceiveMeta.kernel_rx_timestamp and all PR #100 downstream OWD behavior remain
unchanged;
safe Linux MSG_ERRQUEUE timestamp/error classification primitives are added for
upcoming client TX timestamp support;
production TX timestamp generation is deliberately not enabled yet.
RX compatibility
Old: SO_TIMESTAMPNS / SCM_TIMESTAMPNS.
New: SO_TIMESTAMPING with RX_SOFTWARE + SOFTWARE flags, reported via SCM_TIMESTAMPING (nix's ScmTimestampsns/Timestamps, selecting the system slot).
Linux documents fallback quirks when SO_TIMESTAMP[NS] and SO_TIMESTAMPING
are both enabled on one socket, so the client now uses SO_TIMESTAMPING
exclusively for receive timing, ahead of TX timestamp work landing on the
same API.
PacketMeta, downstream OWD, RTT, and IPDV semantics are unchanged. The
control buffer is now sized deliberately via cmsg_space::<Timestamps>() + cmsg_space::<i32>() instead of an unexplained fixed 128-byte constant.
TX foundation
Added as test-only primitives in crates/irtt-client/src/receive/linux/error_queue.rs:
TX_TIMESTAMPING_FLAGS: the future TX-enabled SO_TIMESTAMPING
configuration (RX_SOFTWARE, SOFTWARE, TX_SOFTWARE, OPT_ID, OPT_TSONLY), built from safe nix flag APIs;
a nonblocking MSG_ERRQUEUE parser (try_recv_error_queue_record) using a
zero-length iovec, since OPT_TSONLY notifications carry no payload and
correlation is by ID;
pure classification logic (classify) distinguishing a genuine send
timestamp completion from malformed timing metadata and from a real
socket/network error, order-independent over the decoded cmsgs;
Linux smoke tests enabling the flags on a dedicated test socket and
observing automatic OPT_ID values 0, then 1, across two sequential
sends.
SCM_TSTAMP_SND (value 0) isn't exposed by libc/nix; it's a small
documented private constant verified against the kernel UAPI enum in include/uapi/linux/errqueue.h.
State prominently: production TX_SOFTWARE remains disabled.
Error queue
The classifier distinguishes:
a valid timestamp-origin record with a usable timestamp (TxTimestamp);
a timestamp-origin extended error without a usable timestamp
(MalformedTimestamp — an accuracy failure, not a socket failure);
a non-timestamp extended error (SocketError), so real ICMP/local/network
errors are never misclassified as timing metadata;
records with no extended error at all (Ignored).
No production code drains the error queue yet.
Scope
Explicitly no:
probe TX timestamp association;
PendingProbe changes;
upstream OWD change;
RTT/IPDV change;
explicit SCM_TS_OPT_ID;
unsafe cmsg code;
hardware timestamps.
A deterministic test proving a failed nonblocking send never consumes an
automatic OPT_ID is intentionally not included: there is no reliable
OS-level way to force WouldBlock on a connected loopback UDP socket in
this environment without flaky buffer-saturation/sleep tricks. This remains
an open assumption the next PR (production TX enablement + correlation)
must verify.
cargo test --workspace --all-targets --all-features --locked (all
passing, including 328 irtt-client lib tests, run 3x under Linux/Docker
to confirm no flakiness)
Cross-target cargo check -p irtt-client --all-targets --all-features --locked
for x86_64-unknown-linux-gnu, aarch64-unknown-linux-musl, x86_64-unknown-freebsd, x86_64-pc-windows-gnu, and the 32-bit i686-unknown-linux-gnu — all pass. nix/libc select the platform
timestamp ABI; this migration doesn't add a new time32 dependency, but no
broader Y2038 claim is made than what libc/nix already provide.
Built/tested on Linux (native compile requires target_os = "linux") via a
local rust:1-slim Docker container since the host is macOS.
## Summary
- Linux client RX timestamps now use SO_TIMESTAMPING RX_SOFTWARE instead of
SO_TIMESTAMPNS;
- ReceiveMeta.kernel_rx_timestamp and all PR #100 downstream OWD behavior remain
unchanged;
- safe Linux MSG_ERRQUEUE timestamp/error classification primitives are added for
upcoming client TX timestamp support;
- production TX timestamp generation is deliberately not enabled yet.
## RX compatibility
Old: `SO_TIMESTAMPNS` / `SCM_TIMESTAMPNS`.
New: `SO_TIMESTAMPING` with `RX_SOFTWARE` + `SOFTWARE` flags, reported via
`SCM_TIMESTAMPING` (nix's `ScmTimestampsns`/`Timestamps`, selecting the
`system` slot).
Linux documents fallback quirks when `SO_TIMESTAMP[NS]` and `SO_TIMESTAMPING`
are both enabled on one socket, so the client now uses `SO_TIMESTAMPING`
exclusively for receive timing, ahead of TX timestamp work landing on the
same API.
`PacketMeta`, downstream OWD, RTT, and IPDV semantics are unchanged. The
control buffer is now sized deliberately via `cmsg_space::<Timestamps>() +
cmsg_space::<i32>()` instead of an unexplained fixed 128-byte constant.
## TX foundation
Added as test-only primitives in `crates/irtt-client/src/receive/linux/error_queue.rs`:
- `TX_TIMESTAMPING_FLAGS`: the future TX-enabled `SO_TIMESTAMPING`
configuration (`RX_SOFTWARE`, `SOFTWARE`, `TX_SOFTWARE`, `OPT_ID`,
`OPT_TSONLY`), built from safe nix flag APIs;
- a nonblocking `MSG_ERRQUEUE` parser (`try_recv_error_queue_record`) using a
zero-length iovec, since `OPT_TSONLY` notifications carry no payload and
correlation is by ID;
- pure classification logic (`classify`) distinguishing a genuine send
timestamp completion from malformed timing metadata and from a real
socket/network error, order-independent over the decoded cmsgs;
- Linux smoke tests enabling the flags on a dedicated test socket and
observing automatic `OPT_ID` values `0`, then `1`, across two sequential
sends.
`SCM_TSTAMP_SND` (value `0`) isn't exposed by libc/nix; it's a small
documented private constant verified against the kernel UAPI enum in
`include/uapi/linux/errqueue.h`.
State prominently: **production `TX_SOFTWARE` remains disabled.**
## Error queue
The classifier distinguishes:
- a valid timestamp-origin record with a usable timestamp (`TxTimestamp`);
- a timestamp-origin extended error without a usable timestamp
(`MalformedTimestamp` — an accuracy failure, not a socket failure);
- a non-timestamp extended error (`SocketError`), so real ICMP/local/network
errors are never misclassified as timing metadata;
- records with no extended error at all (`Ignored`).
No production code drains the error queue yet.
## Scope
Explicitly no:
- probe TX timestamp association;
- `PendingProbe` changes;
- upstream OWD change;
- RTT/IPDV change;
- explicit `SCM_TS_OPT_ID`;
- unsafe cmsg code;
- hardware timestamps.
A deterministic test proving a failed nonblocking send never consumes an
automatic `OPT_ID` is intentionally not included: there is no reliable
OS-level way to force `WouldBlock` on a connected loopback UDP socket in
this environment without flaky buffer-saturation/sleep tricks. This remains
an open assumption the next PR (production TX enablement + correlation)
must verify.
## Verification
- `cargo fmt --all -- --check`
- `cargo check --workspace --all-targets --all-features --locked`
- `cargo test --workspace --all-targets --all-features --locked` (all
passing, including 328 `irtt-client` lib tests, run 3x under Linux/Docker
to confirm no flakiness)
- `cargo clippy --workspace --all-targets --all-features --locked -- -D warnings`
- `RUSTDOCFLAGS="-D warnings" cargo doc --workspace --all-features --no-deps --locked`
- `cargo check -p irtt-cli --no-default-features --features {client,tui,server} --all-targets --locked`
- `git diff --check`
- Cross-target `cargo check -p irtt-client --all-targets --all-features --locked`
for `x86_64-unknown-linux-gnu`, `aarch64-unknown-linux-musl`,
`x86_64-unknown-freebsd`, `x86_64-pc-windows-gnu`, and the 32-bit
`i686-unknown-linux-gnu` — all pass. `nix`/`libc` select the platform
timestamp ABI; this migration doesn't add a new time32 dependency, but no
broader Y2038 claim is made than what libc/nix already provide.
Built/tested on Linux (native compile requires `target_os = "linux"`) via a
local `rust:1-slim` Docker container since the host is macOS.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
### 💡 Codex Review
Here are some automated review suggestions for this pull request.
**Reviewed commit:** `97b094539f`
<details> <summary>ℹ️ About Codex in GitHub</summary>
<br/>
[Your team has set up Codex to review pull requests in this repo](https://chatgpt.com/codex/cloud/settings/general). Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
</details>
When this helper runs with a stack layout that does not happen to align control for cmsghdr, [u8; N] guarantees only byte alignment, while nix's RecvMsg::cmsgs() constructs and traverses cmsghdr references and requires aligned ancillary storage. This makes the supposedly safe helper undefined behavior on such layouts; use an explicitly aligned wrapper like the parent module's ControlBuffer, or otherwise guarantee cmsghdr alignment.
Useful? React with 👍 / 👎.
**<sub><sub></sub></sub> Align the error-queue control buffer**
When this helper runs with a stack layout that does not happen to align `control` for `cmsghdr`, `[u8; N]` guarantees only byte alignment, while nix's `RecvMsg::cmsgs()` constructs and traverses `cmsghdr` references and requires aligned ancillary storage. This makes the supposedly safe helper undefined behavior on such layouts; use an explicitly aligned wrapper like the parent module's `ControlBuffer`, or otherwise guarantee `cmsghdr` alignment.
Useful? React with 👍 / 👎.
These 437 lines are explicitly compiled only under #[cfg(test)] and are being landed solely for a later change, so they neither implement current production behavior nor protect a current production contract. Keeping the parser, flags, and tests here substantially enlarges the RX refactor and prematurely pins an unintegrated design; move them into the change that actually enables TX timestamping, retaining only the RX coverage here.
**<sub><sub></sub></sub> Remove the future-only TX timestamp module**
These 437 lines are explicitly compiled only under `#[cfg(test)]` and are being landed solely for a later change, so they neither implement current production behavior nor protect a current production contract. Keeping the parser, flags, and tests here substantially enlarges the RX refactor and prematurely pins an unintegrated design; move them into the change that actually enables TX timestamping, retaining only the RX coverage here.
AGENTS.md reference: [AGENTS.md:L118-L120](https://github.com/Lochnair/irtt-rs/blob/97b094539f2b9824de7991f1036b0d8ba6be6b3f/AGENTS.md#L118-L120)
Useful? React with 👍 / 👎.
Good catch — fixed in 12b4816 by wrapping the control buffer in a #[repr(align(8))] type, mirroring the parent module's ControlBuffer pattern, instead of relying on incidental stack alignment of a plain [u8; N].
Good catch — fixed in 12b4816 by wrapping the control buffer in a `#[repr(align(8))]` type, mirroring the parent module's `ControlBuffer` pattern, instead of relying on incidental stack alignment of a plain `[u8; N]`.
_🤖 Addressed by [Claude Code](https://claude.com/claude-code)_
Keeping this as-is: this PR is deliberately scoped as the foundation PR for client kernel TX timestamping (per the originating task), and establishing these safe, deterministically-tested TX primitives now — while explicitly not enabling them in production — is the point of that scoping, not incidental scope creep. Landing the RX migration alone would leave the next PR to rediscover flag construction, MSG_ERRQUEUE parsing, and extended-error classification under time pressure while also wiring up production correlation, which is a worse split. Everything here is #[cfg(test)]-gated and has no effect on the production build.
Keeping this as-is: this PR is deliberately scoped as the foundation PR for client kernel TX timestamping (per the originating task), and establishing these safe, deterministically-tested TX primitives now — while explicitly not enabling them in production — is the point of that scoping, not incidental scope creep. Landing the RX migration alone would leave the next PR to rediscover flag construction, MSG_ERRQUEUE parsing, and extended-error classification under time pressure while also wiring up production correlation, which is a worse split. Everything here is `#[cfg(test)]`-gated and has no effect on the production build.
_🤖 Addressed by [Claude Code](https://claude.com/claude-code)_
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
SO_TIMESTAMPNS;
unchanged;
upcoming client TX timestamp support;
RX compatibility
Old:
SO_TIMESTAMPNS/SCM_TIMESTAMPNS.New:
SO_TIMESTAMPINGwithRX_SOFTWARE+SOFTWAREflags, reported viaSCM_TIMESTAMPING(nix'sScmTimestampsns/Timestamps, selecting thesystemslot).Linux documents fallback quirks when
SO_TIMESTAMP[NS]andSO_TIMESTAMPINGare both enabled on one socket, so the client now uses
SO_TIMESTAMPINGexclusively for receive timing, ahead of TX timestamp work landing on the
same API.
PacketMeta, downstream OWD, RTT, and IPDV semantics are unchanged. Thecontrol buffer is now sized deliberately via
cmsg_space::<Timestamps>() + cmsg_space::<i32>()instead of an unexplained fixed 128-byte constant.TX foundation
Added as test-only primitives in
crates/irtt-client/src/receive/linux/error_queue.rs:TX_TIMESTAMPING_FLAGS: the future TX-enabledSO_TIMESTAMPINGconfiguration (
RX_SOFTWARE,SOFTWARE,TX_SOFTWARE,OPT_ID,OPT_TSONLY), built from safe nix flag APIs;MSG_ERRQUEUEparser (try_recv_error_queue_record) using azero-length iovec, since
OPT_TSONLYnotifications carry no payload andcorrelation is by ID;
classify) distinguishing a genuine sendtimestamp completion from malformed timing metadata and from a real
socket/network error, order-independent over the decoded cmsgs;
observing automatic
OPT_IDvalues0, then1, across two sequentialsends.
SCM_TSTAMP_SND(value0) isn't exposed by libc/nix; it's a smalldocumented private constant verified against the kernel UAPI enum in
include/uapi/linux/errqueue.h.State prominently: production
TX_SOFTWAREremains disabled.Error queue
The classifier distinguishes:
TxTimestamp);(
MalformedTimestamp— an accuracy failure, not a socket failure);SocketError), so real ICMP/local/networkerrors are never misclassified as timing metadata;
Ignored).No production code drains the error queue yet.
Scope
Explicitly no:
PendingProbechanges;SCM_TS_OPT_ID;A deterministic test proving a failed nonblocking send never consumes an
automatic
OPT_IDis intentionally not included: there is no reliableOS-level way to force
WouldBlockon a connected loopback UDP socket inthis environment without flaky buffer-saturation/sleep tricks. This remains
an open assumption the next PR (production TX enablement + correlation)
must verify.
Verification
cargo fmt --all -- --checkcargo check --workspace --all-targets --all-features --lockedcargo test --workspace --all-targets --all-features --locked(allpassing, including 328
irtt-clientlib tests, run 3x under Linux/Dockerto confirm no flakiness)
cargo clippy --workspace --all-targets --all-features --locked -- -D warningsRUSTDOCFLAGS="-D warnings" cargo doc --workspace --all-features --no-deps --lockedcargo check -p irtt-cli --no-default-features --features {client,tui,server} --all-targets --lockedgit diff --checkcargo check -p irtt-client --all-targets --all-features --lockedfor
x86_64-unknown-linux-gnu,aarch64-unknown-linux-musl,x86_64-unknown-freebsd,x86_64-pc-windows-gnu, and the 32-biti686-unknown-linux-gnu— all pass.nix/libcselect the platformtimestamp ABI; this migration doesn't add a new time32 dependency, but no
broader Y2038 claim is made than what libc/nix already provide.
Built/tested on Linux (native compile requires
target_os = "linux") via alocal
rust:1-slimDocker container since the host is macOS.🤖 Generated with Claude Code
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:
97b094539fℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
@@ -0,0 +1,450 @@//! Safe `MSG_ERRQUEUE` primitives for the future client TX timestamp workWhen this helper runs with a stack layout that does not happen to align
controlforcmsghdr,[u8; N]guarantees only byte alignment, while nix'sRecvMsg::cmsgs()constructs and traversescmsghdrreferences and requires aligned ancillary storage. This makes the supposedly safe helper undefined behavior on such layouts; use an explicitly aligned wrapper like the parent module'sControlBuffer, or otherwise guaranteecmsghdralignment.Useful? React with 👍 / 👎.
@@ -0,0 +5,4 @@//! `SOF_TIMESTAMPING_TX_SOFTWARE` and never drain the error queue, so//! nothing here runs outside `#[cfg(test)]`. It exists so the follow-up PR//! that wires TX timestamps into probes does not need to rediscover flag//! construction, error-queue parsing, or extended-error classification.These 437 lines are explicitly compiled only under
#[cfg(test)]and are being landed solely for a later change, so they neither implement current production behavior nor protect a current production contract. Keeping the parser, flags, and tests here substantially enlarges the RX refactor and prematurely pins an unintegrated design; move them into the change that actually enables TX timestamping, retaining only the RX coverage here.AGENTS.md reference: AGENTS.md:L118-L120
Useful? React with 👍 / 👎.
@@ -0,0 +1,450 @@//! Safe `MSG_ERRQUEUE` primitives for the future client TX timestamp workGood catch — fixed in
12b4816by wrapping the control buffer in a#[repr(align(8))]type, mirroring the parent module'sControlBufferpattern, instead of relying on incidental stack alignment of a plain[u8; N].🤖 Addressed by Claude Code
@@ -0,0 +5,4 @@//! `SOF_TIMESTAMPING_TX_SOFTWARE` and never drain the error queue, so//! nothing here runs outside `#[cfg(test)]`. It exists so the follow-up PR//! that wires TX timestamps into probes does not need to rediscover flag//! construction, error-queue parsing, or extended-error classification.Keeping this as-is: this PR is deliberately scoped as the foundation PR for client kernel TX timestamping (per the originating task), and establishing these safe, deterministically-tested TX primitives now — while explicitly not enabling them in production — is the point of that scoping, not incidental scope creep. Landing the RX migration alone would leave the next PR to rediscover flag construction, MSG_ERRQUEUE parsing, and extended-error classification under time pressure while also wiring up production correlation, which is a worse split. Everything here is
#[cfg(test)]-gated and has no effect on the production build.🤖 Addressed by Claude Code