irtt_server/lib.rs
1//! IRTT-compatible server behavior.
2//!
3//! This crate shares wire semantics with the client through `irtt-proto` and
4//! does not depend on `irtt-client`.
5//!
6//! # Structure
7//!
8//! [`ServerCore`] is the deterministic protocol and session engine: packet
9//! admission, authentication policy, open negotiation, the session table, echo
10//! receive state, rate and lifetime policy, resource decisions and reply
11//! construction. It performs no I/O; the clock its timestamps and deadlines
12//! come from is a private injected seam, not a runtime abstraction, so the
13//! engine stays testable. Each reply it produces is an [`OutboundDatagram`]:
14//! the packet and the raw traffic class it must be sent with. [`Server`] owns
15//! one Tokio UDP listener and one core, and provides sequential receive, reply,
16//! scheduled expiry maintenance and caller-controlled shutdown. [`ServerSet`]
17//! owns one or more `Server`s as a single service: it binds them all or none,
18//! runs each in its own task, fans one shutdown out to them and fails the group
19//! if any listener does. The crate is intentionally Tokio-native; there is no
20//! blocking or alternate-runtime counterpart and no transport abstraction.
21//!
22//! # One listener or several
23//!
24//! `Server` is the primitive and stays directly usable. `ServerSet` is the
25//! service around it, and a set of one is an ordinary set rather than a special
26//! case — the server CLI runs every invocation, single-bind included, through
27//! one.
28//!
29//! Listeners in a set share nothing but the configuration they were cloned
30//! from. Each has its own socket, core, session table and tokens, so a token
31//! issued by one is unknown at the others, and every bound in
32//! [`ServerConfig`] — `max_sessions` included — applies **per listener**, not
33//! per process.
34//!
35//! See `examples/` in the repository for a runnable single-listener and
36//! multi-listener example.
37//!
38//! # Rejection is silence
39//!
40//! The protocol defines no error reply, no reset and no NACK. Malformed,
41//! unauthenticated and policy-refused datagrams are discarded without a
42//! response and without disturbing any live session, and a client tells
43//! "rejected" from "lost" only by timing out. [`ServerError`] is therefore
44//! reserved for internal failures on the server's own side.
45//!
46//! # Resource policy
47//!
48//! Total session state is bounded (see [`DEFAULT_MAX_SESSIONS`]), and so is the
49//! echo datagram a single session can negotiate (see
50//! [`DEFAULT_MAX_PACKET_LENGTH`]), the rate one session is answered at (see
51//! [`DEFAULT_MIN_SEND_INTERVAL`] and [`DEFAULT_BURST_ALLOWANCE`]) and how long
52//! one lives without traffic (see [`DEFAULT_IDLE_TIMEOUT`]). A single
53//! unauthenticated datagram creates a session and opens are never deduplicated,
54//! so neither an unbounded table nor a remotely chosen buffer size is a
55//! compatibility feature. Upstream's observed lack of session bounds, its
56//! unlimited default packet length, and its never expiring a session that has
57//! not carried an echo request are explicitly not compatibility targets: an
58//! `irtt-rs` session ages from the moment it is opened.
59//!
60//! # Reply source address
61//!
62//! A reply leaves from the address its request was sent to. An
63//! explicit-address listener gets that from the bind and uses ordinary
64//! receive/send. A wildcard listener (`0.0.0.0` or `[::]`) instead reads each
65//! request's local destination from packet metadata and sends that request's
66//! reply from it, so a client on the host's second address is answered from the
67//! endpoint it contacted rather than from whichever one the routing table would
68//! have chosen.
69//!
70//! That path is implemented for Linux, macOS and FreeBSD. On other targets a
71//! wildcard bind is refused by [`Server::from_socket`] — and so by
72//! [`Server::bind`] — rather than served incorrectly; explicit-address
73//! listeners work everywhere they did before.
74//!
75//! # Current scope
76//!
77//! Open handling and negotiation, session creation, normal echo processing,
78//! per-session rate limiting, idle expiry, the maximum-duration
79//! server-initiated close, the negotiated per-session reply traffic class,
80//! server fill, the optional timestamp-allowance and DSCP capability
81//! restrictions, wildcard reply-source selection, Tokio UDP orchestration and
82//! multi-listener supervision through [`ServerSet`].
83//!
84//! # Capability restrictions
85//!
86//! Two optional negotiation policies restrict what a session may ask this server
87//! to provide, and both are off by default, so a configuration that sets neither
88//! negotiates exactly what it always did.
89//!
90//! [`TimestampAllowance`] reduces the requested timestamp placement: `Single`
91//! provides at most one timestamp instant, answering a request for both instants
92//! with the midpoint, and `None` provides no timestamps at all. The requested
93//! clock is never rewritten, so a single placement still reports one field per
94//! negotiated clock domain. [`ServerConfig::with_dscp_allowed`] refuses to provide
95//! traffic-class marking, negotiating any requested DSCP value to zero, after
96//! which the session's replies leave unmarked like any other zero-DSCP session.
97//!
98//! # Server fill
99//!
100//! An echo reply's payload region is filled from the session's negotiated
101//! ServerFill descriptor: `none`, `rand`, or `pattern:` followed by a repeating
102//! hexadecimal pattern. Every valid descriptor is accepted — there is no
103//! allow-list to configure — and one this server cannot parse is answered with
104//! its default, `pattern:69727474`, which is also what a client expressing no
105//! preference is served without any change to the negotiated parameters.
106//!
107//! A `none` payload is zeroes. Payload bytes carry no protocol meaning, and a
108//! server must never emit residue from another request or client. Request
109//! payload bytes never reach a reply under any mode.
110#![forbid(unsafe_code)]
111
112mod clock;
113mod config;
114mod core;
115mod error;
116mod fill;
117mod negotiate;
118mod runtime;
119mod session;
120mod set;
121mod socket_io;
122mod socket_options;
123mod token;
124
125#[cfg(test)]
126mod tests;
127
128pub use config::{
129 ServerConfig, TimestampAllowance, DEFAULT_BURST_ALLOWANCE, DEFAULT_IDLE_TIMEOUT,
130 DEFAULT_MAX_PACKET_LENGTH, DEFAULT_MAX_SESSIONS, DEFAULT_MIN_SEND_INTERVAL,
131};
132pub use core::{OutboundDatagram, ServerCore};
133pub use error::ServerError;
134pub use runtime::{Server, ServerRuntimeError};
135pub use set::{address_family_available, ServerSet, ServerSetError};