Deviations
A Deviation is a recorded departure from a Requirement, with its reason. udpstp has 17. Each says whether it applies by default, which setting controls it, and what would make it worth revisiting.
Non-conformances (9)
approximate-upstream-aggregation
In an Upstream Multi-Connection Test each Test Connection’s Load Receiver, at the Server, keeps its own Sub-Interval grid, which the Client can’t align. The Client sums the Test Connections’ Sub-Intervals by subIntSeqNo, so an aggregate Sub-Interval’s bits come from up to as many slightly different dt-long intervals as there are Test Connections, not from one contiguous interval. The results are marked approximate, with the largest offset between the Test Connections’ Sub-Intervals shown.
RFC: RFC 9097 §5.3
Requirements: RFC9097-5.3-L1
By default: on
Revisit: When a Server can be told a common Sub-Interval grid (such as a start time in the Activation Request), or reports per-Trial-Interval counts the Client could re-bin as it does Downstream.
References: docs/spec.md, docs/spec.md story 62
Proving tests:
client-keeps-to-its-max-bandwidth
A Client that stated a maxBandwidth keeps to it as Load Sender: a Sending Rate Structure above it, IP-layer with IPv4 headers as the rows are rated, is sent with its intervals lengthened by the ratio, rounded up, so its packet sizes and Bursts stay as the Server chose them; the results say so. RFC 9946 §8.1 says the Client SHALL use the parameters the Server communicates, but maxBandwidth is the most the Client wants to send (§6.1), and a Server needn’t honour it (§11 item 5 makes that optional). Lengthening intervals rather than shrinking Bursts or sizes keeps the packet sizes the Test measures with and works for a Burst of one.
RFC: RFC 9946 §6.1, §8.1, §11
Requirements: RFC9946-8.1-6
OB-UDPST: An OB-UDPST Client sends whatever the Server assigns; an OB-UDPST Server caps its rows at maxBandwidth only when built with RATE_LIMITING.
By default: on
Setting: --max-bandwidth
Revisit: If every Server honoured the stated maxBandwidth, or RFC 9946 said what a Client should do with a Sending Rate Structure above it.
Proving tests:
crates/udpstp-core/tests/upstream.rs::a_client_keeps_to_its_max_bandwidth_against_a_server_that_assigns_morecrates/udpstp-core/tests/upstream.rs::a_structure_within_the_max_bandwidth_is_sent_as_givencrates/udpstp-core/tests/upstream.rs::our_server_never_makes_the_client_cap_its_rate
no-preamble-transmission
The Client can’t send a short preamble to wake on-demand access (such as a cellular radio) and leave it out of the results. RFC 9946 defines no preamble, and its search starting from row 0 only ramps up. A slow wake-up shows in the first Sub-Intervals, which a user can put in a Capacity Mode of their own (--first-mode-sub-intervals).
RFC: RFC 9097 §8.3
Requirements: RFC9097-8.3-4
OB-UDPST: OB-UDPST has none either; its JSON reports PreambleDuration 0.
By default: on
Revisit: If a user tests over on-demand access and the first Sub-Intervals mislead, or a later UDPSTP version defines a preamble.
References: docs/conformance/requirements/rfc9097-08.toml
No proving test.
unauthenticated-mode
A Server may serve unauthenticated Tests (authMode 0), and a Client may ask for one, only when each opts in. Such a Test has no authentication and no replay protection: requests in Unauthenticated Mode carry no digest, so they bypass the replay cache, and they spend no per-Source budget.
RFC: RFC 9946 §5.3, §6.1
Requirements: RFC9946-6.1-L11, RFC9946-5.3-1
OB-UDPST: OB-UDPST runs unauthenticated whenever the Server has no keys and the Client sends authMode 0, with no opt-in.
By default: off
Setting: --allow-unauthenticated, allow_unauthenticated in TOML (Server); --unauthenticated (Client)
Revisit: If the interop matrix no longer needs Unauthenticated Mode against OB-UDPST, or a lab use for it disappears.
Proving tests:
crates/udpstp-core/tests/setup_exchange.rs::unauthenticated_setup_ends_in_ack_with_a_test_port_when_both_ends_opt_incrates/udpstp-core/tests/setup_exchange.rs::server_without_opt_in_does_not_accept_an_unauthenticated_setupcrates/udpstp-core/tests/replay_cache.rs::unauthenticated_requests_bypass_the_cache
tests-per-source-above-one
A Server may allow more than one concurrent Test per source, for Clients behind carrier-grade NAT that share an address. RFC 9946 §4.2 limits a path to one.
RFC: RFC 9946 §4.2
Requirements: RFC9946-4.2-1, RFC9946-10-4, RFC9097-8.3-6
By default: off
Setting: --tests-per-source, tests_per_source in TOML
Revisit: If a per-path measure better than the source address appears, or CGNAT deployments don’t need it.
Proving tests:
authentic-setup-requests-left-unanswered
Some Setup Requests with a valid authDigest that are refused get no Test Setup Response: beyond a Source’s budget, all but the first refusal since the Source last spent a token; a copy of an admitted Setup Request (same address, mcIdent and mcIndex), which the network may have duplicated; and a new request while the replay cache is full. Answering each would let a key holder make the Server reflect a signed response per datagram, and a duplicate’s refusal would end the Test it belongs to.
RFC: RFC 9946 §6.2.1
Requirements: RFC9946-6.2.1-3
OB-UDPST: OB-UDPST answers every refused Setup Request; it has no per-Source budget or replay cache, and refuses a duplicate as a Test Connection it already has.
By default: on
Revisit: If a rate limit on signed rejections made answering every refused request safe, or a Client were seen to hang on a refusal it was never sent.
References: docs/spec.md
Proving tests:
crates/udpstp-core/tests/source_budget.rs::only_the_first_refusal_is_answeredcrates/udpstp-core/tests/multi_connection_checks.rs::a_duplicated_setup_request_is_answered_oncecrates/udpstp-core/src/replay.rs::a_full_cache_refuses_new_requests_until_entries_expire
operator-keys-exempt-from-500-ms-congestion-inference
A Test with an Operator Key keeps the slowAdjThresh and trialInt it asked for, even when inferring Congestion then takes longer than the 500 ms RFC 9946 §5.1 item 5 sets as its maximum. An Operator configures the measurement, for example to study slow paths.
RFC: RFC 9946 §5.1
Requirements: RFC9946-5.1-8
OB-UDPST: OB-UDPST applies no 500 ms limit to anyone.
By default: on
Revisit: If no Operator needs Congestion inferred more slowly, or RFC 9946 §5.1’s limit is clarified as applying to consumer Tests only.
References: docs/spec.md
Proving tests:
sending-rate-structure-sanity-limits
A Client zeroes each Sending Rate Structure field beyond OB-UDPST’s sanity limits, as an OB-UDPST Client does: an interval above 1 s, a size above 8972 bytes (the random-size flag masked off), a Burst above 100. A size above the largest the Test’s Sending Rate Table variant allows is zeroed too. §8.1 says the Client uses the parameters it receives and gives no limits; this keeps a Server from making it send absurd sizes or Bursts. Only the size limit rests on the RFC (§6.1’s packet sizes); the interval and Burst limits depart from §8.1.
RFC: RFC 9946 §8.1
Requirements: RFC9946-8.1-6
By default: on
Revisit: If a Server needed intervals above 1 s or Bursts above 100 datagrams, or RFC 9946 gave limits of its own.
Proving tests:
crates/udpstp-core/tests/upstream.rs::srstruct_values_beyond_the_sanity_limits_are_not_sentcrates/udpstp-core/tests/upstream.rs::the_sanity_limits_keep_their_edgescrates/udpstp-core/src/data_phase.rs::a_size_beyond_the_table_variant_is_refused
no-lost-status-backoff-upstream
Lost Status Backoff runs only in Downstream Tests, where the Server is the Load Sender. RFC 9097 §8.1, which RFC 9946 §5.1 imports, says “the offered load SHALL then be decreased” when Status PDUs stop arriving; in an Upstream Test nothing lowers it: the Client SHALL send the rates the Server communicates (RFC 9946 §8.1), and a lower rate from the Server could reach it only in the Status PDUs being lost. The Test relies on the 1 s Status PDU timeout instead, which stops the Client (ADR 0002).
RFC: RFC 9097 §8.1; RFC 9946 §4, §5.1, §8.1
Requirements: RFC9097-8.1-13
By default: on
Revisit: If the Client, as Load Sender, were allowed to lower its own rate when Status PDUs stop arriving, or Upstream Tests were seen to flood a path during feedback loss before the 1 s timeout.
References: ADR 0002
Proving tests:
Interop accommodations (1)
rtt-minimum-may-be-zero
rttMinimum may be 0 although §8.2 says it MUST be positive: an RTT below a millisecond, or one that rttRespDelay’s rounding brings to 0 (rtt-sample-rules), is reported as it is, as OB-UDPST reports it, rather than raised to 1 ms.
RFC: RFC 9946 §8.2
Requirements: RFC9946-8.2-8
OB-UDPST: OB-UDPST reports 0 too.
By default: on
Revisit: If a peer refuses a Status PDU whose rttMinimum is 0, or RFC 9946 errata say which to send.
Proving tests:
crates/udpstp-core/tests/delay_variation.rs::rtt_resp_delay_adjusts_the_rttcrates/udpstp-core/tests/delay_variation.rs::rtt_is_measured_from_echoed_status_pdus
Platform gaps (1)
windows-dont-fragment-unset
On Windows test traffic leaves without DF, since no crate udpstp allows sets IP_DONTFRAGMENT without unsafe. The Client and Server warn at startup. Linux and macOS set it by default.
RFC: RFC 9946 §5
Requirements: RFC9946-5-1
By default: on
Revisit: An allowed crate sets DF on Windows without our own unsafe.
References: docs/technology.md
Proving tests:
Unmet SHOULDs (6)
rate-changes-when-a-status-pdu-arrives
The Load Sender changes its rate when a Status PDU arrives, as the algorithm decides, not at the next boundary of its own st: the Load Receiver’s Trial Intervals, which the Status PDUs close, and the Load Sender’s st are the same length but offset by the one-way delay. So one st can carry two rates, and its IP-Layer Sender Bit Rate is their mix.
RFC: RFC 9097 §7.3
Requirements: RFC9097-7.3-4
OB-UDPST: OB-UDPST changes the rate when a Status PDU arrives too.
By default: on
Revisit: If per-st Sender Bit Rates were reported (sender-bit-rate-per-sub-interval) and a clean rate per st mattered: defer each change to the next st boundary.
Proving tests:
crates/udpstp-core/tests/algorithm_b.rs::in_fast_mode_each_clean_interval_adds_high_speed_delta_rows
compact-human-final-metrics
Human results show labelled final metrics rather than a second table repeating the Maximum IP-Layer Capacity. Phase and F appear in the Results Summary parameters, and each Capacity Mode retains its Maximum IP-Layer Capacity, Loss Ratio and RTT bounds. This makes the main result prominent on wide and narrow terminals. The Sub-Interval table and structured JSON retain their measurement data.
RFC: RFC 9097 §9
Requirements: RFC9097-9-2, RFC9097-9-3, RFC9097-9-4, RFC9097-9-5
By default: on
Revisit: If tabular final metrics become necessary for human comparison across Tests; machine consumers use JSON.
Proving tests:
crates/udpstp/src/test_results.rs::report_layout_keeps_one_capacity_headline_and_one_aggregate_outcomecrates/udpstp/src/results.rs::terminal_layout_preserves_measurements_context_and_validity
sender-bit-rate-per-sub-interval
The IP-Layer Sender Bit Rate is reported per Sub-Interval, for the aggregate of the Test Connections, in the text and the JSON, not as RFC 9097 §9’s table of every st for each Test Connection and the aggregate: that table has 20 rows a second for each Test Connection at the default st of 50 ms. The per-st figures exist in the Data Phase’s events.
RFC: RFC 9097 §9
Requirements: RFC9097-9-11, RFC9097-9-12
By default: on
Revisit: If a user needs the per-st, per-Test-Connection rates, for example to study a sender’s burstiness: add them to the JSON first.
Proving tests:
crates/udpstp/src/results.rs::an_upstream_testcrates/udpstp-core/tests/sub_interval_results.rs::an_upstream_client_reports_what_it_sent_per_st
no-frequency-or-volume-quotas
Quotas are off by default: unless the administrator sets one, the Server doesn’t limit how often, or how much, one Client tests, only how much runs at once (Tests per Source, Test Connections, the Admitted Rate Cap) and how long a Consumer’s Test Interval is (60 s), which is RFC 9946’s own example. Set, a Source’s or a Key Table row’s Quota of Tests and load bytes per Quota Period refills evenly; a new Test past it is refused with CONNFAIL at Setup, or with BADPARAM when it first activates after Tests set up alongside it used the Quota first. Operator Keys are exempt. Use is counted in memory, so a restart forgets it; a per-Source Quota treats everyone behind one carrier-grade NAT address as one Source, and an IPv6 Client can move between /64s.
RFC: RFC 9946 §11
Requirements: RFC9946-11-6
OB-UDPST: OB-UDPST has no such quotas.
By default: on
Setting: --quota-period, --source-tests-per-quota-period, --source-bytes-per-quota-period; tests_per_quota_period and bytes_per_quota_period on a key table row
Revisit: If a deployment serves Consumers whose data volumes are limited, set Quotas there; revisit the default if RFC 9946 §11 item 6 is read as requiring them on.
References: docs/spec.md
Proving tests:
crates/udpstp-core/tests/quotas.rs::a_source_past_its_quota_of_tests_is_refused_with_connfailcrates/udpstp-core/tests/quotas.rs::a_source_past_its_quota_of_bytes_is_refusedcrates/udpstp-core/src/admission.rs::the_quota_refills_over_the_quota_periodcrates/udpstp-core/tests/quotas.rs::tests_set_up_together_past_the_quota_are_refused_at_activationcrates/udpstp-core/tests/quotas.rs::a_key_past_its_quota_is_refusedcrates/udpstp-core/src/admission.rs::operator_keys_are_exemptcrates/udpstp-core/src/admission.rs::a_test_lives_on_in_a_test_connection_waiting_for_its_socket
row-zero-below-half-a-megabit
Row 0 offers about 0.1 Mbps (one 625-byte packet every 50 ms), not the 0.5 Mbps RFC 9097 §8.1 recommends; rows 1 to 1000 follow its 1 Mbps steps. The table follows OB-UDPST’s, whose row 0 is about 0.11 Mbps, so a search starts alike against either (row-zero-fixed-size).
RFC: RFC 9097 §8.1
Requirements: RFC9097-8.1-3
OB-UDPST: OB-UDPST’s row 0 is about 0.11 Mbps too, from a random-size add-on.
By default: on
Revisit: If a search’s first Trial Intervals at row 0 mattered, or matching OB-UDPST’s table stopped mattering.
Proving tests:
crates/udpstp-core/tests/sending_rate_table.rs::row_zero_sends_fixed_size_datagrams_near_0_1_mbpscrates/udpstp-core/tests/sending_rate_table.rs::row_n_is_n_mbps_up_to_1_gbps
table-steps-above-1-gbps
Above the 1 Gbps row the Sending Rate Table follows OB-UDPST’s rows, so a search runs alike against either. Its steps are 100 Mbps to 10 Gbps with the default 1250-byte packets, as recommended, but 120 Mbps with Traditional MTU’s 1500-byte packets, and with Jumbo 100 Mbps only to 7.2 Gbps, then 720 Mbps (one more 9000-byte packet a Burst) to 72 Gbps, not 1 Gbps above 10 Gbps.
RFC: RFC 9097 §8.1
Requirements: RFC9097-8.1-4, RFC9097-8.1-5
OB-UDPST: OB-UDPST’s table has the same rows.
By default: on
Revisit: If matching OB-UDPST’s table stopped mattering, or searches above 7.2 Gbps needed finer steps than 720 Mbps.
Proving tests: