Releases: lance0/xfr
v0.9.9
What's New
Added
- Max jitter and packet size in UDP summary (#48 follow-up) — final UDP summary now reports
Jitter Max(peak of the RFC 3550 running estimate) alongside the average, plusPacket Size(UDP payload bytes). Surfaced in plain text and JSON. Requested by @brettowe for NFS UDP packet-size tuning context. -wshort alias for--window(#60) — matches iperf3 muscle memory.
Changed
- Bare-integer duration arguments mean seconds (#61) —
-t 10,--max-duration 60,--rate-limit-window 30, anddiscover --timeout 5now accept plain integers as seconds. Unit-suffixed forms (10s,1min,500ms) continue to work unchanged.- Side effect:
--rate-limit-windownow rejects zero (0,0s,0ms) —0swas previously accepted and would later panic in the rate-limiter cleanup task becausetokio::time::intervalrequires a non-zero duration. Other duration flags still accept0for their existing meanings (-t 0is infinite).
- Side effect:
- Smoothed TUI jitter reading (#48) — the UDP stats panel shows jitter averaged over a 10-second rolling window rather than raw per-second samples. Server data pipeline is unchanged; display is smoothed. While running, the label reads
Jitter (10s):; on completion it reverts toJitter:with the authoritative final value from the server.
Fixed
-
Duplicate receive-error log on the server (#54) —
tcp::receive_data/receive_data_halfeach warned at the read-error site, and the caller warned again on the returnedErr. The innerwarn!is removed so receive errors log exactly once. Reported by @matttbe. -
Default to kernel TCP autotuning (#60) — xfr no longer forces
SO_SNDBUF/SO_RCVBUFto 4 MB on either side by default. Both ends let the kernel autotune unless the user passes-w/--window. When set, the client's value propagates to the server over the control protocol so both sides apply the socket option symmetrically (matching iperf3). Reported by @matttbe.Caveats:
- Loopback / intra-host benchmark numbers may decrease by roughly 10% — this is expected; the previous numbers were inflated by the oversized app-applied buffer.
- On high-RTT paths, very short tests (e.g.
-t 1sat high bitrate) may now show ramp-up-limited throughput in the final summary because kernel autotune takes a handful of RTTs to grow the window. Use a longer-tor pass an explicit-wto skip autotune. Note that-O/--omitonly hides early intervals from output — the server's final summary is computed over the full test duration. - Explicit window sizes above
c_int::MAX(≈2.1 GB on 64-bit) are now rejected withInvalidInputinstead of silently wrapping beforesetsockopt.
Removed
- Library API: pre-1.0 break —
TcpConfig::high_speed()andTcpConfig::with_auto_detect()are gone; constructTcpConfigdirectly with the fields you want set. TheHIGH_SPEED_BUFFERandHIGH_SPEED_WINDOW_THRESHOLDconstants (which were private) are also removed. Downstream code that constructsControlMessage::TestStart,protocol::UdpStats, ortui::app::Appby name now needs to supply the new fields (window_size,jitter_max_ms,packet_size,jitter_history); all are additive with sensible defaults.
Install
- Homebrew:
brew upgrade xfr(vialance0/tap) - crates.io:
cargo install xfr - Pre-built binaries: see assets below
Full Changelog: v0.9.8...v0.9.9
v0.9.8
Highlights
Fast, accurate TCP teardown (issue #54) — the shutdown() drain on the send path was waiting for bufferbloated send queues to ACK through rate-limited paths, sometimes far past the test duration. Replaced with SO_LINGER=0 (abortive close) on Linux, with tcpi_bytes_acked clamping so abrupt close doesn't overcount discarded tail bytes. macOS keeps graceful shutdown() since it lacks the bytes_acked counter.
Separate send/recv reporting in bidir tests (issue #56) — --bidir now reports per-direction bytes and throughput so asymmetric links no longer collapse into a single misleading number. Visible in plain text, JSON (4 new optional fields), CSV (4 new columns), and the TUI throughput panel (↑ X / ↓ Y).
What's New
Added
- Per-direction reporting in
--bidirresults (#56)
Fixed
- TCP teardown no longer blocks on
shutdown()drain (#54) - Sender-side byte counts clamped to
tcpi_bytes_ackedto remove ~5-10% overcount on bufferbloated/rate-limited links
Install
# Homebrew
brew install lance0/tap/xfr
# Cargo
cargo install xfr
# Download binary
gh release download v0.9.8 --repo lance0/xfrFull Changelog: v0.9.7...v0.9.8
v0.9.7
Highlights
Early exit summary (issue #35) — Ctrl+C now displays a test summary with accumulated stats instead of silently exiting. Works in both plain text and TUI modes. Double Ctrl+C force-exits immediately.
DSCP server-side propagation — --dscp flag is now sent to the server and applied to server-side TCP/UDP sockets for download and bidirectional tests. Previously only client-side sockets were marked.
What's New
Added
- Early exit summary — Ctrl+C triggers graceful cancel, waits for server result, and displays full test summary
- DSCP propagated to server via
TestStartprotocol message for download/bidir tests - Non-Unix
--dscpwarning shown before test starts
Fixed
- Cancel flow waits for server
Resultmessage instead of immediately erroring - Server sends
Resultbefore slow post-processing (push gateway, metrics) to prevent false cancel timeouts - Rust 1.95 clippy compatibility (
manual_checked_ops,collapsible_match_arms)
Changed
- Bump
softprops/action-gh-releasefrom 2 to 3
Install
# Homebrew
brew install lance0/tap/xfr
# Cargo
cargo install xfr
# Download binary
gh release download v0.9.7 --repo lance0/xfrFull Changelog: v0.9.6...v0.9.7
v0.9.6
Added
--dscpflag — DSCP/TOS marking on TCP and UDP client sockets for QoS policy testing. Accepts raw TOS byte (0-255) or DSCP names (EF, AF11-AF43, CS0-CS7, VA). UsesIP_TOSfor IPv4,IPV6_TCLASSfor IPv6. Warns for QUIC and non-Unix platforms.omit_secsconfig support (#43) —[client] omit_secs = Nin config.toml sets default--omit. CLI--omit(including--omit 0) takes precedence.- Per-interval jitter in TUI and plain text (#48) — TUI stream bars show
jitter: X.XXmsfor UDP. Plain text interval lines showjitter: X.XXms lost: Nfor UDP,rtx: N rtt: Xmsfor TCP. Multi-stream jitter is averaged across streams.
Full Changelog: v0.9.5...v0.9.6
v0.9.5
Added
- TCP
--cportsupport (#44) —--cportnow pins client-side TCP data-stream source ports for ECMP load balancing and strict egress firewall scenarios. Multi-stream TCP (-P N) uses sequential ports (cport,cport+1, ...,cport+N-1), matching existing UDP behavior. - Nix flake (#46) —
nix run github:lance0/xfrornix developfor a dev shell. Thanks to @deephack1982 for the contribution!
Changed
- TCP
--cportsemantics — TCP control connection remains on an ephemeral source port while data streams use the requested port or range. TCP data binds now match the remote address family (dual-stack fix). Overlap between control and data ports is detected and rejected.
Full Changelog: v0.9.4...v0.9.5
v0.9.4
Added
--no-mdnsflag (#41) —xfr serve --no-mdnsdisables mDNS service registration for environments where multicast is unwanted or another service already uses mDNS.server.no_mdnsconfig support — also configurable via[server] no_mdns = truein~/.config/xfr/config.toml.
Changed
- Delta retransmits in interval reports (#36) — plain text interval lines now show per-interval retransmit deltas instead of cumulative totals, making it easier to spot when retransmits actually occur. Hidden intervals from
--omit,--quiet, or larger--intervalsettings no longer get folded into the next visiblertx:value. Final summary still shows cumulative totals.
Full Changelog: v0.9.3...v0.9.4
v0.9.3
Added
- Server
--bindflag (#38) —xfr serve --bind <IP>binds TCP, QUIC, and UDP data listeners to a specific address. Validates against-4/-6flags and rejects unspecified addresses (::,0.0.0.0).
Changed
- Server sends random payloads (#34) — server-side TCP and UDP send paths now use random bytes by default in reverse and bidirectional modes, matching the client's default-on behavior.
Fixed
- QUIC dual-stack on Windows (#39) — QUIC server endpoint now creates its UDP socket via socket2 with explicit
IPV6_V6ONLYhandling instead of relying on Quinn'sEndpoint::server(). On Windows/macOS whereIPV6_V6ONLYdefaults totrue, binding to[::]would only accept IPv6 connections. - Server random payload on single-port TCP reverse (#34) — the single-port TCP handler (DataHello path used by all modern clients) was missing
random_payload = true, causing reverse-mode downloads to still send zeros.
Security
- quinn-proto DoS fix — updated quinn-proto 0.11.13 → 0.11.14 (RUSTSEC-2026-0037, severity 8.7)
Full Changelog: v0.9.2...v0.9.3
v0.9.2
Full Changelog: v0.9.1...v0.9.2
v0.9.1
What's New (includes v0.9.0 + v0.9.1)
v0.9.0 was tagged but never released due to CI issues. This release includes all v0.9.0 features plus v0.9.1 fixes.
Added
- MPTCP support (
--mptcp) — Multi-Path TCP on Linux 5.6+ (issue #24). UsesIPPROTO_MPTCPat socket creation via socket2. All TCP features work transparently. Server automatically creates MPTCP listeners when available (no flag needed) — accepts both MPTCP and regular TCP clients with silent fallback. - Kernel TCP pacing via
SO_MAX_PACING_RATE(issue #30) — On Linux,-bnow uses the kernel's FQ scheduler with EDT for precise per-packet timing, eliminating burst behavior from userspace sleep/wake cycles. Falls back to userspace pacing on non-Linux, MPTCP sockets, or if the setsockopt fails. Note:-bsets a global bitrate shared across all parallel streams (unlike iperf3 where-bis per-stream).
Changed
- Library API —
create_tcp_listener(),connect_tcp(), andconnect_tcp_with_bind()now take amptcp: boolparameter. Passfalseto preserve existing behavior.
Fixed
- High stream-count teardown hardening (issue #32) — client now stops local data streams at local duration expiry instead of waiting for server
Result, scales stream join timeout with stream count (max(2s, streams*50ms)), and TCP receivers drain briefly after cancel to reduce reset-on-close bursts at high-P. - Best-effort send shutdown —
send_data()shutdown no longer propagates errors during normal teardown races. - Kernel pacing rate width —
SO_MAX_PACING_RATEnow uses nativec_ulonginstead ofu32, removing an unintended ~34 Gbps ceiling on 64-bit Linux. - JoinHandle panic with many parallel streams (issue #24) — removed second
join_allafter aborting timed-out stream tasks. - Final summary showing 0 retransmits/RTT/cwnd (issue #26) — each stream task captures a final sender-side TCP_INFO snapshot before socket close.
- Broken pipe / connection reset at teardown (issue #25) — client joins stream task handles with timeout before returning.
- MPTCP label in server log — server displays "MPTCP" instead of "TCP" when client uses
--mptcp.
Install
# Homebrew
brew install lance0/tap/xfr
# Cargo
cargo install xfr
# Binary (Linux x86_64)
curl -sSL https://github.com/lance0/xfr/releases/download/v0.9.1/xfr-x86_64-unknown-linux-musl.tar.gz | tar xzAcknowledgments
Thanks to @matttbe (Linux kernel MPTCP co-maintainer) for extensive testing, bug reports (#24, #25, #30, #32), suggesting kernel TCP pacing via SO_MAX_PACING_RATE, and catching the FIN vs RST teardown behavior.
Full Changelog: v0.8.0...v0.9.1
v0.8.0
Full Changelog: v0.7.1...v0.8.0