Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

JSON results

udpstp client --format json prints the Test’s results as JSON compatible with OB-UDPST v9.1.0’s client output: the same keys, in the same order, with numbers and times printed the way OB-UDPST prints them, so tools written for OB-UDPST’s formatted JSON (-f jsonf) keep working. Progress goes to stderr, so stdout holds only the JSON.

From 0.1.0 the key names, types and enumerated values are a stable interface, the extension’s included; the free text in ErrorMessage, ErrorMessage2 and a warning’s Message is not.

What is the same

  • Top-level keys: IPLayerMaxConnections, IPLayerMaxIncrementalResult, IPLayerCapSupported, then Input and Output once the Test was activated, then ErrorStatus, ErrorMessage and ErrorMessage2.
  • Output: BOMTime to TimestampResolutionUsed, then IncrementalResult, Summary, ModalSummary, AtMax, ModalResult, ECNBleachingDetected, then EOMTime and Status.
  • Printed forms: delays in seconds from whole milliseconds (0.011), Mbps and percentages with 2 decimals, ratios with 9, zero as 0.0, times as UTC with microseconds (2026-09-25T14:13:20.712759Z), and OB-UDPST’s tab-indented layout.
  • A Test that never activated has only the top-level IPLayer* keys and the error fields; one that didn’t complete (ErrorStatus 200 and up) has only Output’s header and trailer.

Tests compare the key paths, their order and each value’s printed form with OB-UDPST’s vendored examples 01, 06, 11 and 12 (testdata/obudpst-9.1.0/json_examples).

Fields udpstp never measures

They carry OB-UDPST’s own value for the feature being off:

ValueFields
0 or 0.0CECountOfDelivered, CEPercentOfDelivered (every object), ECNBleachingDetected, Input.ECN, Input.CEThreshold
"", 0.0 or 0Input.Interface, InterfaceEthMbps (every object), Input.InterfaceDeterminesMax
fixedNumberFirstModeSuppSubIntervals 0, PreambleDuration 0, IPRREnable 1, RIPREnable 1, TimestampResolution and TimestampResolutionUsed 1, Tmax, TmaxUsed, TimeoutNoTestTraffic and TimeoutNoStatusMessage 1000 (OB-UDPST’s 1 s traffic-stopped timer, not RFC 9097’s Tmax), TmaxRTT and TmaxRTTUsed 3000, ProtocolVersion "Any"; JumboFramesPermitted, UDPPayloadMax and UDPPayloadDefault follow --jumbo and --traditional-mtu
Multi-ConnectionNumberOfConnections and MinNumOfConnections from --test-connections and --min-test-connections, NumberOfHosts and HostList from the SERVERs; a Downstream Test’s Sub-Intervals are its Test Connections’ summed on one grid, where OB-UDPST sums them by index, so the figures can differ from OB-UDPST’s; ActiveConnections counts the Test Connections that received in the Sub-Interval (1 with one Test Connection, as OB-UDPST), in a Summary in its Capacity Mode’s last; MinOnewayDelay is 0 when the Test Connections go to several Servers, whose clocks the Client can’t relate. An Upstream Test’s Sub-Intervals are each Server’s own, summed by number, so they are approximate: a warning in the udpstp extension, not ErrorMessage, gives the largest offset between them

ClientIPAddress is "" today: the Client’s socket is bound to a wildcard address, which says nothing about the one used. When it holds an address, it follows --address-privacy (below).

Where udpstp differs

