docs: turn the root README into a project landing page
Reduce the root README to project discovery and navigation: what irtt-rs is, its relationship to upstream IRTT (including a factual capability comparison checked against upstream v0.9.2), installation, a short quick start, and links out to detailed documentation. Detailed CLI behavior, output/measurement semantics, continuous-mode memory guidance, and server bind/session/negotiation semantics move into new manpage-shaped docs under docs/ (irtt-rs, irtt-client, irtt-tui, irtt-server), rewritten as operational guidance rather than copied verbatim from the old README sections they replace.
This commit is contained in:
5 files changed
+743
-343
No files matched your search
@@ -1,26 +1,75 @@
|
|||||||
# irtt-rs
|
# irtt-rs
|
||||||
|
|
||||||
`irtt-rs` is a Rust implementation of an IRTT-compatible client and server.
|
`irtt-rs` is a Rust implementation of an IRTT-compatible client and server:
|
||||||
|
finite or continuous UDP latency probing, plus the server side those probes
|
||||||
|
run against.
|
||||||
|
|
||||||
It provides command-line applets and reusable Rust libraries for finite or continuous latency probing, plus the server side those probes can run against. It is intended for interactive diagnostics, scripting, monitoring, and integration into applications such as network autorate controllers.
|
This is not the upstream IRTT project. For the original implementation,
|
||||||
|
protocol background, and broader documentation, see
|
||||||
This is not the upstream IRTT project. For the original implementation, protocol background, and broader documentation, see [heistp/irtt](https://github.com/heistp/irtt).
|
[heistp/irtt](https://github.com/heistp/irtt). The installed dispatcher is
|
||||||
|
named `irtt-rs` rather than `irtt` to avoid conflicting with or being
|
||||||
|
mistaken for it.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* Finite and continuous probe sessions
|
* Finite and continuous probe sessions
|
||||||
* Concurrent probing of multiple targets
|
* Concurrent probing of multiple targets, with staggered or burst pacing
|
||||||
* Reusable client library with a structured event stream
|
* Reusable client library with a structured event stream (blocking, Tokio,
|
||||||
* Human-readable table output
|
or managed)
|
||||||
* CSV, TSV, and JSON Lines output for scripts and monitoring systems
|
* Human-readable table output, plus CSV, TSV, and JSON Lines for scripts and
|
||||||
* Selectable output columns
|
monitoring systems, with selectable output columns
|
||||||
* Automatic final summaries for eligible table-output runs
|
|
||||||
* Optional terminal UI
|
* Optional terminal UI
|
||||||
* Reusable Tokio-native server library
|
* Reusable Tokio-native server library
|
||||||
* UDP server applet with explicit bind and session policy options
|
* UDP server applet with sensible zero-argument defaults and explicit bind
|
||||||
|
and session-policy options
|
||||||
* Router-friendly multi-applet binary layout
|
* Router-friendly multi-applet binary layout
|
||||||
|
|
||||||
The installed dispatcher is named `irtt-rs` rather than `irtt` to avoid conflicting with or being mistaken for upstream IRTT.
|
## Comparison with upstream IRTT
|
||||||
|
|
||||||
|
`irtt-rs` is an independent, compatible reimplementation, not a successor or
|
||||||
|
replacement for upstream IRTT — [heistp/irtt](https://github.com/heistp/irtt)
|
||||||
|
remains the original implementation. Interoperability is a project goal; see
|
||||||
|
[`docs/INTEROP_COMPARISON.md`](docs/INTEROP_COMPARISON.md) and
|
||||||
|
[`docs/protocol/`](docs/protocol/) for the black-box interoperability
|
||||||
|
methodology and protocol baseline this project verifies against. Choose
|
||||||
|
between them based on the capabilities and deployment model you need.
|
||||||
|
|
||||||
|
The table below was checked against upstream release
|
||||||
|
[v0.9.2](https://github.com/heistp/irtt/releases/tag/v0.9.2) (2026-07-17).
|
||||||
|
This project's own interop testing and protocol baseline are currently
|
||||||
|
pinned to the prior release, v0.9.1; the wire protocol version number is
|
||||||
|
unchanged between the two, but v0.9.2 has not itself been exercised against
|
||||||
|
this project's interop harness.
|
||||||
|
|
||||||
|
| Capability | irtt-rs | upstream IRTT (v0.9.2) |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Wire protocol | Protocol version 1, verified against upstream v0.9.1 | Protocol version 1 (unchanged in v0.9.2) |
|
||||||
|
| Finite probing | Supported, default 10s | Supported, default 1m |
|
||||||
|
| Continuous/indefinite operation | Supported (`--duration 0`); still computes running statistics (see below) | Supported via streaming mode (`-s`, added in v0.9.2); computes no statistics at all in that mode |
|
||||||
|
| Statistics in continuous mode | Running count/min/max/mean/variance per target; exact medians unavailable; bounded IPDV tracking | None in streaming mode — raw/event output only, by design, to avoid unbounded memory |
|
||||||
|
| Multi-target probing (one invocation) | Supported, with labels and staggered/burst pacing | Not supported — one remote address per invocation |
|
||||||
|
| Terminal UI | `irtt-tui`, a live graph/dashboard applet | Not present |
|
||||||
|
| Output formats | table, csv, tsv, jsonl, with selectable columns | Text summary, JSON (optionally gzip to file), and a fixed-field raw mode; no CSV/TSV and no column selection |
|
||||||
|
| Client/server library API | Separate, independently versioned Rust crates (`irtt-client`, `irtt-server`) | One combined Go package exposing both roles |
|
||||||
|
| Server: multiple listeners | `ServerSet` supervises several listeners in one process | `ListenAndServe` also serves multiple requested addresses |
|
||||||
|
| Server: wildcard reply-source | Default behavior on Linux/macOS/FreeBSD; refused elsewhere rather than served incorrectly | Opt-in via `--set-src-ip`, with a documented per-packet allocation cost |
|
||||||
|
| Server: zero-argument bind | Binds the wildcard address on both families by default, gated by the platform check above | Binds the wildcard address on both families by default; no documented platform gate |
|
||||||
|
| Prebuilt release binaries | Published for six target triples | None published; install via `go install` or manual build |
|
||||||
|
|
||||||
|
Upstream has capabilities this project does not implement: a `report`
|
||||||
|
subcommand for replaying saved JSON results, benchmarking and
|
||||||
|
timer/clock-diagnostic subcommands, client-side payload fill and
|
||||||
|
don't-fragment/wait-strategy/timer-algorithm controls, an operator-configurable
|
||||||
|
server-side fill policy (this project's server always honors a session's
|
||||||
|
negotiated fill request, with a fixed fallback pattern, but has no equivalent
|
||||||
|
to upstream's flags for restricting or overriding it), syslog logging, and
|
||||||
|
bundled systemd/OpenRC service files.
|
||||||
|
|
||||||
|
This project has capabilities upstream does not implement: multi-target
|
||||||
|
probing with labels, the terminal UI, CSV/TSV output with selectable
|
||||||
|
columns, a quantified and actively-warned memory-retention model for
|
||||||
|
long-running client sessions, and best-effort Linux kernel-timestamp
|
||||||
|
preference for its own send/receive endpoint.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -32,15 +81,10 @@ Install the latest release from crates.io:
|
|||||||
cargo install irtt-rs --locked
|
cargo install irtt-rs --locked
|
||||||
```
|
```
|
||||||
|
|
||||||
This installs:
|
This installs the `irtt-rs` dispatcher plus dedicated `irtt-client`,
|
||||||
|
`irtt-tui`, and `irtt-server` binaries. Each applet can also be built in
|
||||||
* `irtt-rs`, the canonical multi-applet dispatcher
|
isolation with `--no-default-features` plus the applet's own feature, for
|
||||||
* `irtt-client`, the stream and text client
|
example a lightweight client-only install:
|
||||||
* `irtt-tui`, the terminal UI
|
|
||||||
* `irtt-server`, the UDP server
|
|
||||||
|
|
||||||
Each applet can also be built in isolation with `--no-default-features` plus
|
|
||||||
the applet's own feature. For example, a lightweight client-only install:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cargo install irtt-rs --locked --no-default-features --features client
|
cargo install irtt-rs --locked --no-default-features --features client
|
||||||
@@ -56,356 +100,80 @@ cargo install --path crates/irtt-app --locked
|
|||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
Probe a server using the default settings:
|
Probe a server:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
irtt-client netperf-eu.bufferbloat.net:2112
|
irtt-client netperf-eu.bufferbloat.net:2112 --duration 30s --interval 100ms
|
||||||
```
|
```
|
||||||
|
|
||||||
Set the test duration and probe interval:
|
Probe continuously until interrupted (`Ctrl-C`):
|
||||||
|
|
||||||
```sh
|
|
||||||
irtt-client netperf-eu.bufferbloat.net:2112 \
|
|
||||||
--duration 30s \
|
|
||||||
--interval 100ms
|
|
||||||
```
|
|
||||||
|
|
||||||
Run continuously until interrupted:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
irtt-client netperf-eu.bufferbloat.net:2112 --duration 0
|
irtt-client netperf-eu.bufferbloat.net:2112 --duration 0
|
||||||
```
|
```
|
||||||
|
|
||||||
Use `Ctrl-C` to stop gracefully.
|
Watch the same session live in the terminal UI:
|
||||||
|
|
||||||
### Continuous-mode memory
|
|
||||||
|
|
||||||
`--duration 0` uses continuous statistics. Its retained application state does
|
|
||||||
not grow without bound with elapsed run time or the number of probes: timing
|
|
||||||
metrics keep running count/min/max/mean/variance values, not a complete
|
|
||||||
timing-sample history. Consequently, exact medians are unavailable. The
|
|
||||||
default continuous statistics configuration also enables no rolling
|
|
||||||
event/history window; it keeps bounded adjacent-sequence IPDV state (4,096
|
|
||||||
sequences) so nearby replies can still form IPDV pairs.
|
|
||||||
|
|
||||||
Reply classification is bounded per target session. By default, each of the
|
|
||||||
pending, timed-out, and completed/duplicate sequence stores has its own
|
|
||||||
4,096-entry limit; this is not one shared 4,096-probe total. A full pending
|
|
||||||
store makes the managed CLI/TUI fail and drain that target as resource
|
|
||||||
exhausted; it does not wait for capacity to free. Timed-out and completed state
|
|
||||||
evict their oldest entries, so a sufficiently old late reply can still be seen
|
|
||||||
and counted but no longer has retained send state for measurements such as RTT
|
|
||||||
or applicable one-way delay.
|
|
||||||
|
|
||||||
The retained state scales with the number of configured targets and these
|
|
||||||
fixed per-target limits, rather than total probes. CLI output is written as
|
|
||||||
events arrive rather than accumulated. Its managed presentation stream is
|
|
||||||
bounded and lossy (the applets configure 16,384 events), so a slow consumer can
|
|
||||||
miss events and the CLI/TUI reports that resulting output/statistics may be
|
|
||||||
incomplete. The TUI additionally keeps up to 100,000 graph samples per target
|
|
||||||
and 80 recent messages. These are retained-application-state bounds, not a
|
|
||||||
claim of a fixed process RSS ceiling: allocator behavior, output consumers,
|
|
||||||
and operating-system resources remain outside them.
|
|
||||||
|
|
||||||
For finite runs, the tradeoff is different: exact timing samples (and
|
|
||||||
adjacent-sequence IPDV state) are retained to calculate exact medians, so
|
|
||||||
statistics memory grows with probe count.
|
|
||||||
|
|
||||||
## Multiple targets
|
|
||||||
|
|
||||||
Probe several targets concurrently:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
irtt-client host-a:2112 host-b:2112
|
irtt-tui netperf-eu.bufferbloat.net:2112
|
||||||
```
|
```
|
||||||
|
|
||||||
Every target argument accepts `[LABEL=]TARGET`. An optional `LABEL=` prefix assigns the logical
|
Run a server with sensible defaults — no `--bind` needed:
|
||||||
target name used in output and the TUI; without it, the target string itself is used. Assign
|
|
||||||
stable labels to targets:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
irtt-client ams=ams.example.com:2112 sg=sg.example.com:2112
|
irtt-server
|
||||||
```
|
```
|
||||||
|
|
||||||
Labeled and unlabeled targets can be freely mixed in one argument list:
|
## Documentation
|
||||||
|
|
||||||
```sh
|
* [`docs/irtt-client.md`](docs/irtt-client.md) — target syntax, output
|
||||||
irtt-client host-a:2112 ams=ams.example.com:2112
|
formats and columns, measurement field definitions, finite/continuous
|
||||||
```
|
memory behavior, kernel timestamps.
|
||||||
|
* [`docs/irtt-tui.md`](docs/irtt-tui.md) — controls, continuous default,
|
||||||
|
retained history.
|
||||||
|
* [`docs/irtt-server.md`](docs/irtt-server.md) — bind defaults and
|
||||||
|
overrides, wildcard listener behavior, session policy, HMAC.
|
||||||
|
* [`docs/irtt-rs.md`](docs/irtt-rs.md) — the multicall dispatcher, applet
|
||||||
|
binary names, feature-gated builds.
|
||||||
|
* [docs.rs](https://docs.rs) for the library crates below.
|
||||||
|
* [`docs/protocol/`](docs/protocol/) and
|
||||||
|
[`docs/INTEROP_COMPARISON.md`](docs/INTEROP_COMPARISON.md) for the wire
|
||||||
|
protocol specification and interoperability validation against upstream
|
||||||
|
`irtt`.
|
||||||
|
|
||||||
Multi-target sessions use staggered pacing by default. To send each target's probes together:
|
Every applet also documents its full option list with `--help`.
|
||||||
|
|
||||||
```sh
|
## Crates
|
||||||
irtt-client host-a:2112 host-b:2112 --pacing burst
|
|
||||||
```
|
|
||||||
|
|
||||||
Target labels are included in the default multi-target output. Final per-target summaries are printed in the order targets were supplied on the command line, not the order in which they finish or their labels sort alphabetically.
|
| Crate | crates.io | What it is |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| [`irtt-rs`](crates/irtt-app) | [crates.io](https://crates.io/crates/irtt-rs) | Application package: the `irtt-rs`, `irtt-client`, `irtt-tui`, and `irtt-server` binaries |
|
||||||
|
| [`irtt-client`](crates/irtt-client) | [crates.io](https://crates.io/crates/irtt-client) | Reusable client/session library ([docs.rs](https://docs.rs/irtt-client)) |
|
||||||
|
| [`irtt-server`](crates/irtt-server) | [crates.io](https://crates.io/crates/irtt-server) | Reusable Tokio-native server library ([docs.rs](https://docs.rs/irtt-server)) |
|
||||||
|
| [`irtt-stats`](crates/irtt-stats) | [crates.io](https://crates.io/crates/irtt-stats) | Statistics aggregation over `irtt-client` events ([docs.rs](https://docs.rs/irtt-stats)) |
|
||||||
|
| [`irtt-proto`](crates/irtt-proto) | [crates.io](https://crates.io/crates/irtt-proto) | Low-level wire protocol encode/decode ([docs.rs](https://docs.rs/irtt-proto)) |
|
||||||
|
|
||||||
## Output formats
|
Each library crate has its own crates.io README describing what it's for and
|
||||||
|
when to use it instead of `irtt-client`/`irtt-server` directly.
|
||||||
The CLI supports four event-row formats:
|
|
||||||
|
|
||||||
* `table`: human-readable terminal output
|
|
||||||
* `csv`: comma-separated output
|
|
||||||
* `tsv`: tab-separated output
|
|
||||||
* `jsonl`: one JSON object per event
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
irtt-client <server> --format table
|
|
||||||
irtt-client <server> --format jsonl
|
|
||||||
irtt-client <server> --format csv \
|
|
||||||
--columns event,seq,remote,effective_rtt_us
|
|
||||||
```
|
|
||||||
|
|
||||||
For a stream containing only effective RTT values in microseconds:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
irtt-client <server> \
|
|
||||||
--format tsv \
|
|
||||||
--columns effective_rtt_us \
|
|
||||||
--header never
|
|
||||||
```
|
|
||||||
|
|
||||||
List all available columns with:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
irtt-client --list-columns
|
|
||||||
```
|
|
||||||
|
|
||||||
Useful measurement fields include:
|
|
||||||
|
|
||||||
* `raw_rtt_us`: client-observed send-to-receive RTT
|
|
||||||
* `adjusted_rtt_us`: RTT adjusted for server processing when available
|
|
||||||
* `effective_rtt_us`: adjusted RTT when available, otherwise raw RTT
|
|
||||||
* `sd_us` and `rd_us`: send and receive one-way delay estimates
|
|
||||||
* `ipdv_us`: inter-packet delay variation
|
|
||||||
* `server_processing_us`: time spent processing the packet at the server
|
|
||||||
|
|
||||||
Adjusted RTT can be negative when server processing exceeds the measured raw RTT. One-way delay estimates can be negative because of clock skew between the client and server.
|
|
||||||
|
|
||||||
On Linux, `sd_us` and `rd_us` (and the underlying one-way delay samples) prefer a kernel-captured timestamp over userspace timing for this client's own send/receive endpoint where the platform and socket support it, reducing the effect of scheduling and queuing delay on these measurements. The peer's reported endpoint gets the same preference when it is the `irtt-server` applet running on Linux; against another IRTT-compatible server, whatever wall-clock value it reports is used as-is. This is a best-effort fallback: unsupported or implausible kernel timestamps fall back to userspace timing. `raw_rtt_us` always uses userspace timing and is unaffected; `adjusted_rtt_us`/`effective_rtt_us` can still shift slightly, since server processing time is derived from the same server receive endpoint.
|
|
||||||
|
|
||||||
Default table output prints a final summary after completed finite runs and interrupted continuous runs when the run policy permits it. CSV, TSV, and JSON Lines output do not print this summary.
|
|
||||||
|
|
||||||
A peer closing a finite CLI or TUI run is accepted as a terminal outcome. In
|
|
||||||
continuous mode, peer closure exits nonzero unless the user requested
|
|
||||||
interruption, allowing a supervisor to restart the client.
|
|
||||||
|
|
||||||
## Terminal UI
|
|
||||||
|
|
||||||
When built with the `tui` feature, `irtt-tui` provides a live cumulative dashboard:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
irtt-tui <server>
|
|
||||||
```
|
|
||||||
|
|
||||||
It runs continuously by default. A finite duration can be selected explicitly:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
irtt-tui <server> --duration 30s
|
|
||||||
```
|
|
||||||
|
|
||||||
Multiple targets and pacing options work the same way as in `irtt-client`:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
irtt-tui host-a:2112 host-b:2112 --pacing burst
|
|
||||||
```
|
|
||||||
|
|
||||||
Quit with `q` or `Ctrl-C`.
|
|
||||||
|
|
||||||
## Server
|
|
||||||
|
|
||||||
The server applet requires at least one explicit bind address:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
irtt-server --bind 127.0.0.1:2112
|
|
||||||
```
|
|
||||||
|
|
||||||
The same applet is reachable through the dispatcher:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
irtt-rs server --bind 192.0.2.10:2112
|
|
||||||
```
|
|
||||||
|
|
||||||
Repeat `--bind` to serve several addresses from one process, in the order given:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
irtt-server \
|
|
||||||
--bind 0.0.0.0:2112 \
|
|
||||||
--bind [::]:2112
|
|
||||||
```
|
|
||||||
|
|
||||||
Every invocation runs through the same multi-listener path, so one address is an
|
|
||||||
ordinary set of one rather than a separate mode. The policy options below are
|
|
||||||
applied to every listener, but the listeners are otherwise independent: each has
|
|
||||||
its own sessions and tokens, so a session belongs to the address it was opened
|
|
||||||
on and `--max-sessions` bounds each listener rather than the process. Binding is
|
|
||||||
all or nothing — if any address cannot be bound, none are served — and a port of
|
|
||||||
`0` selects an unused port per listener. If a listener fails while running, the
|
|
||||||
others are shut down with it rather than leaving a service configured for two
|
|
||||||
families answering on one.
|
|
||||||
|
|
||||||
An explicit interface address works on every supported system. A wildcard bind
|
|
||||||
such as `--bind 0.0.0.0:2112` reads each request's local destination from packet
|
|
||||||
metadata and sends that request's reply from the same address, so a client on a
|
|
||||||
multi-homed host is answered from the endpoint it contacted. That is implemented
|
|
||||||
on Linux, macOS and FreeBSD; elsewhere a wildcard bind is refused rather than
|
|
||||||
served from a routing-table source address a client would discard. Wildcard
|
|
||||||
IPv4 and IPv6 listeners may share one port, as above.
|
|
||||||
|
|
||||||
Each bound endpoint is printed on startup once every listener is up, which also
|
|
||||||
resolves a port of `0`. `Ctrl-C` stops the server gracefully.
|
|
||||||
|
|
||||||
Session policy is set with options that map directly onto the server library's
|
|
||||||
configuration; anything left unset keeps the library default:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
irtt-server \
|
|
||||||
--bind 192.0.2.10:2112 \
|
|
||||||
--max-sessions 512 \
|
|
||||||
--idle-timeout 30s
|
|
||||||
```
|
|
||||||
|
|
||||||
Two optional controls restrict what the server will provide a session, and both
|
|
||||||
are off unless asked for:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
irtt-server \
|
|
||||||
--bind 192.0.2.10:2112 \
|
|
||||||
--timestamp-allowance single \
|
|
||||||
--no-dscp
|
|
||||||
```
|
|
||||||
|
|
||||||
`--timestamp-allowance` takes `dual` (the default, honoring every requested
|
|
||||||
placement), `single`, which provides at most one timestamp instant and answers a
|
|
||||||
request for both receive and send timestamps with the midpoint, or `none`, which
|
|
||||||
provides no timestamps. The requested clock source is never changed, so a single
|
|
||||||
instant is still reported once per requested clock. `--no-dscp` negotiates any
|
|
||||||
requested DSCP to zero, so clients are told their echo replies will be unmarked,
|
|
||||||
and they are sent unmarked.
|
|
||||||
|
|
||||||
To require authentication, pass the shared key both sides use:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
irtt-server --bind 192.0.2.10:2112 --hmac secret
|
|
||||||
```
|
|
||||||
|
|
||||||
For the full option list:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
irtt-server --help
|
|
||||||
```
|
|
||||||
|
|
||||||
## Library usage
|
|
||||||
|
|
||||||
`irtt-client` exposes the client session and event layer independently of CLI formatting and statistics.
|
|
||||||
|
|
||||||
Add it from crates.io:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[dependencies]
|
|
||||||
irtt-client = { version = "0.5", features = ["tokio"] }
|
|
||||||
```
|
|
||||||
|
|
||||||
For development against an unpublished change, use a path dependency instead:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[dependencies]
|
|
||||||
irtt-client = { path = "crates/irtt-client", features = ["tokio"] }
|
|
||||||
```
|
|
||||||
|
|
||||||
Minimal managed-client example for a synchronous frontend:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use irtt_client::managed::{
|
|
||||||
BlockingManagedClient, ManagedClientConfig, ManagedCompletionPolicy,
|
|
||||||
ManagedTargetConfig, TargetId,
|
|
||||||
};
|
|
||||||
use irtt_client::ClientConfig;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let config = ManagedClientConfig {
|
|
||||||
client: ClientConfig {
|
|
||||||
duration: Some(Duration::from_secs(10)),
|
|
||||||
interval: Duration::from_secs(1),
|
|
||||||
..ClientConfig::default()
|
|
||||||
},
|
|
||||||
completion: ManagedCompletionPolicy::FinishWhenQuiescent,
|
|
||||||
..ManagedClientConfig::default()
|
|
||||||
};
|
|
||||||
let targets = vec![ManagedTargetConfig::new(
|
|
||||||
TargetId::from("edge"),
|
|
||||||
"netperf-eu.bufferbloat.net:2112",
|
|
||||||
)];
|
|
||||||
let owner = BlockingManagedClient::start(config, targets)?;
|
|
||||||
let outcome = owner.join()?;
|
|
||||||
println!("session ended: {:?}", outcome.end_reason);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
For presentation events, use `BlockingManagedClient::start_with_subscription`,
|
|
||||||
`ManagedEvent`, and `ManagedEventTryRecvError`, while consulting the durable
|
|
||||||
`handle.status()` and final outcome for authoritative state. Presentation
|
|
||||||
events are bounded and lossy, not reliable history. Dynamic desired-target
|
|
||||||
updates are available through `ManagedClientHandle::update_targets`.
|
|
||||||
|
|
||||||
`BlockingManagedClient` owns a dedicated Tokio runtime and thread for
|
|
||||||
synchronous callers. Callers that already own Tokio can drive
|
|
||||||
`ManagedClientTask` directly.
|
|
||||||
|
|
||||||
## Binaries and features
|
## Binaries and features
|
||||||
|
|
||||||
| Build | Binaries |
|
| Build | Binaries |
|
||||||
| -------------------------------------------- | --------------------------------------------------- |
|
| --- | --- |
|
||||||
| `--no-default-features` | `irtt-rs` |
|
| `--no-default-features` | `irtt-rs` |
|
||||||
| `--no-default-features --features client` | `irtt-rs`, `irtt-client` |
|
| `--no-default-features --features client` | `irtt-rs`, `irtt-client` |
|
||||||
| `--no-default-features --features server` | `irtt-rs`, `irtt-server` |
|
| `--no-default-features --features server` | `irtt-rs`, `irtt-server` |
|
||||||
| `--no-default-features --features tui` | `irtt-rs`, `irtt-tui` |
|
| `--no-default-features --features tui` | `irtt-rs`, `irtt-tui` |
|
||||||
| Default features (or `--all-features`) | `irtt-rs`, `irtt-client`, `irtt-tui`, `irtt-server` |
|
| Default features (or `--all-features`) | `irtt-rs`, `irtt-client`, `irtt-tui`, `irtt-server` |
|
||||||
|
|
||||||
`irtt-client` requires the `client` feature, `irtt-server` the `server` feature,
|
|
||||||
and `irtt-tui` the `tui` feature. The `irtt-rs` dispatcher is always built, and
|
|
||||||
reports which applets the build actually contains.
|
|
||||||
|
|
||||||
For available command-line options:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
irtt-client --help
|
|
||||||
irtt-server --help
|
|
||||||
irtt-tui --help
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
Common verification commands:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cargo fmt --check
|
cargo fmt --all -- --check
|
||||||
cargo test --workspace
|
cargo test --workspace --locked
|
||||||
cargo clippy --workspace --all-targets -- -D warnings
|
cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
|
||||||
cargo build -p irtt-rs --all-features --release
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Project status
|
See [`AGENTS.md`](AGENTS.md) for architecture, testing policy, and the
|
||||||
|
clean-room provenance boundary this project maintains against upstream
|
||||||
The client, event stream, machine-readable output, multi-target execution, local statistics, and optional TUI are implemented.
|
`irtt`.
|
||||||
|
|
||||||
The server library and the server applet are implemented: open negotiation,
|
|
||||||
session state, echo processing, per-session rate limiting, idle expiry, the
|
|
||||||
maximum-duration close, HMAC authentication, the negotiated per-session reply
|
|
||||||
traffic class, which is applied on sockets that support it, server fill,
|
|
||||||
wildcard reply-source selection on Linux, macOS and FreeBSD, the optional
|
|
||||||
timestamp and DSCP restriction controls, and serving several listeners from one
|
|
||||||
process with independent sessions per listener.
|
|
||||||
|
|
||||||
Server replies fill their payload with the negotiated ServerFill mode: `none`,
|
|
||||||
which is zero-filled, `rand`, or `pattern:` with a repeating hexadecimal
|
|
||||||
pattern. Every valid descriptor is honored; a descriptor the server cannot
|
|
||||||
parse, and a client that expresses no fill preference at all, get the default
|
|
||||||
`pattern:69727474`.
|
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
# irtt-client
|
||||||
|
|
||||||
|
## NAME
|
||||||
|
|
||||||
|
irtt-client - IRTT-compatible stream client
|
||||||
|
|
||||||
|
## SYNOPSIS
|
||||||
|
|
||||||
|
`irtt-client` [*OPTIONS*] [*[LABEL=]TARGET*]...
|
||||||
|
|
||||||
|
`irtt-client` `--list-columns`
|
||||||
|
|
||||||
|
## DESCRIPTION
|
||||||
|
|
||||||
|
`irtt-client` probes one or more IRTT-compatible servers over UDP and prints
|
||||||
|
per-probe measurement events as they arrive, plus a final summary for
|
||||||
|
eligible runs.
|
||||||
|
|
||||||
|
## TARGETS
|
||||||
|
|
||||||
|
Every target argument accepts `[LABEL=]TARGET`. An optional `LABEL=` prefix
|
||||||
|
assigns the logical target name used in output; without it, the target
|
||||||
|
string itself is used as the label. Labeled and unlabeled targets can be
|
||||||
|
freely mixed in one argument list.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
irtt-client host-a:2112
|
||||||
|
irtt-client eu=host-a:2112 us=host-b:2112
|
||||||
|
irtt-client host-a:2112 eu=host-b:2112
|
||||||
|
```
|
||||||
|
|
||||||
|
Multiple targets are probed concurrently. By default, a multi-target session
|
||||||
|
uses **staggered** pacing, spacing active targets across the probe interval;
|
||||||
|
pass `--pacing burst` to send one probe to every active target back-to-back
|
||||||
|
once per interval instead. Final per-target summaries print in the order
|
||||||
|
targets were supplied on the command line, not the order in which they
|
||||||
|
finish or their labels sort alphabetically.
|
||||||
|
|
||||||
|
## FINITE VERSUS CONTINUOUS OPERATION
|
||||||
|
|
||||||
|
`--duration` controls the run length:
|
||||||
|
|
||||||
|
- Any nonzero duration (default `10s`) is a **finite** run: probing stops
|
||||||
|
after that long, and a final summary is always printed for table output
|
||||||
|
(see [OUTPUT](#output) below — CSV, TSV, and JSON Lines never print one).
|
||||||
|
- `--duration 0` is **continuous**: the client runs until interrupted
|
||||||
|
(`Ctrl-C`). For table output, a final summary is printed only if the run
|
||||||
|
was interrupted; an uninterrupted continuous run (one that exits for
|
||||||
|
another reason) does not get one.
|
||||||
|
|
||||||
|
See [MEASUREMENTS AND MEMORY](#measurements-and-memory) below for why this
|
||||||
|
distinction also determines what statistics can be computed and how much
|
||||||
|
memory a long run retains.
|
||||||
|
|
||||||
|
## OUTPUT
|
||||||
|
|
||||||
|
### `--format FORMAT`
|
||||||
|
|
||||||
|
One of four event-row formats, default `table`:
|
||||||
|
|
||||||
|
- `table`: human-readable terminal output; the only format that prints a
|
||||||
|
final summary.
|
||||||
|
- `csv`: comma-separated output.
|
||||||
|
- `tsv`: tab-separated output.
|
||||||
|
- `jsonl`: one JSON object per event.
|
||||||
|
|
||||||
|
CSV, TSV, and JSON Lines default to **all** columns for structured export;
|
||||||
|
`table` defaults to a compact column set and hides `echo_sent` rows. An
|
||||||
|
explicit `--columns` selection other than `default` shows all event rows,
|
||||||
|
including `echo_sent`; `--columns default` is equivalent to omitting the
|
||||||
|
option and keeps `echo_sent` hidden for table output.
|
||||||
|
|
||||||
|
### `--columns COLUMNS` / `-c COLUMNS`
|
||||||
|
|
||||||
|
Comma-separated event row columns, or the special values `default` (the
|
||||||
|
format's default set) or `all` (every column). List every available column,
|
||||||
|
its meaning, and its aliases with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
irtt-client --list-columns
|
||||||
|
```
|
||||||
|
|
||||||
|
(`--list-columns` does not require a target.)
|
||||||
|
|
||||||
|
### `--header auto|always|never`
|
||||||
|
|
||||||
|
Header policy for `table`, `csv`, and `tsv` output (default `auto`). JSON
|
||||||
|
Lines never prints a header line.
|
||||||
|
|
||||||
|
### `--verbose`
|
||||||
|
|
||||||
|
Includes extra fields in table output and final summaries. Has no effect on
|
||||||
|
CSV, TSV, or JSON Lines output either way — those formats show their default
|
||||||
|
all-column set or an explicit `--columns` selection exactly as given,
|
||||||
|
regardless of `--verbose`.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```sh
|
||||||
|
irtt-client <server> --format table
|
||||||
|
irtt-client <server> --format jsonl
|
||||||
|
irtt-client <server> --format csv --columns event,seq,remote,effective_rtt_us
|
||||||
|
```
|
||||||
|
|
||||||
|
A stream containing only effective RTT values in microseconds:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
irtt-client <server> --format tsv --columns effective_rtt_us --header never
|
||||||
|
```
|
||||||
|
|
||||||
|
## MEASUREMENT FIELDS
|
||||||
|
|
||||||
|
Useful measurement columns:
|
||||||
|
|
||||||
|
- `raw_rtt_us`: client-observed send-to-receive RTT. Always userspace-timed
|
||||||
|
and unaffected by kernel timestamp preference (below).
|
||||||
|
- `adjusted_rtt_us`: RTT adjusted for server processing when available. Can
|
||||||
|
be negative when server processing exceeds the measured raw RTT.
|
||||||
|
- `effective_rtt_us`: `adjusted_rtt_us` when available, otherwise
|
||||||
|
`raw_rtt_us`.
|
||||||
|
- `rd_us` / `sd_us` (selectable with the longer aliases `receive_delay_us` /
|
||||||
|
`send_delay_us`, but always emitted under the short names): one-way delay
|
||||||
|
estimates. Can be negative because of clock skew between client and
|
||||||
|
server.
|
||||||
|
- `ipdv_us`: inter-packet delay variation.
|
||||||
|
- `server_processing_us`: time spent processing the packet at the server.
|
||||||
|
- `kernel_rx_ns`: kernel receive timestamp, in Unix nanoseconds, when
|
||||||
|
captured (see below).
|
||||||
|
|
||||||
|
### Kernel timestamps (Linux)
|
||||||
|
|
||||||
|
On Linux, one-way delay estimates prefer a kernel-captured timestamp over
|
||||||
|
userspace timing for this client's own send/receive endpoint, where the
|
||||||
|
platform and socket support it, reducing the effect of scheduling and
|
||||||
|
queuing delay. The peer's reported endpoint gets the same preference when it
|
||||||
|
is the `irtt-server` applet running on Linux; against another
|
||||||
|
IRTT-compatible server, whatever wall-clock value it reports is used as-is.
|
||||||
|
This is best-effort: an unsupported or implausible kernel timestamp falls
|
||||||
|
back to userspace timing. `raw_rtt_us` is unaffected either way;
|
||||||
|
`adjusted_rtt_us`/`effective_rtt_us` can still shift slightly, since server
|
||||||
|
processing time is derived from the same server receive endpoint.
|
||||||
|
|
||||||
|
## MEASUREMENTS AND MEMORY
|
||||||
|
|
||||||
|
Finite and continuous modes make a different tradeoff between exact
|
||||||
|
statistics and bounded memory.
|
||||||
|
|
||||||
|
**Finite mode** retains exact timing samples (and adjacent-sequence IPDV
|
||||||
|
state) so the final summary can report exact medians. Retained memory
|
||||||
|
therefore grows with the number of probes sent, at a small constant cost per
|
||||||
|
probe (on the order of a few hundred bytes of retained sample state per
|
||||||
|
probe per target, before allocator overhead) — a very long finite run at a
|
||||||
|
short interval is the case to plan capacity for. The client warns on stderr
|
||||||
|
when its own estimate of this retained state crosses roughly 128 MiB, and
|
||||||
|
again more strongly past roughly 512 MiB and 1 GiB; treat those numbers as
|
||||||
|
practical guidance, not a promised RSS ceiling, since allocator and OS
|
||||||
|
overhead sit outside the estimate.
|
||||||
|
|
||||||
|
**Continuous mode** (`--duration 0`) does not retain a growing sample
|
||||||
|
history: running statistics keep count/min/max/mean/variance only, so exact
|
||||||
|
medians are unavailable. It still keeps a bounded adjacent-sequence IPDV
|
||||||
|
store, capped at 4,096 sequences per target, so nearby replies can still
|
||||||
|
form IPDV pairs. Retained statistics state therefore approaches a fixed
|
||||||
|
per-target bound rather than growing with elapsed run time.
|
||||||
|
|
||||||
|
Reply classification (independent of the statistics choice above) is also
|
||||||
|
bounded per target: each of the pending, timed-out, and completed/duplicate
|
||||||
|
sequence stores has its own 4,096-entry limit — not one shared total across
|
||||||
|
the three. A full pending store makes the client fail and drain that target
|
||||||
|
as resource-exhausted rather than wait for capacity to free. Timed-out and
|
||||||
|
completed state evict their oldest entries, so a sufficiently old late reply
|
||||||
|
can still be seen and counted, but no longer has retained send state for
|
||||||
|
measurements such as RTT.
|
||||||
|
|
||||||
|
Overall, retained state scales with the **number of configured targets**
|
||||||
|
and these fixed per-target limits, not with total probes sent in continuous
|
||||||
|
mode. Output is written as events arrive rather than accumulated; the
|
||||||
|
managed event stream feeding it is itself bounded and lossy (16,384 events),
|
||||||
|
so a sufficiently slow consumer can miss events, in which case the client
|
||||||
|
reports that resulting output/statistics may be incomplete.
|
||||||
|
|
||||||
|
For capacity planning on constrained devices (for example an OpenWrt
|
||||||
|
router): a finite run's statistics memory is roughly proportional to
|
||||||
|
`targets × probes_sent`, while a continuous run's statistics memory
|
||||||
|
approaches a fixed multiple of `targets` regardless of how long it runs.
|
||||||
|
|
||||||
|
## PEER CLOSE AND EXIT BEHAVIOR
|
||||||
|
|
||||||
|
A peer closing a finite run is accepted as a terminal, successful outcome.
|
||||||
|
In continuous mode, an uninterrupted peer closure is treated as an error —
|
||||||
|
the client exits nonzero — so a process supervisor can restart it; an
|
||||||
|
interrupted run (`Ctrl-C`) never reports peer closure as an error, whichever
|
||||||
|
mode it was in.
|
||||||
|
|
||||||
|
## EXIT STATUS
|
||||||
|
|
||||||
|
`0` on success (including a finite run's ordinary completion, and an
|
||||||
|
interrupted run of either mode). Nonzero if the managed driver failed, if an
|
||||||
|
uninterrupted continuous run ended by peer closure, or if the run was not
|
||||||
|
interrupted and no target completed successfully. An interrupted run never
|
||||||
|
fails on the last of these conditions — for example, interrupting a
|
||||||
|
continuous run whose only target never opened still exits `0`.
|
||||||
|
|
||||||
|
## SERVER FILL REQUEST
|
||||||
|
|
||||||
|
`--sfill DESCRIPTOR` requests the server's echo-reply payload fill: `none`
|
||||||
|
(zero-filled), `rand` (random bytes), or `pattern:HEX` (a repeating
|
||||||
|
hexadecimal pattern). See `irtt-server(1)` SERVER FILL for what the server
|
||||||
|
does with an unset or unparseable request.
|
||||||
|
|
||||||
|
## EXAMPLES
|
||||||
|
|
||||||
|
```sh
|
||||||
|
irtt-client netperf-eu.bufferbloat.net:2112
|
||||||
|
irtt-client netperf-eu.bufferbloat.net:2112 --duration 30s --interval 100ms
|
||||||
|
irtt-client netperf-eu.bufferbloat.net:2112 --duration 0
|
||||||
|
irtt-client host-a:2112 host-b:2112 --pacing burst
|
||||||
|
```
|
||||||
|
|
||||||
|
## SEE ALSO
|
||||||
|
|
||||||
|
`irtt-tui(1)`, `irtt-server(1)`, `irtt-rs(1)`, and `irtt-client --help` for
|
||||||
|
the full option list, including HMAC, clock/timestamp negotiation, DSCP, and
|
||||||
|
TTL flags.
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# irtt-rs
|
||||||
|
|
||||||
|
## NAME
|
||||||
|
|
||||||
|
irtt-rs - IRTT-compatible multi-applet dispatcher
|
||||||
|
|
||||||
|
## SYNOPSIS
|
||||||
|
|
||||||
|
`irtt-rs` `client` [*OPTIONS*] [*[LABEL=]TARGET*]...
|
||||||
|
|
||||||
|
`irtt-rs` `tui` [*OPTIONS*] *[LABEL=]TARGET*...
|
||||||
|
|
||||||
|
`irtt-rs` `server` [*OPTIONS*]
|
||||||
|
|
||||||
|
## DESCRIPTION
|
||||||
|
|
||||||
|
`irtt-rs` is a multicall dispatcher: one binary that runs the client, TUI,
|
||||||
|
or server applet depending on the subcommand given, or on the name it was
|
||||||
|
invoked/copied as. It exists for router-friendly single-binary deployment;
|
||||||
|
it does not add behavior beyond what `irtt-client(1)`, `irtt-tui(1)`, and
|
||||||
|
`irtt-server(1)` already provide.
|
||||||
|
|
||||||
|
Each subcommand behaves exactly like its dedicated binary — `irtt-rs client
|
||||||
|
...` is equivalent to running `irtt-client ...` directly. See those manuals
|
||||||
|
for target syntax, options, output, and exit behavior.
|
||||||
|
|
||||||
|
## APPLET BINARY NAMES
|
||||||
|
|
||||||
|
The build also produces dedicated binaries: `irtt-client`, `irtt-tui`, and
|
||||||
|
`irtt-server`. A dedicated binary always runs its own role, regardless of
|
||||||
|
what it is invoked or copied as — it does not consult argv0 dispatch logic.
|
||||||
|
|
||||||
|
`irtt-rs` itself inspects argv0 first: if invoked (or symlinked/copied) under
|
||||||
|
one of the recognized applet names — `irtt-client`, `irtt-tui`, `irttd`, or
|
||||||
|
`irtt-server` — it runs that applet directly, without needing a subcommand.
|
||||||
|
Any other name starting with `irtt-` (other than `irtt-rs` itself) is
|
||||||
|
rejected as an unknown applet name before subcommand parsing is even
|
||||||
|
attempted — `irtt-custom server` is a hard error, not a fallthrough. Only
|
||||||
|
`irtt-rs` itself, or a name that doesn't start with `irtt-` at all, falls
|
||||||
|
through to ordinary `client`/`tui`/`server` subcommand parsing.
|
||||||
|
|
||||||
|
## FEATURE-DEPENDENT APPLET AVAILABILITY
|
||||||
|
|
||||||
|
Each applet is gated by a Cargo feature on the `irtt-rs` package: `client`,
|
||||||
|
`tui`, `server`. All three are in the default feature set. A build with a
|
||||||
|
feature disabled still produces the `irtt-rs` binary, but that applet is
|
||||||
|
unavailable at runtime — running it (by subcommand or by binary name)
|
||||||
|
reports which feature to rebuild with, and `irtt-rs --help` lists which
|
||||||
|
applets the current build actually contains.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo install irtt-rs --locked --no-default-features --features client
|
||||||
|
```
|
||||||
|
|
||||||
|
## RELATIONSHIP TO irtt-client, irtt-tui, irtt-server
|
||||||
|
|
||||||
|
`irtt-rs` is packaging and dispatch only. All probing, display, and server
|
||||||
|
behavior lives in `irtt-client(1)`, `irtt-tui(1)`, and `irtt-server(1)` —
|
||||||
|
consult those manuals for anything observable about running a probe or
|
||||||
|
serving one.
|
||||||
|
|
||||||
|
## SEE ALSO
|
||||||
|
|
||||||
|
`irtt-client(1)`, `irtt-tui(1)`, `irtt-server(1)`.
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
# irtt-server
|
||||||
|
|
||||||
|
## NAME
|
||||||
|
|
||||||
|
irtt-server - IRTT-compatible UDP server
|
||||||
|
|
||||||
|
## SYNOPSIS
|
||||||
|
|
||||||
|
`irtt-server` [`--bind` *ADDR*]... [*OPTIONS*]
|
||||||
|
|
||||||
|
`irtt-rs server` [`--bind` *ADDR*]... [*OPTIONS*]
|
||||||
|
|
||||||
|
## DESCRIPTION
|
||||||
|
|
||||||
|
`irtt-server` answers IRTT echo requests over UDP. It is thin orchestration
|
||||||
|
over the reusable `irtt-server` library: one current-thread Tokio runtime,
|
||||||
|
one or more listeners, and the library's own session/policy logic.
|
||||||
|
|
||||||
|
Run with no arguments for the ordinary case:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
irtt-server
|
||||||
|
```
|
||||||
|
|
||||||
|
With no `--bind`, the server listens on the standard IRTT port (2112) on the
|
||||||
|
wildcard address for both address families, in this fixed order:
|
||||||
|
|
||||||
|
1. `[::]:2112`
|
||||||
|
2. `0.0.0.0:2112`
|
||||||
|
|
||||||
|
This is available wherever the platform supports safe wildcard reply-source
|
||||||
|
selection (see [Wildcard binds](#wildcard-binds) below). Any explicit
|
||||||
|
`--bind` **replaces** this default pair entirely rather than adding to it —
|
||||||
|
`irtt-server --bind 127.0.0.1:2112` binds only that one address.
|
||||||
|
|
||||||
|
If one of the two default addresses cannot be bound because its address
|
||||||
|
family itself has no local support at all — IPv6 disabled on an otherwise
|
||||||
|
IPv4-capable host, for example — the server falls back to serving just the
|
||||||
|
other one instead of failing outright, and says so on startup. Any other
|
||||||
|
reason a default listener fails — the port already in use, permission
|
||||||
|
denied, no safe wildcard reply-source path — is not a family-support
|
||||||
|
problem and is not silently worked around; the invocation fails as it would
|
||||||
|
for an explicit `--bind`. This fallback applies only to the implicit default
|
||||||
|
pair: an explicit `--bind` list always keeps the ordinary all-or-nothing
|
||||||
|
behavior described below.
|
||||||
|
|
||||||
|
## OPTIONS
|
||||||
|
|
||||||
|
### `--bind ADDR`
|
||||||
|
|
||||||
|
Local UDP address to bind, as `ADDR:PORT`, for example `127.0.0.1:2112` or
|
||||||
|
`[::1]:2112`. Host names are not resolved. Repeatable.
|
||||||
|
|
||||||
|
Repeat the option to serve several addresses from one process, in the order
|
||||||
|
given. Every listener applies the same policy options, but each keeps its
|
||||||
|
own sessions and tokens, so a session belongs to the address it was opened
|
||||||
|
on. Binding is all-or-nothing: if any address cannot be bound, none are
|
||||||
|
served.
|
||||||
|
|
||||||
|
A port of `0` selects an unused port per listener, so two such binds get two
|
||||||
|
different ports.
|
||||||
|
|
||||||
|
With no `--bind` at all, see [Default bind](#default-bind) above.
|
||||||
|
|
||||||
|
### `--hmac KEY`
|
||||||
|
|
||||||
|
HMAC key, taken as the UTF-8 bytes of this argument. With a key configured,
|
||||||
|
every request must carry a valid MAC and every reply is authenticated;
|
||||||
|
without one, authenticated requests are dropped. The key is visible in the
|
||||||
|
process arguments.
|
||||||
|
|
||||||
|
### `--max-sessions COUNT`
|
||||||
|
|
||||||
|
Maximum number of simultaneously live sessions **per listener**, not per
|
||||||
|
process. Once a listener's table is full, a session-creating open to it is
|
||||||
|
dropped silently; nothing is evicted. Zero refuses every session-creating
|
||||||
|
open.
|
||||||
|
|
||||||
|
### `--max-packet-length BYTES`
|
||||||
|
|
||||||
|
Maximum echo datagram size a session may negotiate, in bytes. A longer
|
||||||
|
requested length is reduced during negotiation, an open whose mandatory
|
||||||
|
field block would not fit is refused, and inbound echo requests are admitted
|
||||||
|
against the same limit. This is a resource bound, not an MTU.
|
||||||
|
|
||||||
|
### `--min-interval DURATION`
|
||||||
|
|
||||||
|
Floor on the send interval a session may negotiate. The negotiated interval
|
||||||
|
is still capped at a quarter of the idle timeout afterwards, so a
|
||||||
|
`--min-interval` above that cap is not the value actually negotiated.
|
||||||
|
|
||||||
|
A session's reply allowance refills at the shorter of this configured floor
|
||||||
|
and the interval it actually negotiated. Ordinarily that is the configured
|
||||||
|
floor itself, since the negotiated interval is normally at least as long —
|
||||||
|
a 10ms `--min-interval` against a 1s negotiated interval refills every
|
||||||
|
10ms. The two differ only when the idle-timeout cap above pulled the
|
||||||
|
negotiated interval below the configured floor, in which case the shorter,
|
||||||
|
negotiated value is what the allowance actually refills at: a server must
|
||||||
|
not enforce a cadence it did not advertise to the client. Use `0` for no
|
||||||
|
time-based throttling.
|
||||||
|
|
||||||
|
### `--burst COUNT`
|
||||||
|
|
||||||
|
Echo replies one session may have answered before its allowance has to
|
||||||
|
refill. Use `0` for no allowance at all, which rate-limits every echo
|
||||||
|
request whatever the interval is.
|
||||||
|
|
||||||
|
### `--idle-timeout DURATION`
|
||||||
|
|
||||||
|
Release a session after this long without a served or rate-limited echo
|
||||||
|
request. The deadline runs from the open, and release is silent. Use `0` to
|
||||||
|
expire a session at the next evaluation; it is not a way to disable expiry.
|
||||||
|
|
||||||
|
### `--max-duration DURATION`
|
||||||
|
|
||||||
|
Maximum test duration a session may negotiate. A longer request is reduced
|
||||||
|
to it, and a continuous request is answered with it. Omit this flag for no
|
||||||
|
maximum; a maximum of zero cannot be expressed, because a negotiated
|
||||||
|
duration of zero means continuous.
|
||||||
|
|
||||||
|
### `--timestamp-allowance MODE`
|
||||||
|
|
||||||
|
How many timestamps this server will provide: `dual` (default, honors a
|
||||||
|
request for send, receive, both, or midpoint timestamps), `single`
|
||||||
|
(provides at most one timestamp instant; a request for both is negotiated to
|
||||||
|
midpoint), or `none` (no timestamps at all). The requested clock source is
|
||||||
|
never changed, only which instants are reported, so a single instant is
|
||||||
|
still reported once per requested clock.
|
||||||
|
|
||||||
|
### `--no-dscp`
|
||||||
|
|
||||||
|
Refuse to provide requested traffic-class marking. Any requested DSCP is
|
||||||
|
negotiated to zero, so the client is told its echo replies will be unmarked,
|
||||||
|
and they are sent unmarked. The session is not refused.
|
||||||
|
|
||||||
|
Durations everywhere in this command accept an integer followed by a unit:
|
||||||
|
`ms`, `s`, or `m`.
|
||||||
|
|
||||||
|
## OUTPUT
|
||||||
|
|
||||||
|
Each bound endpoint is printed on startup, once every listener is up, which
|
||||||
|
also resolves a port of `0`:
|
||||||
|
|
||||||
|
```
|
||||||
|
irtt-server: listening on [::]:2112
|
||||||
|
irtt-server: listening on 0.0.0.0:2112
|
||||||
|
```
|
||||||
|
|
||||||
|
`Ctrl-C` stops the server gracefully.
|
||||||
|
|
||||||
|
## MULTIPLE LISTENERS
|
||||||
|
|
||||||
|
Every invocation runs through the same multi-listener path, so one address —
|
||||||
|
whether from the default pair or a single explicit `--bind` — is an ordinary
|
||||||
|
set of one rather than a separate mode. The policy options above apply to
|
||||||
|
every listener, but listeners are otherwise independent: each has its own
|
||||||
|
sessions and tokens, and `--max-sessions` bounds each listener rather than
|
||||||
|
the process. If any listener fails while running, the others are shut down
|
||||||
|
with it rather than leaving a service configured for two families answering
|
||||||
|
on one.
|
||||||
|
|
||||||
|
Wildcard IPv4 and IPv6 listeners may share one port, as the default pair
|
||||||
|
does.
|
||||||
|
|
||||||
|
## SERVER FILL
|
||||||
|
|
||||||
|
Echo replies fill their payload according to the negotiated `ServerFill`
|
||||||
|
mode, which a client requests with `irtt-client`'s `--sfill` (see
|
||||||
|
`irtt-client(1)`):
|
||||||
|
|
||||||
|
- `none`: zero-filled.
|
||||||
|
- `rand`: random bytes.
|
||||||
|
- `pattern:HEX`: the given repeating hexadecimal pattern.
|
||||||
|
|
||||||
|
Every valid descriptor is honored. A descriptor the server cannot parse, and
|
||||||
|
a client that expresses no fill preference at all, both get the default
|
||||||
|
`pattern:69727474`, i.e. the repeating bytes `69 72 74 74` (`irtt`).
|
||||||
|
|
||||||
|
## WILDCARD BINDS
|
||||||
|
|
||||||
|
An explicit interface address works on every supported system. A wildcard
|
||||||
|
bind such as `0.0.0.0:2112` or `[::]:2112` reads each request's local
|
||||||
|
destination from packet metadata and sends that request's reply from the
|
||||||
|
same address, so a client on a multi-homed host is answered from the
|
||||||
|
endpoint it actually contacted.
|
||||||
|
|
||||||
|
That reply-source selection is implemented on **Linux, macOS, and FreeBSD**.
|
||||||
|
Elsewhere, a wildcard bind is refused at construction rather than served
|
||||||
|
from a routing-table-selected source address a client would discard — this
|
||||||
|
also applies to the zero-argument default, which will fail with a clear
|
||||||
|
error asking for an explicit `--bind` on such a platform. This correctness
|
||||||
|
rule is deliberate and is not weakened by the zero-argument default.
|
||||||
|
|
||||||
|
## EXAMPLES
|
||||||
|
|
||||||
|
Run with the default wildcard listeners on the standard port:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
irtt-server
|
||||||
|
```
|
||||||
|
|
||||||
|
Bind a single explicit address:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
irtt-server --bind 127.0.0.1:2112
|
||||||
|
```
|
||||||
|
|
||||||
|
Through the `irtt-rs` dispatcher:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
irtt-rs server --bind 192.0.2.10:2112
|
||||||
|
```
|
||||||
|
|
||||||
|
Serve two explicit addresses from one process:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
irtt-server \
|
||||||
|
--bind 0.0.0.0:2112 \
|
||||||
|
--bind [::]:2112
|
||||||
|
```
|
||||||
|
|
||||||
|
Apply session policy:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
irtt-server \
|
||||||
|
--bind 192.0.2.10:2112 \
|
||||||
|
--max-sessions 512 \
|
||||||
|
--idle-timeout 30s
|
||||||
|
```
|
||||||
|
|
||||||
|
Restrict optional capabilities:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
irtt-server \
|
||||||
|
--bind 192.0.2.10:2112 \
|
||||||
|
--timestamp-allowance single \
|
||||||
|
--no-dscp
|
||||||
|
```
|
||||||
|
|
||||||
|
Require authentication:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
irtt-server --bind 192.0.2.10:2112 --hmac secret
|
||||||
|
```
|
||||||
|
|
||||||
|
## EXIT STATUS
|
||||||
|
|
||||||
|
`0` on a clean shutdown (`Ctrl-C`), nonzero if binding fails or a listener
|
||||||
|
fails while running.
|
||||||
|
|
||||||
|
## LIMITATIONS
|
||||||
|
|
||||||
|
- No hostname resolution, interface expansion, daemonization, or
|
||||||
|
configuration file — binds are explicit addresses only.
|
||||||
|
- Wildcard reply-source selection is unavailable outside Linux, macOS, and
|
||||||
|
FreeBSD; such platforms must bind explicit addresses.
|
||||||
|
|
||||||
|
## SEE ALSO
|
||||||
|
|
||||||
|
`irtt-client(1)`, `irtt-tui(1)`, `irtt-rs(1)`, and `irtt-server --help` for
|
||||||
|
the full option list.
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# irtt-tui
|
||||||
|
|
||||||
|
## NAME
|
||||||
|
|
||||||
|
irtt-tui - IRTT-compatible terminal UI client
|
||||||
|
|
||||||
|
## SYNOPSIS
|
||||||
|
|
||||||
|
`irtt-tui` [*OPTIONS*] *[LABEL=]TARGET*...
|
||||||
|
|
||||||
|
## DESCRIPTION
|
||||||
|
|
||||||
|
`irtt-tui` is a live dashboard over the same probing engine as
|
||||||
|
`irtt-client(1)`: a graph and status view instead of a printed event stream.
|
||||||
|
At least one target is required.
|
||||||
|
|
||||||
|
Target syntax, multiple targets, and pacing are identical to
|
||||||
|
`irtt-client(1)` — see that manual for `[LABEL=]TARGET` syntax and the
|
||||||
|
`--pacing staggered|burst` option. The full set of negotiation flags
|
||||||
|
(`--interval`, `--length`, `--hmac`, `--clock`, `--tstamp`, `--stats`,
|
||||||
|
`--sfill`, `--dscp`, `--ttl`, `--loose`) is shared with `irtt-client` as
|
||||||
|
well; consult that manual for their meaning.
|
||||||
|
|
||||||
|
## CONTINUOUS DEFAULT
|
||||||
|
|
||||||
|
Unlike `irtt-client`, **the TUI defaults to continuous mode**
|
||||||
|
(`--duration 0`): it runs until you quit. Pass an explicit `--duration` for
|
||||||
|
a finite run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
irtt-tui <server> --duration 30s
|
||||||
|
```
|
||||||
|
|
||||||
|
## CONTROLS
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
| --- | --- |
|
||||||
|
| `q`, `Ctrl-C` | Quit |
|
||||||
|
| `p` | Toggle display pause (probing continues; only redraws stop) |
|
||||||
|
| `g` | Toggle Graph / Dashboard view |
|
||||||
|
| `m` | Cycle graph metric |
|
||||||
|
| `r` | Clear visible graph history |
|
||||||
|
| `←` / `→` | Pan graph |
|
||||||
|
| `PageUp` / `PageDown` | Page-pan graph |
|
||||||
|
| `Home` / `End` | Jump to oldest / jump to live |
|
||||||
|
| `+` / `=` | Zoom graph in |
|
||||||
|
| `-` | Zoom graph out |
|
||||||
|
| `0` | Reset graph window and zoom |
|
||||||
|
|
||||||
|
## RETAINED HISTORY
|
||||||
|
|
||||||
|
The TUI keeps its own bounded presentation state on top of the statistics
|
||||||
|
retention described in `irtt-client(1)`:
|
||||||
|
|
||||||
|
- Up to 100,000 graph samples per target.
|
||||||
|
- Up to 80 recent status/log messages.
|
||||||
|
|
||||||
|
`r` clears the currently visible graph history and returns the viewport to
|
||||||
|
live; it does not change the 100,000-sample cap itself. These are
|
||||||
|
presentation bounds on top of the client's own statistics retention (see
|
||||||
|
`irtt-client(1)` MEASUREMENTS AND MEMORY) — a long continuous TUI session's
|
||||||
|
total memory is dominated by whichever of the two is larger for your target
|
||||||
|
count.
|
||||||
|
|
||||||
|
## EXIT BEHAVIOR
|
||||||
|
|
||||||
|
Quitting with `q` or `Ctrl-C` is an interrupted, successful exit. Otherwise,
|
||||||
|
exit status follows the same peer-close and driver-failure rules as
|
||||||
|
`irtt-client(1)`.
|
||||||
|
|
||||||
|
## EXAMPLES
|
||||||
|
|
||||||
|
```sh
|
||||||
|
irtt-tui host.example
|
||||||
|
irtt-tui eu=host.example
|
||||||
|
irtt-tui eu=host-a.example us=host-b.example --pacing burst
|
||||||
|
irtt-tui host.example --duration 30s
|
||||||
|
```
|
||||||
|
|
||||||
|
## SEE ALSO
|
||||||
|
|
||||||
|
`irtt-client(1)` for target syntax, pacing, negotiation flags, and
|
||||||
|
finite/continuous memory behavior; `irtt-server(1)`; `irtt-rs(1)`.
|
||||||
Reference in new issue
Block a user