Each is an OB-UDPST Difference with its own Conformance Record.

  • AtMax and ModalResult are the Maximum IP-Layer Capacity, which meets the Performance Criterion. OB-UDPST’s maximum, the Highest IP-Layer Capacity, is in the extension. A Capacity Mode with no maximum has no AtMax (or ModalResult), never zeros, and the Test gets ErrorStatus 49.
  • ErrorStatus is the most severe status, where OB-UDPST keeps the last one written: 200 for a Test that didn’t complete, with its cause in ErrorMessage2; for a completed Test the most severe warning: 49 no valid maximum, 4 nothing received for 1 s, 5 the peer stopped receiving, 3 the peer missed Status PDUs, 2 Status PDUs missing here, 6 invalid PDUs. The exit status of a completed Test is 0 all the same.
  • An interrupted Test (Ctrl-C, a signal to stop, or a failed write to standard output while the Test ran) has ErrorStatus 204 and the results so far, its Test Connections having sent their STOP; OB-UDPST stops without one and exits 255.
  • A Test that didn’t complete keeps its measured Sub-Intervals in udpstp.SubIntervals, with the same fields and units as Output.IncrementalResult, and its minimum RTT in udpstp.MinRTT. Output still has only its header and trailer. These partial results require the extension, which is on by default.
  • SoftwareVersion is "udpstp <version>"; ControlProtocolVersion is 20.
  • Delay fields of a Sub-Interval without samples are 0, as OB-UDPST writes, and the extension lists those Sub-Intervals; Summary is computed from real samples only.
  • A Sub-Interval never measured is left out of IncrementalResult and listed in the extension; Interval is its subIntSeqNo.
  • IPLayerCapacity is over the nominal Sub-Interval (RFC 9097 §5.3); OB-UDPST divides by the measured one.
  • A name that doesn’t resolve, or a Client that can’t start, prints no JSON; OB-UDPST prints JSON with 255.

The extension

One last object, "udpstp", holds results OB-UDPST’s schema lacks; --json-extension=false leaves it out for tools that reject unknown keys:

KeyWhat
WarningsThe results’ warnings, each {Code, Count, Message}: the text results’ line as Message, which may change; Code, the Server log’s event name for it (such as BurstsSkipped, PathMtuExceeded, TrafficStopped, SocketBuffersShort, or DataPhaseEnded for a Test that ended without the STOP exchange: in a Multi-Connection Test, one for each Test Connection that did, its Message naming it), or SendingRateCapped, FirstModeSplitIgnored, UpstreamResultsApproximate, Interrupted (the Client user stopped the Test), ReceiveBufferOverflows (datagrams this host’s OS discarded at the socket before udpstp read them; Linux only), SendingRateNotKept (a Downstream Fixed-Rate Test whose Server sent more than 5% below its row’s sending rate in the Sub-Interval of a Maximum IP-Layer Capacity, as the Client estimated what it sent: it may not have kept up, so the result may understate the path; its Count is the Capacity Modes it concerns; a Search Test’s Server tells the Client no sending rate), CongestionNeverConfirmed (a Search Test none of whose Test Connections’ searches found the path’s limit, as the Client classified their Trial Intervals, by the maximum delay variation: none confirmed Congestion, slowAdjThresh Errored Intervals back to back, nor met the limit at its end, at least 4 of its last 64 Trial Intervals errored and at least 1 in 4 from the first of those on, as a search cycling around the limit above the 1 Gbps row does, so the result may understate the path: one for each Test Connection, in a Multi-Connection Test its Message naming it, saying why its search fell short, the first that fits (its limits): its IP-Layer Sender Bit Rate was at the maxBandwidth it stated, or reached the top of the Sending Rate Table; delay variation held it in Hold Intervals; the Test ended while it still rose; or else the Load Sender, the Server in a Downstream Test and this host in an Upstream one, stopped rising though few of the last 64 Trial Intervals were Errored Intervals, so it may not have kept up. Only for a completed Search Test whose every activated Test Connection completed), SubIntervalsAfterMaximumMissed (a Search Test in which no Sub-Interval after the Maximum IP-Layer Capacity’s met the Performance Criterion, and one that missed it ran more than --faster-sub-interval-margin faster than the Maximum, after the Maximum’s, as a search cycling around a drop-tail bottleneck gives, or before it, as a search that climbed back slowly after an overshoot, a path that slowed, a policer or shaper that let a burst through at first or, Downstream, a Server that fell behind gives; or the last one ran under 60% of the Maximum, as a queue that kept dropping after it overflowed, a path that slowed or, Downstream, a Server that fell behind gives, so the Maximum may be well below the path, or no longer what it carries: one for each Capacity Mode, its Message giving each such Sub-Interval, whether before or after the Maximum’s, with its IP-Layer Capacity and Loss Ratio; an Upstream Test in which this host left more than 1% of its Load PDUs unsent isn’t judged on the last one or on one before the Maximum’s) or NoValidMaximum; and Count, the datagrams or PDUs it counts, or 1. NoValidMaximum is listed only here
EndReasonHow the Test ended. With one Test Connection, how it ended: Completed, Unconfirmed, TestIntervalElapsed, StoppedByClient, Stopped, Watchdog or OverRate, or DataPhaseEngineFailed, ActivationRejected, ControlPhaseFailed (a time limit too), Abandoned (given up by the Multi-Connection Coordinator), SocketError. A Multi-Connection Test’s is the Test’s own: Completed, or why the Multi-Connection Coordinator aborted it, TooFewTestConnections, ParametersDiffer, TestConnectionEnded (one refused a Coercion or can’t run a parameter), TestConnectionsTimedOut or Interrupted, or NoTestConnectionCompleted when every Test Connection ended otherwise; each Test Connection’s own end is in TestConnections
ControlPhaseFailureWhy the Control Phase failed, when EndReason is ControlPhaseFailed, else null (so always null for a Multi-Connection Test, whose Test Connections each have their own in TestConnections): TestInitiationTimedOut, NoAuthenticationConfigured, NoKeyForSending, AmbiguousKeyForSending, UnacceptableCoercion, UnrunnableParameter, UnknownSetupResponse, UnknownActivationResponse, MulticastOrBroadcastServer, SocketUnavailable, or SetupRejected and the Setup Response’s code: SetupRejectedBadVersion, SetupRejectedJumboMismatch, SetupRejectedTraditionalMtuMismatch, SetupRejectedAuthNotConfigured, SetupRejectedAuthRequired, SetupRejectedAuthModeInvalid, SetupRejectedAuthFailed, SetupRejectedAuthTimeInvalid, SetupRejectedMaxBandwidthRequired, SetupRejectedCapacityExceeded, SetupRejectedMultiConnectionInvalid, SetupRejectedConnectionAllocationFailure
CoercionsEach parameter the Server coerced, {McIndex, Parameter, Requested, Coerced}: the Test Connection, the parameter’s RFC 9946 name as --refuse-coercion takes it, and the values as the Activation Request and Response carry them (dscpEcn whole, the DSCP shifted left by 2)
DontFragment, SocketBuffers, DataPhaseEngineHow this host ran the Test. DataPhaseEngine is portable. In a Multi-Connection Test, SocketBuffers is the smallest the OS granted any of its Test Connections, receive and send each
FastPathsThe fast paths this host sent Load PDUs, read datagrams and waited with, in this order: sendmmsg (Load PDUs sent in batches), UdpGso (a Burst’s Load PDUs as one message the kernel splits), recvmmsg (datagrams read in batches, Load PDUs header-only), KernelRxTimestamps (datagrams timed by the kernel’s receive timestamps rather than the read) and ppoll (the Data Phase Engine waited on its socket between turns, so an arrival woke it, rather than sleeping). Linux only; empty elsewhere. sendmmsg and UdpGso are missing in a Downstream Test (the Client sends no Load PDUs) and where the kernel refused sendmmsg; UdpGso also where the kernel or device refused it, or no Burst held more than one Load PDU. With UDPSTP_KERNEL_TX_TIMESTAMPS=1 on Linux 6.13 and later sendmmsg is missing too, since each message then goes with a sendmsg call of its own to carry the number of its send timestamp, so UdpGso can come without it; if the kernel refuses the numbers after all, the batches after that go in sendmmsg calls, and it is named. With UDPSTP_KERNEL_TX_TIMESTAMPS=kernel-count the messages carry no numbers, and sendmmsg is named as without the variable. recvmmsg, KernelRxTimestamps and ppoll are missing where the kernel refused them. In a Multi-Connection Test, the fast paths every Test Connection that completed used, so a fallback on any one shows; if none completed, every one whose Data Phase Engine ran until its Data Phase ended. A Test Connection whose engine failed or never ran, or that ended early, is left out: it used none, or not all it would have
KernelSendTimestampsOnly with UDPSTP_KERNEL_TX_TIMESTAMPS set to 1 or kernel-count on Linux, in a Test where the Client is the Load Sender: how much later than their lpduTime the kernel sent the Load PDUs, from its software send timestamps, one per message (a UDP GSO message counts once): Samples, and LagP50Us, LagP99Us and LagMaxUs in µs. On Linux 6.13 and later each message carries its own number, so a send the kernel refuses costs only its own sample; each message then goes with a sendmsg call of its own, and FastPaths doesn’t name sendmmsg. A kernel that refuses the numbers after all (EINVAL) gets its messages without them from then on, its batches in sendmmsg calls, which FastPaths then names, and counting stops there, the Test going on. Before 6.13 the kernel numbers the messages, and may have numbered one it refused (a full queue, a refused UDP GSO send), so counting stops at the first send refused, whole or in part, and a Test with refused sends reports fewer samples. With kernel-count the kernel numbers them on every kernel, as before 6.13, so a result can be checked on that send path: batches in sendmmsg calls, counting stopping at the first send refused. In a Multi-Connection Test, its Test Connections’ samples counted together, with the largest of their lags. Experimental: not a stable interface (interfaces.md)
BurstLatenessIn a Test where the Client is the Load Sender: how late after their scheduled time it wrote its Send Plans, and so stamped their lpduTime, its Burst lateness: Samples, one a Send Plan, and P50Us, P99Us and MaxUs in µs, counted in 1 µs buckets up to 10 ms. In a Multi-Connection Test, its Test Connections’ samples counted together, with the largest of their percentiles. The Server’s log reports its own as burst_lateness_p99_us in DataPhaseEnded. Experimental: not a stable interface (interfaces.md)
PerformanceCriterionMaxLossRatio
CapacityModesEach Capacity Mode’s Sub-Intervals, whether it has a valid result, its Maximum and Highest IP-Layer Capacity
SubIntervals, MinRTTOnly for a Test that activated but didn’t complete, including an interrupted Test. SubIntervals holds every measured Sub-Interval with the same fields as Output.IncrementalResult, preserving its Interval number, loss, delay variation, RTT variation and IP-Layer Capacity. It is empty if none was measured. MinRTT is the smallest measured minimum RTT, in seconds, or null if unavailable. Neither field is a completed-Test summary
MissingSubIntervals, NoSamplesSub-Intervals never measured, and those without delay or RTT samples
IPLayerSenderBitRatePer Sub-Interval, where the Client was the Load Sender; in a Downstream Test IPLayerSenderBitRateEstimate instead, the Client’s estimate of what the Server sent in each st of the Server’s clock, from the lpduSeqNo span of the Load PDUs that arrived, a lost one as big as the st’s others
TestConnectionsA Multi-Connection Test’s Test Connections, each on its own, as the text results show them: {McIndex, Server, EndReason, ControlPhaseFailure, CapacityModes}. A Test with one Test Connection has none

Not yet

  • Downstream output follows OB-UDPST’s code, where only Role changes; every OB-UDPST example is an Upstream Test, so a Downstream Test’s full structure hasn’t been compared with OB-UDPST’s.
  • OB-UDPST’s brief (jsonb) and summary-only (-s) forms.

ClientIPAddress follows --address-privacy: the address in full (the default), anon- and a keyed hash of it (pseudonymize, with --address-privacy-key-file), or its IPv4 /24 or IPv6 /48 (truncate).