RFC 9946

The UDP Speed Test Protocol (UDPSTP) for One-Way IP Capacity Metric Measurement

๐Ÿ“… published April 2026, from ietf

RFC 9946: The UDP Speed Test Protocol (UDPSTP) for One-Way IP Capacity Metric Measurement โœจ

Who wrote it: A. Morton and L. Ciavattone (AT&T Labs), plus R. Geib, Ed. (Deutsche Telekom) ๐Ÿ’– Published by: the Internet Engineering Task Force (IETF) ๐ŸŒธ Date: April 2026 ๐Ÿ“… Category: Standards Track ๐ŸŽ€ (it's an Internet Standards Track document) ISSN: 2070-1721 Obsoletes / Updates: The header doesn't list any Obsoletes or Updates entries ๐Ÿ™ˆ


Abstract

okay so this doc is about measuring the One-Way IP Capacity metrics that RFC 9097 defines ๐Ÿ’…. The measurement method in that RFC needs a feedback channel going from the receiver back to the sender, so the sender can adjust its transmission rate in near real-time ๐Ÿ”ฅ. This document defines the UDP Speed Test Protocol (UDPSTP) for running measurements as described in RFC 9097 and other related measurements โœจ.

Status of This Memo

This is an Internet Standards Track document ๐ŸŽ€.

It's a product of the Internet Engineering Task Force (IETF), so it represents the consensus of the IETF community ๐Ÿซถ. It got public review and was approved for publication by the Internet Engineering Steering Group (IESG) ๐Ÿ‘‘. If you want more on Internet Standards, check out Section 2 of RFC 7841 ๐Ÿงš.

For the current status, any errata, and how to send feedback, go to https://www.rfc-editor.org/info/rfc9946 ๐Ÿฆ‹.

Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved ๐Ÿ’œ.

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) as they're in effect on the publication date. Please actually read these, bestie, because they explain your rights and restrictions for this document ๐Ÿ“œ. Code Components pulled out of this document must include Revised BSD License text, as described in Section 4.e of the Trust Legal Provisions. They come without warranty, as described in the Revised BSD License ๐Ÿ™ˆ.

Table of Contents

   1.  Introduction
   2.  Conventions and Terminology
   3.  Scope, Goals, and Applicability
     4.  Protocol Overview
       4.1.  Fixed-Rate Testing
       4.2.  Handling of and Safeguards Required by Self-Induced
             Congestion
   5.  Requirements, Security Operations, and Optional Checksum
       5.1.  Load Rate Adjustment Algorithm Requirements
       5.2.  Parameters and Definitions
       5.3.  Security Mode Operations
         5.3.1.  Mode 1: Required Authenticated Mode
         5.3.2.  Mode 2: Optional Authenticated Mode for Data Phase
       5.4.  Key Management
         5.4.1.  Key Derivation Function (KDF)
       5.5.  Configuration of Network Functions with Stateful Filtering
       5.6.  Optional Checksum
   6.  Test Setup Request and Response
       6.1.  Client Generates Test Setup Request
       6.2.  Server Test Setup Request Processing and Response
             Generation
         6.2.1.  Test Setup Request Processing -- Rejection
         6.2.2.  Test Setup Request Processing -- Acceptance
       6.3.  Setup Response Processing at the Client
   7.  Test Activation Request and Response
       7.1.  Client Generates Test Activation Request
       7.2.  Server Processes Test Activation Request and Generates
             Response
         7.2.1.  Server Rejects or Modifies Request
         7.2.2.  Server Accepts Request and Generates Response
       7.3.  Client Processes Test Activation Response
   8.  Test Load Stream Transmission and Measurement Status Feedback
             Messages
       8.1.  Load PDU and Roles
       8.2.  Status PDU
   9.  Stopping a Test
   10. Operational Considerations for the Measurement Method
       10.1.  Notes on Interface Measurements
   11. Security Considerations
   12. IANA Considerations
       12.1.  New User Port Number Assignment
       12.2.  New KeyTable KDF
       12.3.  New UDPSTP Registry Group
         12.3.1.  PDU Identifier Registry
         12.3.2.  Protocol Version Registry
         12.3.3.  Test Setup PDU Modifier Bitmap Registry
         12.3.4.  Test Setup PDU Authentication Mode Registry
         12.3.5.  Test Setup PDU Command Response Field Registry
         12.3.6.  Test Activation PDU Command Request Registry
         12.3.7.  Test Activation PDU Modifier Bitmap Registry
         12.3.8.  Test Activation PDU Rate Adjustment Algo Registry
         12.3.9.  Test Activation PDU Command Response Field Registry
       12.4.  Guidelines for Designated Experts
   13. References
       13.1.  Normative References
       13.2.  Informative References
   Appendix A.  KDF Example (OpenSSL)
   Acknowledgments
   Authors' Addresses

1. Introduction โœจ

okay so the performance community has been cooking for a while ๐Ÿณ. They wrote informative Bulk Transport Capacity (BTC) definitions in "A Framework for Defining Empirical Bulk Transfer Capacity Metrics" [RFC3148] and "Defining Network Capacity" [RFC5136], and they tried out experimental metric definitions and methods in "Model-Based Metrics for Bulk Transport Capacity" [RFC8337] ๐Ÿ’œ.

This document specifies the UDP Speed Test Protocol (UDPSTP), which lets you measure One-Way IP Capacity metrics as defined by [RFC9097] ๐ŸŽ€. The Method of Measurement in that RFC uses a feedback channel from the receiver to control the sender's transmission rate in near real-time. Section 8.1 of [RFC9097] lays out the requirements for this method ๐Ÿ”ฅ.

UDPSTP does stuff that TCP-based speed tests and standard measurement protocols couldn't do before it existed. That includes the One-Way Active Measurement Protocol (OWAMP) [RFC4656], the Two-Way Active Measurement Protocol (TWAMP) [RFC5357], and the Simple Two-Way Active Measurement Protocol (STAMP) [RFC8762] ๐Ÿฆ‹. The controlled BTC measurement, which is the Speed Test, is built on UDP instead of TCP ๐Ÿ’…. The bulk measurement load only goes one direction. Those specs could already make asymmetric traffic, combined with some two-way communication (TWAMP and STAMP can do this), by the time UDPSTP work started ๐Ÿ™ˆ. But two-way communication in TWAMP and STAMP is limited to reflection or unidirectional load properties, and neither has closed loop feedback operation. Closed loop feedback is what limits congestion at a bottleneck (the one whose capacity you're measuring) to a short time range. Supporting that control loop is the main point of UDPSTP ๐Ÿ‘‘.

Beyond measurement, a Key Derivation Function (KDF) was added to give cryptographic separation of key material for authenticating protocol messages, in a standardized and cryptographically secure way ๐Ÿ”. That's a secondary win for UDPSTP, and it may make the protocol easier to reuse for other measurement purposes โœจ. Also, the protocol uses synthetic payload data and has no direct user information in it, so the authors chose not to support encryption ๐Ÿงš. They expect this to let more low-end devices run the test methodology too ๐Ÿ’–.

2. Conventions and Terminology ๐ŸŽ€

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document get interpreted the way BCP 14 [RFC2119] [RFC8174] describes. That only happens when they appear in all capitals, exactly like this ๐Ÿ”ฅ.

Downstream UDP Speed Test: a Network Capacity measurement that the client starts, going from a server acting as sender to a client acting as receiver ๐Ÿฆ‹.

Upstream UDP Speed Test: a Network Capacity measurement that the client starts, going from a client acting as sender to a server acting as receiver ๐ŸŒธ.

In this document, "message" and "Protocol Data Unit" (or "PDU" [RFC5044]) mean the same thing, so they're used interchangeably ๐Ÿ’….

3. Scope, Goals, and Applicability ๐Ÿฆ‹

The scope of this document is to define a protocol for measuring the Maximum IP-Layer Capacity Metric, using the Method of Measurement standardized in Section 8 of [RFC9097] ๐Ÿ’œ. So this document follows the applicability scope defined in Section 2 of [RFC9097] โœจ.

Some aspects of this protocol and end-host configuration can open the door to other kinds of measurement. Application emulation is one example, if you get creative with the load rate adjustment algorithm ๐Ÿงš. But per [RFC9097], that algorithm must not be used as a general Congestion Control Algorithm (CCA) ๐Ÿ™ˆ. Its real job is to help figure out the Maximum IP-Layer Capacity during an infrequent, diagnostic, short-term measurement ๐Ÿ”ฅ.

The goal is to harmonize the specified IP-Layer Capacity Metric and Method across the whole industry. UDPSTP supports the specs from the IETF (see [RFC9097]) and from other Standards Development Organizations (SDOs) (see, e.g., [TR-471]) ๐Ÿ‘‘.

The main application of UDPSTP is the same as in Section 2 of [RFC7497], which says:

The access portion of the network is the focus of this problem statement. The user typically subscribes to a service with bidirectional access partly described by rates in bits per second.

UDPSTP is a client-based protocol ๐ŸŽ€. Consumers can use it to measure their own access bandwidth ๐Ÿ’–. They may prefer an independent third-party domain hosting the measurement server for that ๐Ÿซถ. UDPSTP can be deployed in Large-scale Measurement of Broadband Performance (LMAP) environments (see [RFC7497]) and in other independent third-party domain measurement server deployments ๐ŸŒˆ. A network operator can also use UDPSTP to support operation and maintenance inside its own domain, which is a typical intra-domain deployment ๐Ÿ“. All of these deployments require, or benefit from, trusting the results, and authenticated communication is what makes that trust possible ๐Ÿ”๐Ÿ’….

4. Protocol Overview โœจ

Okay bestie, first things first: every message defined in this doc SHALL use UDP transport [RFC0768] ๐Ÿซถ

The rest of this section is an informative overview of how two test endpoints chat with each other ๐Ÿ’ฌ. It doesn't set requirements, and we're skipping the authentication stuff for now.

So one endpoint is the server ๐Ÿ–ฅ๏ธ, listening for connection requests from the other endpoint (the client) on a standard UDP Speed Test Protocol port number.

The client needs a test direction parameter (upstream or downstream test, where the client is the sender or receiver, respectively) plus the hostname or IP address(es) of the server(s) so it can start the setup and configuration exchanges ๐Ÿ“‹. By default, the client uses the single, standard UDPSTP port number per connection (see Section 6). If that default port number isn't used, the client may need the control port number used by each server. That happens when multiple server instances (processes) run on one or more machines.

Multi-connection (multi-flow) testing is supported too ๐ŸŒˆ. Each connection is independent and tries to max out its own traffic rate. For multi-connection tests, one client process repeats the connection setup and test procedure once per flow, aimed at one or more server instances. The server instances treat each connection independently, as if they came from separate clients. It's the client process's job to manage the related connections: handling each connection's setup successes and failures, cleaning up connections during a test if some fail, and combining the individual results into one overall set of performance statistics ๐Ÿ“Š. The fields in the Setup Request (mcIndex, mcCount, and mcIdent; see Section 6.1) are how the connections get told apart and tied together as one test ๐Ÿงฉ.

The protocol uses UDP transport with two connection phases, Control and Data. As shown below, exchanges 1 and 2 make up the Control phase, while exchanges 3 and 4 make up the Data phase. In this document, "message" and "Protocol Data Unit", or "PDU" [RFC5044], are used interchangeably ๐Ÿค“.

  1. Test Setup Request and Response: If a server instance is identified by a host name that resolves to both IPv4 and IPv6 addresses, it is recommended to use the first address returned in the name resolution response, whether it's IPv4 or IPv6. So the preferred IP address family is left up to the name resolver's default behavior ๐Ÿคทโ€โ™€๏ธ. Separate IPv4 and IPv6 measurements, or an IPv4 and IPv6 multi-connection setup, are left for future improvement. The client then asks to start a test by sharing its UDPSTP protocol version, intended security mode, and datagram size support. The server either confirms that it matches a configuration or rejects the request. If the request is accepted, the server gives each test connection a unique ephemeral port number so they can keep talking ๐Ÿ“ฎ. In a multi-connection setup, distinct UDP port numbers may be assigned with each Setup Response from a server instance. Distinct UDP port numbers will be assigned if all Setup Response messages originate from the same server in that case.
  2. Test Activation Request and Response: After the client gets confirmation of the configuration from a server, it composes a request with parameters like the testing direction, the duration of the test interval and test Sub-Intervals, and various thresholds (for a detailed discussion, see [RFC9097] and [TR-471]) ๐ŸŽฏ. The server then chooses to accept, ignore, or modify any of the test parameters, and tells the client which set will be used unless the client rejects the modifications. Note that the client assumes the Test Activation exchange has opened any co-located firewalls and network address/port translators for the test connection (in response to the Request packet on the ephemeral port number) and for the traffic that follows. See [RFC9097] for a more detailed discussion of firewall and NAT-related features. If the Test Activation Request is rejected or fails, the client assumes the firewall will close the address/port number pinhole entry after the firewall's configured idle traffic timeout โณ.
  3. Test Stream Transmission and Measurement Feedback Messages: Testing goes on with one endpoint sending the Load PDUs while the other receives them and sends frequent status messages about its status and reception conditions ๐ŸŒŠ. The data in the feedback messages, whether it's received from the client or being sent to the client, feeds a load rate adjustment algorithm at the server, and that algorithm controls future sending rates at either end. Putting the algorithm at the server, regardless of transmission direction, means it can be updated more easily at a host inside the network and at a smaller number of hosts than there are clients ๐Ÿ› ๏ธ. Note that the status messages also help keep the pinhole (or mapping, respectively) active at on-path stateful devices. UDPSTP is at least partially compliant to Section 3.1 of [RFC8085] if the bottleneck is congested, but pending congestion is avoided by limiting the duration of that congestion to the minimum required to determine the bottleneck capacity ๐Ÿšฆ.
  4. Stopping the Test: Once the specified test duration has been reached, the server starts the exchange to stop the test by setting a STOP indication in its outgoing Load PDUs or Status Feedback messages ๐Ÿ›‘. After the client receives that, it acknowledges it by also setting a STOP indication in its outgoing Load PDUs or Status Feedback messages. A graceful connection termination at each end follows. Since the Load PDUs and Status Feedback messages are used, this exchange is considered a sub-exchange of 3 above. If the test traffic stops or the communication path fails, the client assumes the firewall will close the address/port number combination after the firewall's configured idle traffic timeout.
  5. Both the client and server react to unexpected interruptions in the Control or Data phase, respectively. Watchdog timers limit how long a server or client will wait before stopping all traffic and terminating a test โฐ.

Figure 1 is an example exchange of control and measurement PDUs for both downstream and upstream UDP Speed Tests (always client initiated) ๐ŸŽ€:

                =========== Downstream Test ===========
   +---------+                                           +---------+
   | Client  |          Test Setup Request ----->        |  Server |
   +---------+                                           +---------+
               <----- Test Setup Response (Accept)
               <----- Null Request PDU

                      Test Activation Request ----->

               <----- Test Activation Response (Accept)

               <----- Load PDUs

                         Status Feedback PDUs ----->

             After expiry of server's test duration timer...

               <----- Load PDU (TEST_ACT_STOP)

              Status Feedback PDU (TEST_ACT_STOP) ----->


                ============ Upstream Test ============
   +---------+                                           +---------+
   | Client  |          Test Setup Request ----->        |  Server |
   +---------+                                           +---------+
               <----- Test Setup Response (Accept)
               <----- Null Request PDU

                      Test Activation Request ----->

               <----- Test Activation Response (Accept)

                         Load PDUs ----->

               <----- Status Feedback PDUs

             After expiry of server's test duration timer...

               <----- Status Feedback PDU (TEST_ACT_STOP)

                       Load PDU (TEST_ACT_STOP) ----->

Figure 1: Successful UDPSTP Message Exchanges


4.1. Fixed-Rate Testing

A network operator who is certain of the IP-Layer Capacity to be validated can run a fixed-rate test of the IP-Layer Capacity and skip activating the measurement load rate adjustment algorithm (see Section 8.1 of [RFC9097]) ๐ŸŽฏ. Fixed-rate testing SHOULD only be activated for operation and maintenance purposes by operators within their local network domain.

If a subscriber asks the network operator for a diagnostic test, that strongly implies there's no certainty about the bottleneck capacity ๐Ÿค”. So starting a UDP Speed Test based on the load adjustment algorithm is RECOMMENDED. To protect against misuse, a client (and in general, a consumer) MUST NOT be able to start a fixed-rate test ๐Ÿšซ. A network operator may run a fixed-rate test for a stable measurement at or near the maximum determined by the load rate adjustment algorithm for debugging purposes. This may be valuable for post-installation or post-repair verification ๐Ÿ”ง.


4.2. Handling of and Safeguards Required by Self-Induced Congestion

Active capacity measurement requires inducing intentional congestion ๐Ÿ’ฅ. On paths where the capacity bottleneck is not shared with other flows, this self-congestion will be observed as loss and/or delay. But when a path is shared by other flows, the measurement traffic can congest the bottleneck on the path and therefore degrade the performance of other flows. Unrestricted use of UDPSTP could lead to traffic starvation and significant issues ๐Ÿ˜ญ.

Heads up: fixed-rate testing doesn't use congestion control, so keep it away from other operators' network segments ๐Ÿšจ.

Measurements that generate traffic on shared paths (including Wi-Fi and Internet paths) need to consider the impact on other traffic ๐Ÿซฃ. Fixed-rate testing operates without congestion control and therefore must not be executed over other operators' network segments. Fixed-rate testing, therefore, is limited to paths within a domain entirely managed and operated section-wise and end-to-end by the network operator performing the measurement. When the risks of disruption to other flows has been considered, testing could be extended to include adjacent operational domains for which there is also a testing agreement ๐Ÿค.

Concurrent tests that congest a common bottleneck will impair the measurement and result in additional congestion. Concurrent measurements to measure the maximum capacity on a single path are counterproductive ๐Ÿ™ƒ. The number of concurrent independent tests of a path SHALL be limited to one, regardless of the number of flows ๐Ÿ‘‘.

A load rate adjustment algorithm (see Section 5.1) is required to mitigate the impact of this congestion and to limit the duration of any congestion by terminating the test when sudden impairments or a loss of connectivity is detected ๐ŸŒธ.

5. Requirements, Security Operations, and Optional Checksum โœจ

okay bestie, so [RFC9097] only covers the Method of Measurement ๐Ÿงš. It says nothing about security or the optional checksum, so this section fills that gap with the operational specs for both ๐Ÿ’…. Once you lose the direct mapping of packets to datagrams between Layer 3 and Layer 4, things get messy, so it is recommended that Layer 3 fragmentation be avoided ๐Ÿ™ˆ. A simple move is to pick a default datagram size that's small enough to stop fragmentation from happening. This version doesn't support Datagram Packetization Layer Path MTU Discovery (DPLPMTUD) [RFC8899]. A future version could specify how, but DPLPMTUD support needs a carefully adapted protocol design so everyone can interoperate ๐Ÿซถ. Unless IP fragmentation is what you're expecting and measuring, the IPv4 Don't Fragment (DF) bit SHOULD be set for all tests.

Note: When this specification is used for network debugging, it may be useful for fragmentation to be under the control of the test administrator. ๐Ÿ“

This section lays out the generic requirements that a measurement load rate adjustment algorithm conforming to this specification MUST fulfill ๐Ÿ‘‘.

5.1. Load Rate Adjustment Algorithm Requirements

This document describes an active capacity measurement method that uses a load rate adjustment algorithm ๐Ÿฆ‹. The requirements in this section, plus the currently standardized load rate adjustment algorithms B [Y.1540Amd2] and C [TR-471], came from years of experiments by the original authors. Those tests ran in labs and on the actual Internet, across fixed, broadband, mobile, and wireless access types and technologies in different countries and continents ๐ŸŒ. The requirements also reflect feedback from performance measurement experts, plus changes from the standardization of [RFC9097] (which is also reflected in algorithm B [Y.1540Amd2], an update to an earlier version of this algorithm). Very thorough, very iconic.

Load rate adjustment algorithms for capacity measurement MUST comply with the requirements in this section. New standard load rate adjustment algorithms for capacity measurement MUST be reviewed by IETF designated experts before a code point gets assigned in the Test Activation PDU Rate Adjustment Algo registry ๐Ÿ”ฅ.

Here are the load rate adjustment algorithm requirements for capacity measurement:

  1. The measurement load rate adjustment algorithm described in this section MUST NOT be used as a general CCA. ๐Ÿ™…โ€โ™€๏ธ
  2. This specification MUST only be used for diagnostic and operations measurements.
  3. Both Load PDU messages and Status Feedback PDU messages MUST contain sequence numbers ๐Ÿ”ข.
  4. The nominal duration of a measurement interval at the Destination, parameter testIntTime ("I" in [RFC9097]), MUST default to a value of no more than 10 seconds โฑ๏ธ.
  5. A high-speed mode that aims for high sending rates quickly MUST reduce the measurement load below the level where the first feedback interval inferred "congestion" from the measurements. Consecutive feedback intervals that have a supra-threshold count of sequence number anomalies and/or contain an upper delay variation threshold exception in all of the consecutive intervals indicate "congestion" within a test. The threshold of consecutive feedback intervals SHALL be configurable, with a default of 3 intervals and a maximum duration to infer congestion of 500 ms (milliseconds) ๐Ÿ˜ค.
  6. Congestion MUST be indicated if the Status Feedback PDUs show that either sequence number anomalies were detected OR the delay range was above the upper delay variation threshold. The RECOMMENDED threshold values are 10 for sequence number gaps, 30 ms for lower delay variation thresholds, and 90 ms for upper delay variation thresholds ๐Ÿ“Š.
  7. The load rate adjustment algorithm MUST include a Load PDU timeout and a Status PDU timeout, which both stop the test when received PDU streams cease unexpectedly ๐Ÿ›‘.
  8. The Load PDU timeout SHALL be reset to the configured value each time a Load PDU is received. If the Load PDU timeout expires, the receiver SHALL be closed and no further Status PDU feedback sent. The default Load PDU timeout MUST be no more than 1 second โณ.
  9. The Status PDU timeout SHALL be reset to the configured value each time a feedback message is received. If the Status PDU timeout expires, the sender SHALL be closed and no further load packets sent. The default Status PDU timeout MUST be no more than 1 second โณ.
  10. A network operator who is certain of the IP-Layer Capacity to be validated MAY start with a fixed-rate test at the IP-Layer Capacity and skip activating the measurement load rate adjustment algorithm (see Section 8.1 of [RFC9097]). However, the stimulus for a diagnostic test (such as a subscriber request) strongly implies that there is no certainty, so the load adjustment algorithm is RECOMMENDED ๐Ÿคทโ€โ™€๏ธ.
  11. This specification MUST only be used in circumstances consistent with Section 10 (Security Considerations) of [RFC9097] ๐Ÿ”.
  12. Further measurement load rate adjustment algorithm requirements are specified by [RFC9097].

The following measurement load rate adjustment algorithms are subject to these requirements:

  • Measurement load rate adjustment algorithm B [Y.1540Amd2] ๐Ÿ’œ
  • Measurement load rate adjustment algorithm C [TR-471] ๐Ÿ’œ

5.2. Parameters and Definitions

Please check Section 4 of [RFC9097] for an overview of the parameters for the Maximum IP-Layer Capacity Metric and Method ๐Ÿ“š. There's also a set of error codes to help with debugging in Section 12.3.5 ๐Ÿงš.

5.3. Security Mode Operations

There are two security modes of operation that perform authentication of the client/server messaging ๐Ÿ”’. The two modes are:

  1. A REQUIRED mode with authentication during the Control phase (Test Setup and Test Activation exchanges). This mode may be preferred for large-scale servers or low-end client devices where processing power is a consideration (see Section 3) ๐Ÿ–ฅ๏ธ.
  2. An OPTIONAL mode with the additional authentication of the Status Feedback messages during the Data phase. This mode may be preferred for environments that want an extra layer of message integrity verification throughout the test (see Section 3) ๐Ÿ›ก๏ธ.

The requirements below refer to the PDUs in Sections 6 and 7, mainly the authMode, keyId, authUnixTime, and authDigest fields ๐ŸŽ€. The roles in this section are generalized so the requirements for the PDU sender and receiver can be re-used and referred to by other sections of this document. Each successive mode adds more security but also more performance impact and complexity. The protocol carries an unsubstantial payload and may run on very low-end devices, so being able to choose among security operation modes lets it fit whatever end-device resources are available ๐Ÿซง. In general, an active measurement technique like the one defined here is better suited to protecting the privacy of the people involved in measurements [RFC7594] ๐Ÿฆ‹.

A load rate adjustment method needs to satisfy the requirements listed in Section 5.1. This also helps avoid potentially inducing congestion after an overload or loss, including loss on the control path ๐Ÿšฆ.

5.3.1. Mode 1: Required Authenticated Mode

In this mode, the client and the server SHALL be configured to use one of a number of shared secret keys, designated by the numeric keyId field (see Section 5.4). This key SHALL be used as input to the KDF, as specified in Section 5.4.1, to get the actual keys the client and server use for authentication ๐Ÿ—๏ธ.

During the Control phase, the sender SHALL read the current system (wall-clock) time and fill in the authUnixTime field. Next, it calculates the 32-octet HMAC-SHA-256 hash of the entire PDU according to Section 6 of [RFC6234], with the authDigest and checkSum fields preset to all zeroes. The authDigest field is then filled with the result, and the packet is sent to the receiver ๐Ÿ“จ. The value in the authUnixTime field is a 32-bit timestamp, and a 10-second tolerance window (+/- 5 seconds) SHALL be used by the receiver to tell apart a later replay of a PDU โฐ. See Table 2 of [TR-471] for a recommended timestamp resolution.

When a PDU comes in, the receiver SHALL validate it for correct length, validity of authDigest, immediacy of authUnixTime, and expected formatting. PDU-specific fields are checked too, such as protocol version ๐Ÿง. To validate authDigest, the receiver extracts it from the PDU, zeroes it along with the checkSum field, and then does the HMAC calculation to compare against (see Section 7.2 of [RFC9145]).

If validation fails, the receiver SHOULD NOT continue with the Control phase and SHOULD implement silent rejection (no further packets sent on the address/port pairs) ๐Ÿค. The exception is when the testing hosts are configured for troubleshooting Control phase failures and rejection messages will help with that.

If validation succeeds, the receiver SHALL continue with the Control phase and compose either a successful response or a response that indicates any error conditions found โœ….

This process SHALL be executed for the request and response in the Test Setup exchange, including the Null Request (Section 6), and for the Test Activation exchange (Section 7) ๐Ÿ’–.

5.3.2. Mode 2: Optional Authenticated Mode for Data Phase

This mode builds on Authenticated mode 1. When the optional authentication is used during the Data phase, authentication SHALL also be applied to the Status Feedback PDU (see Section 8.2). The client sends the Status PDU in a downstream test, and the server sends it in an upstream test ๐ŸŒธ.

The Status PDU sender SHALL 1) read the current system (wall-clock) time and populate the authUnixTime field, 2) calculate the authDigest field of the entire Status PDU (with the authDigest and checkSum fields preset to all zeroes), and 3) send the packet to the receiver. The values of the authUnixTime and authDigest fields are determined as defined in Section 5.3.1 ๐Ÿง.

When a PDU comes in, the receiver SHALL validate it for correct length, validity of authDigest, immediacy of authUnixTime, and expected formatting. PDU-specific fields are checked too, such as protocol version. To validate authDigest, the receiver extracts it from the PDU, zeroes it along with the checkSum field, and then does the HMAC calculation to compare against ๐Ÿ”.

If authentication validation fails, the receiver SHALL ignore the message ๐Ÿšซ. If the watchdog timer expires (because of successive failed validations), the test session will end early, and no further load traffic SHALL be transmitted. This is also needed to avoid potentially inducing congestion after an overload or loss on the control path ๐Ÿซ .

If this optional mode hasn't been selected, then the keyId, authUnixTime, and authDigest fields of the Status PDU (see Section 8.2) SHALL be set to all zeroes ๐ŸŒˆ.

5.4. Key Management ๐Ÿ”‘

Okay so Section 2 of [RFC7210] is the spec for a conceptual database of long-lived cryptographic keys ๐Ÿ—๏ธ. The key table SHALL be used with the REQUIRED authentication mode and the OPTIONAL authentication mode (using the same key). For authentication, this key SHALL only be used as input to the KDF, as specified in Section 5.4.1, to derive the actual keys used for authentication processing. Key rotation and related management specifics are beyond the scope of this document, so that's a whole other slay for another time ๐Ÿ’….

The key table SHALL have (at least) the following fields per Section 2 of [RFC7210]:

  • AdminKeyName
  • LocalKeyName
  • KDF
  • AlgID
  • Key
  • SendLifetimeStart
  • SendLifeTimeEnd
  • AcceptLifeTimeStart
  • AcceptLifeTimeEnd

The LocalKeyName SHALL be determined from the corresponding keyId field in the PDUs that follow ๐ŸŽ€.

5.4.1. Key Derivation Function (KDF)

A KDF is a one-way function that gives you cryptographic separation of key material ๐Ÿงš. The protocol needs a KDF to securely derive the cryptographic keys used for authentication of protocol messages. Using a KDF means keys get generated in a standardized, cryptographically secure way, which lowers the risk of key compromise and helps different implementations work together ๐Ÿค. The benefits of using a KDF include:

  • Security: A KDF produces keys with high entropy, resistant to brute-force and related-key attacks, ensuring robust protection for protocol communications ๐Ÿ”.
  • Flexibility: The KDF allows derivation of multiple keys from a single shared secret, supporting distinct keys for client and server authentication ๐Ÿฆ‹.
  • Standardization: By adhering to established cryptographic standards, the KDF ensures compatibility with existing security frameworks and facilitates implementation audits ๐Ÿ“‹.
  • Efficiency: The KDF enables efficient key generation without requiring additional key exchange mechanisms, minimizing protocol overhead โšก.

The KDF algorithm SHALL be a Key Derivation Function in Counter Mode, as specified in Section 4.1 of [NIST800-108]. This algorithm uses a counter-based mechanism to generate key material from a shared secret, which is deterministic and secure key derivation, no cap ๐Ÿงฎ. The Pseudorandom Function (PRF) used in the KDF SHALL be HMAC-SHA-256, as defined in Section 6 of [RFC6234]. IANA has assigned "HMAC-SHA-256" as a new KeyTable KDF (Section 12.2) ๐ŸŒธ.

The KDF SHALL use the following parameters:

  • Kin (key-derivation key): The shared key as identified by the keyId field in the PDU.
  • Label: The fixed string "UDPSTP" (without quotes), encoded as a UTF-8 string, used to bind the derived keys to this specific protocol ๐Ÿท๏ธ.
  • Context: The UTF-8 string representation of the authUnixTime field received in the very first Setup Request PDU sent from the client to the server. This makes the derived keys unique to the session and ties them to the temporal context of the initial setup exchange โฐ. The authUnixTime field serves as a nonce and is protected from modification by the HMAC-SHA-256 hash present in the authDigest field ๐Ÿ™Œ.
  • r: The length of the binary encoding of the counter SHALL be 32 (bits).

The total derived key material SHALL be 512 bits (64 octets) in length. The key material SHALL be structured as follows, from most significant bit (MSB) to least significant bit (LSB) ๐Ÿ‘‘:

  • Client Authentication Key: 256 bits (32 octets); used for authenticating messages sent by the client.
  • Server Authentication Key: 256 bits (32 octets); used for authenticating messages sent by the server.

This structure makes sure the derived keys are enough for securing authentication operations within the protocol, while keeping a clear separation of function and directionality. Iconic behavior ๐Ÿ’œ.

If authentication of the initial Setup Request PDU received by the server fails, due to an invalid authDigest field, any and all derived keying material and keys SHALL be considered invalid. ๐Ÿ’€ Big yikes, so be careful!

The key material derived from the initial Setup Request PDU, either at the client prior to transmission or at the server upon reception, SHALL be used for all subsequent PDUs sent between them for that test connection. So the KDF only needs to run once by the client and server for each test connection โœจ.

Appendix A, Figure 12 has a code snippet showing how to derive the specified keys from key material using the OpenSSL cryptographic library, specifically the high-level Key-Based EVP_KDF implementation (Key-Based Envelope Key Derivation Function). See [EVP_KDF-KB] for details ๐Ÿ“š.

5.5. Configuration of Network Functions with Stateful Filtering ๐Ÿ›ก๏ธ

Successful interaction with a local firewall assumes the firewall is configured to let a host open a bidirectional connection using unique source and destination addresses as well as port numbers (i.e., a 4-tuple), by sending a packet using that 4-tuple for a given transport protocol. The client's interaction with its firewall depends on this configuration ๐Ÿซถ.

The firewall at the server MUST be configured with an open pinhole for the server IP address and standard UDP port number of the server. All messages sent by the client to the server use this standard UDP port number ๐Ÿšช.

The server uses one ephemeral UDP port number per test connection. Assuming the firewall administration at the server does not allow an open UDP ephemeral port range, then the server MUST send a Null Request to the client from the ephemeral port number communicated to the client in the Test Setup Response. The Null Request may not reach the client, since it may be discarded by the client's firewall ๐Ÿ™ˆ.

If the server firewall administration allows an open UDP ephemeral port range, then the Null Request is not strictly necessary. However, the availability of an open port range policy cannot be assumed ๐Ÿคทโ€โ™€๏ธ.

Network Address Translators (NATs) are expected to offer support for a wider set of operational configurations compared to firewalls. Specifications covering NAT behavior, apart from the above, are out of scope for this document, and so are combined implementations of NAT and firewalls too ๐ŸŒˆ.

5.6. Optional Checksum ๐Ÿง

The protocol MUST utilize the standard UDP checksum for all IPv4 and IPv6 datagrams it sends. The purpose of this checksum is to protect the intended recipient, as well as other recipients to whom a corrupted packet may be delivered. This provides:

  • Protection of the endpoint transport state from unnecessary extra state (e.g., Invalid state from rogue packets).
  • Protection of the endpoint transport state from corruption of internal state.
  • Pre-filtering by the endpoint of erroneous data, to protect the transport from unnecessary processing and from corruption that it cannot itself reject.
  • Pre-filtering of incorrectly addressed destination packets, before responding to a source address.

All of the PDUs exchanged between the client and server support an optional header checksum that covers the various fields in the UDPSTP PDU (excluding the payload content of the Load PDU and, to be clear, also the IP and UDP headers). The calculation is the same as the 16-bit one's complement Internet checksum used in the IPv4 packet Header Checksum specification (see Section 3.1 of [RFC0791]). This checksum is meant for environments where UDP data integrity may be uncertain. That includes situations where the standard UDP checksum is not verified upon reception, or a nonstandard network API is in use (things typically done to improve performance on low-end devices). However, all UDPSTP datagrams transmitted via IPv4 or IPv6 SHALL include a standard UDP checksum to protect other potential recipients to whom a corrupted packet may be delivered. In the case of a nonstandard network API, one option to reduce processing overhead may be to restrict testing to only utilize a payload content of all zeros, so the UDP checksum calculation need not include it for Load PDUs ๐Ÿงฎ.

If a PDU sender is populating the checkSum field, it SHALL do so as the last step after the PDU is built in all other respects (with the checkSum field set to zero prior to the calculation). The PDU receiver SHALL subsequently verify the PDU checksum whenever checksum processing has been configured and the field is populated. If PDU checksum validation fails, the PDU SHALL be discarded ๐Ÿ—‘๏ธ.

Because of the redundancy when used in conjunction with authentication, it is OPTIONAL for a PDU sender to utilize the UDPSTP checkSum field. However, because authentication is not applicable to the Load PDU, the checkSum field SHALL be utilized by the sender whenever UDP data integrity may be uncertain (as outlined above) ๐Ÿซง.

6. Test Setup Request and Response ๐Ÿ’Œ

The client source IP address and the server destination IP address MUST NOT be a broadcast or multicast address. Any Test Setup Request or Test Setup Response packet containing a multicast or broadcast source or destination IP address MUST be silently dropped and ignored ๐Ÿšซ.

The measurement method and the protocol specified by this document are expected to function with unicast and anycast IP addresses ๐Ÿฆ‹.

6.1. Client Generates Test Setup Request

Okay bestie, the client starts the Control phase by sending a Test Setup Request to the server's (standard) control port ๐Ÿ“ฎ. This standard UDPSTP port number gets reused for every connection in a multi-connection test ๐Ÿฆ‹. The client SHALL begin the Control phase exchange by sending a Test Setup Request message to the server's (standard) control port. This standard UDPSTP port number is utilized for each connection of a multi-connection test.

The client SHALL simultaneously start a test initiation timer โฐ. If the Control phase doesn't finish Test Setup and Test Activation in time, the client software SHALL exit (the UDP socket gets closed and an error message shows up for the user) ๐Ÿ’€. Lost messages mean a Test Setup and Test Activation failure, so no drama, just exit. The test initiation timer MAY reuse the test termination timeout value.

The watchdog timeout is set to a 1-second interval, which triggers a warning message saying the received traffic has stopped ๐Ÿ‘€. The test termination timeout is based on that watchdog interval and waits 2 extra seconds before triggering a non-graceful termination ๐Ÿซถ.

Note: Any field labeled as 'reserved for alignment', in any PDU, MUST be set to 0 and MUST be ignored upon receipt. ๐Ÿ™ˆ

The UDP PDU format layout SHALL be as follows (big-endian AB, starting with the most significant byte and ending with the least significant byte) ๐ŸŽ€:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|            pduId              |          protocolVer          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|    mcIndex    |    mcCount    |            mcIdent            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  cmdRequest   | cmdResponse   |         maxBandwidth          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           testPort            |modifierBitmap |   authMode    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         authUnixTime                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
.                     authDigest (32 octets)                    .
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     keyId     | reservedAuth1 |           checkSum            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                  Figure 2: Test Setup PDU Layout

Here are the extra details for the Setup Request and Response fields, so you know exactly what each one is doing ๐Ÿงš:

  • pduId: A two-octet field. IANA has assigned the hex value 0xACE1 (Section 12.3.1). โœจ
  • protocolVer: A two-octet field identifying the actual protocol version. IANA has assigned 20 as the value (Section 12.3.2). ๐Ÿ’œ
  • mcIndex: A one-octet field indicating the index of a connection relative to all connections that make up a single test (starting at 0, incremented by 1 per connection). It is used to differentiate separate connections within a multi-connection test. An implementation may restrict the number of connections supported for a single test to a value less than or equal to 255. ๐ŸŒธ
  • mcCount: A one-octet field indicating the total count of connections that the client is attempting to set up. ๐Ÿ“
  • mcIdent: A two-octet field containing a pseudorandom non-zero identifier (via a Random Number Generator, source port number, ...) that is common to all connections of a single test. It is used by clients/servers to associate separate connections with a single multi-connection test. ๐Ÿฆ‹
  • cmdRequest: A one-octet field set to CHSR_CREQ_SETUPREQ to indicate a Setup Request message. Note that CHSR_CREQ_NONE remains unused. ๐Ÿ’…
  • cmdResponse: A one-octet field. All Request PDUs always have a Command Response of XXXX_CRSP_NONE (i.e., CHSR_CRSP_NONE, CHNR_CRSP_NONE, or CHTA_CRSP_NONE). ๐Ÿ™ƒ
  • maxBandwidth: A two-octet field. A non-zero value of this field specifies the maximum bit rate the client expects to send or receive during the requested test in Mbps. The server compares this value to its currently available configured limit for test admission control. This field MAY be used to rate-limit the maximum rate the server should attempt. The maxBandwidth field's most significant bit, the CHSR_USDIR_BIT, is set to 0 by default to indicate "downstream" and has to be set to 1 to indicate "upstream". ๐ŸŒˆ
  • testPort: A two-octet field set to zero in the Test Setup Request and populated by the server in the Test Setup Response. It contains the UDP ephemeral port number on the server that the client has to use for the Test Activation Request and subsequent Load or Status PDUs. ๐ŸŽ€
  • modifierBitmap: A one-octet field. This document only assigns two bits in this bitmap; see Section 12.3.3:
    • CHSR_JUMBO_STATUS (0x01): This bit SHALL be set by default. By default, sending rates up to 1 Gbps SHALL NOT produce IP packet sizes greater than 1250 bytes (unless CHSR_TRADITIONAL_MTU is set), while rates above 1 Gbps MAY produce IP packet sizes up to 9000 bytes. When CHSR_JUMBO_STATUS is not set, all sending rates SHALL NOT produce IP packet sizes greater than 1250 bytes (unless CHSR_TRADITIONAL_MTU is set). ๐Ÿš€
    • CHSR_TRADITIONAL_MTU (0x02): This bit SHALL NOT be set by default. If set, sending rates up to 1 Gbps MAY produce IP packets up to the traditional size of 1500 bytes. If CHSR_JUMBO_STATUS is simultaneously not set, all sending rates SHALL NOT produce IP packets greater than the traditional size of 1500 bytes. ๐ŸŒ™

Other bit positions are left unassigned per this document. ๐Ÿซง

  • authMode: A one-octet field. The authMode field currently has two values assigned (see Section 12.3.4). One of the following has to be set (see Section 5.3 for requirements and details of operation):
    • AUTHMODE_1: Required authentication for the Control phase.
    • AUTHMODE_2: Required authentication for the Control and Data phases (Status Feedback PDU only). ๐Ÿ”

A range of 60 through 63 is reserved for experimentation. IANA has created the "Test Setup PDU Authentication Mode" registry for the assigned values; see Section 12.3.4. ๐Ÿงช

  • authUnixTime: A 32-bit timestamp of the current system (wall-clock) time since the Unix Epoch on January 1, 1970 at 00:00:00 UTC. โณ
  • authDigest: This field contains the 32-octet HMAC-SHA-256 hash that covers the entire PDU. Normally, the calculation is done as the last step of building the PDU. However, if the optional checkSum field is being utilized, it becomes the penultimate step and is done just prior to the checksum calculation (with the checkSum field set to zero). ๐Ÿงฎ
  • keyId: A one-octet field carrying localKeyName, the numeric key identifier for a key in the shared key table. ๐Ÿ—๏ธ
  • reservedAuth1: A one-octet field. This field MUST be set to 0 and MUST be ignored upon receipt. Consistent naming and placement of the reservedAuth1 field across all PDUs is done to minimize authentication-related changes in future UDPSTP versions. ๐Ÿงธ
  • checkSum: A two-octet field containing an optional checksum of the entire PDU (see Section 5.6 for guidance). The calculation is done as the very last step of building the PDU, with the checkSum field set to zero. ๐Ÿ’–

6.2. Server Test Setup Request Processing and Response Generation

This section is all about what the server does when a Setup Request lands โœจ. It describes the processes the server uses to evaluate the Test Setup Request and decide what happens next. When the server receives the Setup Request, it SHALL first perform the following:

Message Verification Procedure:

  1. Verify that the size of the message is correct. ๐Ÿ“
  2. If the optional checkSum field is being utilized, validate the checksum as described in Section 5.6 and (if valid) zero the checkSum field prior to authentication verification. ๐Ÿ”
  3. Verify that the authMode value is valid and appropriate (per Section 5.3) for the message type. ๐ŸŽฏ
  4. If the authMode is valid and appropriate, authenticate the message by checking the authDigest as prescribed in Section 5.3. ๐Ÿ”‘
  5. If the message is authentic, check the authUnixTime field for acceptable immediacy. โฑ๏ธ

Note: If any of the above checks fail, the message SHALL be considered invalid. ๐Ÿšซ

6.2.1. Test Setup Request Processing -- Rejection ๐Ÿ™…โ€โ™€๏ธ๐Ÿ’”

okay so bestie, this is the "uh oh, we're rejecting this" part ๐Ÿ˜ญ The server SHALL then check the other fields in the header ๐Ÿ‘€ like the protocol version, the PDU ID (so it knows what kind of message this is), the maximum bandwidth the test wants, and the modifierBitmap, which is where the options live: Jumbo datagram status and Traditional MTU (1500 bytes) ๐Ÿงš

If the client picked any of these:

  • Jumbo datagram support (modifierBitmap),
  • Traditional MTU (modifierBitmap), and
  • Authentication mode (authMode)

that don't match what the server is configured for, the server MUST reject the Setup Request. no exceptions bestie ๐Ÿ™…โ€โ™€๏ธ

Now, if the Setup Request does have to get rejected, whether the server actually says something back depends on the vibes of the authDigest ๐Ÿ”ฎ

  • If the authDigest is valid, a Test Setup Response SHALL be sent back to the client, with a Command Response value that explains why it got rejected ๐Ÿ’…
  • If the authDigest is invalid, the Test Setup Request SHOULD fail silently ๐Ÿคซ The one exception is for operations support: server administrators are permitted to send a Setup Response so they can handle operations and troubleshooting ๐Ÿ› ๏ธ

There are also some situations where the server SHALL NOT send the proper Command Response code for an error. Instead it just fails silently ๐Ÿซฅ That happens when:

  • the Setup Request PDU size is not equal to the struct controlHdrSR size shown in Figure 3,
  • the PDU ID is not 0xACE1 (Test Setup PDU), or
  • a directed attack has been detected.

In that last case the server will let setup attempts end silently โ˜ ๏ธ Attack detection is beyond the scope of this specification, so we're not going there ๐Ÿ™ˆ

When the server replies to a Test Setup Request, the Test Setup Response PDU is built exactly like the Test Setup Request PDU โœจ It SHALL keep the original values it received, with these exceptions:

  • The cmdRequest field is set to CHSR_CREQ_SETUPRSP, which says "hey, this is a response" ๐Ÿ’Œ
  • The cmdResponse field is set to an error code (starting at cmdResponse 2, Bad Protocol Version; see Section 12.3.5) that explains the rejection ๐Ÿ™ƒ If cmdResponse says it's a bad protocol version (CHSR_CRSP_BADVER), the protocolVer field also gets updated to show the current expected version ๐ŸŒˆ
  • The authUnixTime field is updated to the current system (wall-clock) time. Then, after the authDigest and checkSum fields are zeroed, the authDigest is recalculated and inserted ๐Ÿ•ฐ๏ธ If the optional checkSum field is being used, it gets calculated and inserted too ๐Ÿงฎ

The Setup Request/Response message PDU SHALL be organized as follows (here and in all following code figures, coded in the programming language C [C-Prog]) ๐ŸŽ€

//
// Control header for UDP payload of Setup Request/Response PDUs
//
struct controlHdrSR {
#define CHSR_ID 0xACE1
        uint16_t pduId; // PDU ID
#define PROTOCOL_VER 20
        uint16_t protocolVer; // Protocol version
        uint8_t mcIndex;      // Multi-connection index
        uint8_t mcCount;      // Multi-connection count
        uint16_t mcIdent;     // Multi-connection identifier
#define CHSR_CREQ_NONE     0
#define CHSR_CREQ_SETUPREQ 1   // Setup Request
#define CHSR_CREQ_SETUPRSP 2   // Setup Response
        uint8_t cmdRequest;    // Command Request
#define CHSR_CRSP_NONE     0   // (used with request)
#define CHSR_CRSP_ACKOK    1   // Acknowledgment
#define CHSR_CRSP_BADVER   2   // Bad version
#define CHSR_CRSP_BADJS    3   // Jumbo setting mismatch
#define CHSR_CRSP_AUTHNC   4   // Auth. not configured
#define CHSR_CRSP_AUTHREQ  5   // Auth. required
#define CHSR_CRSP_AUTHINV  6   // Auth. (mode) invalid
#define CHSR_CRSP_AUTHFAIL 7   // Auth. failure
#define CHSR_CRSP_AUTHTIME 8   // Auth. time invalid
#define CHSR_CRSP_NOMAXBW  9   // Max bandwidth required
#define CHSR_CRSP_CAPEXC   10  // Capacity exceeded
#define CHSR_CRSP_BADTMTU  11  // Trad. MTU mismatch
#define CHSR_CRSP_MCINVPAR 12  // Multi-conn. invalid params
#define CHSR_CRSP_CONNFAIL 13  // Conn. allocation failure
        uint8_t cmdResponse;   // Command Response
#define CHSR_USDIR_BIT 0x8000  // Upstream direction bit
        uint16_t maxBandwidth; // Required bandwidth in Mbps
        uint16_t testPort;     // Test port on server
#define CHSR_JUMBO_STATUS    0x01
#define CHSR_TRADITIONAL_MTU 0x02
        uint8_t modifierBitmap; // Modifier bitmap
        // ========== Integrity Verification ==========
#define AUTHMODE_1 1 // Mode 1: Authenticated Control
#define AUTHMODE_2 2 // Mode 2: Authenticated Control+Status
        uint8_t authMode;      // Authentication mode
        uint32_t authUnixTime; // Authentication timestamp
#define AUTH_DIGEST_LENGTH 32  // SHA-256 digest length
        uint8_t authDigest[AUTH_DIGEST_LENGTH];
        uint8_t keyId;         // Key ID in shared table
        uint8_t reservedAuth1; // (reserved for alignment)
        uint16_t checkSum;     // Header checksum
};
#define SHA256_KEY_LEN 32 // Authentication key length

Figure 3: Test Setup PDU ๐Ÿง

6.2.2. Test Setup Request Processing -- Acceptance ๐Ÿฅณ

okay now the good news ๐ŸŽ‰ If the server finds that the Setup Request matches its configuration and is otherwise acceptable, the server SHALL open a new connection to receive the Test Activation Request from the client ๐Ÿ’ซ It does this with a brand new UDP socket from the UDP ephemeral port range. This new socket is also used for the Load and Status PDUs that come later during testing, and its port number gets sent back to the client in the testPort field of the Test Setup Response ๐Ÿฆ‹ Then the server SHALL start a watchdog timer (so it can close the new connection if the client goes silent) and SHALL send the Test Setup Response back to the client ๐Ÿซถ The watchdog timer is set to the same value as on the client side (see Section 6).

When the server replies to the Test Setup Request message, the Test Setup Response PDU is built exactly like the Test Setup Request PDU and SHALL keep the original values it received, with these exceptions:

  • The cmdRequest field is set to CHSR_CREQ_SETUPRSP, indicating a response ๐Ÿ“ฌ
  • The cmdResponse field is set to CHSR_CRSP_ACKOK, indicating an acknowledgment โœ…
  • The testPort field is set to the ephemeral port number to be used for the client's Test Activation Request and all subsequent communication ๐ŸŒธ
  • The authUnixTime field is updated to the current system (wall-clock) time. Then, after the authDigest and checkSum fields are zeroed, the authDigest is recalculated and inserted ๐Ÿ•ฐ๏ธ If the optional checkSum field is being used, it is then calculated and inserted too ๐Ÿงฎ

Finally, the new UDP connection, with its new socket and port number, gets made ready, and the server waits for further communication there ๐Ÿ‘‘

Now here's the sneaky part ๐Ÿ‘€ To make sure the server's local firewall lets packets through for the new ephemeral port number, the server SHALL immediately send a Null Request with the matching values, including the source and destination IP addresses and port numbers. The source port SHALL be the new ephemeral port ๐Ÿ”ฅ This works even when the server's local firewall blocks open ranges of ephemeral ports ๐Ÿงฑ The packet is not expected to actually arrive at the client if the client-side firewall blocks unexpected traffic ๐Ÿ™ƒ If the Null Request does arrive at the client, that's confirmation that more exchanges can happen on the new port-pair (though this isn't strictly necessary) ๐Ÿ’œ If it's received, the client SHALL follow the Message Verification Procedure listed in Section 6.2, Paragraph 2. Note that there is no response to a Null Request ๐Ÿค

The UDP PDU format layout SHALL be as follows (big-endian AB) ๐Ÿ“

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|            pduId              |          protocolVer          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  cmdRequest   |  cmdResponse  |   reserved1   |   authMode    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         authUnixTime                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
.                     authDigest (32 octets)                    .
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     keyId     | reservedAuth1 |           checkSum            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 4: Null Request PDU Layout

The authentication and checkSum fields follow the same methodology as with the Setup Request and Response ๐Ÿ”

Here are the extra details for the Null Request fields ๐Ÿ“

  • pduId: IANA has assigned the hex value 0xDEAD (Section 12.3.1).
  • cmdRequest: Set to CHNR_CREQ_NULLREQ to indicate a Null Request message.
  • cmdResponse: Set to CHNR_CRSP_NONE.
  • authMode: Same as in Section 6.1.
  • authUnixTime: Same as in Section 6.1.
  • authDigest: Same as in Section 6.1.
  • keyId: Same as in Section 6.1.
  • reservedAuth1: Same as in Section 6.1.
  • checkSum: Same as in Section 6.1.

If a Test Activation Request doesn't come in from the client on the new ephemeral port number before the watchdog timer runs out, the server SHALL close the socket and free up the resources tied to it ๐Ÿงน

The Null Request message PDU SHALL be organized as follows ๐Ÿงฉ

//
// Control header for UDP payload of Null Request PDU
//
struct controlHdrNR {
#define CHNR_ID 0xDEAD
        uint16_t pduId;       // PDU ID
        uint16_t protocolVer; // Protocol version
#define CHNR_CREQ_NONE    0
#define CHNR_CREQ_NULLREQ 1  // Null Request
        uint8_t cmdRequest;  // Command Request
#define CHNR_CRSP_NONE 0     // (used with request)
        uint8_t cmdResponse; // Command Response
        uint8_t reserved1;   // (reserved for alignment)
        // ========== Integrity Verification ==========
        uint8_t authMode;      // Authentication mode
        uint32_t authUnixTime; // Authentication timestamp
        uint8_t authDigest[AUTH_DIGEST_LENGTH];
        uint8_t keyId;         // Key ID in shared table
        uint8_t reservedAuth1; // (reserved for alignment)
        uint16_t checkSum;     // Header checksum
};

Figure 5: Null Request PDU


6.3. Setup Response Processing at the Client ๐Ÿ’Œ

okay so when the client gets the Test Setup Response message back ๐Ÿ“ฌ, the very first thing it SHALL do is follow the Message Verification Procedure listed in Section 6.2, Paragraph 2 ๐Ÿ’….

then the client SHALL move on and check the other fields in the protocol ๐Ÿ”. it starts with the protocol version, then the PDU ID (to make sure it's the right type of message), then cmdRequest for the role of the message. that cmdRequest MUST be Test Setup Response, CHSR_CREQ_SETUPRSP, as shown in Figure 3 โœจ.

if the cmdResponse value shows an error (anything greater than CHSR_CRSP_ACKOK), the client SHALL display or report a relevant message to the user or management process and then exit ๐Ÿ™ˆ. if the client gets a Command Response code that isn't one of the codes defined, the client MUST terminate the connection and end the current Setup Request. bestie, that's a hard stop ๐Ÿ’€. if the Command Response code says success (CHSR_CRSP_ACKOK), the client SHALL compose a Test Activation Request with all the test parameters it wants, like the test direction, the test duration, and so on. more on that below ๐Ÿฆ‹.

7. Test Activation Request and Response ๐ŸŒธ

this section is split up by who is sending and processing: the client and the server, and then the client again ๐Ÿซถ.

7.1. Client Generates Test Activation Request ๐Ÿš€

after a successful setup exchange, the client SHALL compose and send the Test Activation Request to the UDP port number the server shared in the Test Setup Response. that's the new ephemeral port, NOT the standard UDPSTP port ๐Ÿ™…โ€โ™€๏ธ. (ok caps lock was not me, that's just the vibe. the word is "not" ๐Ÿ˜ญ)

here's how the UDP PDU format is laid out (big-endian AB) ๐Ÿงš:

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                          txInterval1                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                          udpPayload1                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                          burstSize1                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                          txInterval2                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                          udpPayload2                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                          burstSize2                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           udpAddon2                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |            pduId              |          protocolVer          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  cmdRequest   | cmdResponse   |           lowThresh           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         upperThresh           |           trialInt            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         testIntTime           |   reserved1   |   dscpEcn     |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         srIndexConf           |  useOwDelVar  |highSpeedDelta |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         slowAdjThresh         |         seqErrThresh          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | ignoreOooDup  |modifierBitmap |  rateAdjAlgo  |   reserved2   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   .                      srStruct (28 octets)                     .
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |        subIntPeriod           |          reserved3            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          reserved4            |   reserved5   |   authMode    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                         authUnixTime                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   .                     authDigest (32 octets)                    .
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     keyId     | reservedAuth1 |           checkSum            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 6: Test Activation PDU Layout ๐ŸŽ€

the fields get filled in from default values or command-line options ๐Ÿ› ๏ธ. the authentication and checkSum fields use the same method as the Setup Request and Response.

pduId: IANA has assigned the hex value 0xACE2 (Section 12.3.1) โœจ.

cmdRequest: set this to CHTA_CREQ_TESTACTUS for an upstream test activation, or to CHTA_CREQ_TESTACTDS for a downstream test activation. heads up, CHTA_CREQ_NONE stays unused ๐Ÿ™ˆ. see Section 12.3.6.

cmdResponse: three CHTA_CRSP_<Indication> values are defined. see Figure 7 ๐Ÿ’œ.

lowThresh: a two-octet field. this is the lower threshold on the range of Round-Trip Time (RTT) variation. the range is made up of values above the minimum RTT. see also Table 3 [TR-471] ๐Ÿ“Š.

upperThresh: a two-octet field. this is the upper threshold on the range of RTT variation. the range is made up of values above the minimum RTT. see also Table 3 of [TR-471] ๐Ÿ“ˆ.

trialInt: a two-octet field showing the Status Feedback / trial interval in ms. the test interval Delta_t gets split into a bunch of Sub-Intervals dt, and each Sub-Interval gets split again into trial intervals (see [TR-471]). it starts at 1 and keeps going up during one test interval (testIntTime) ๐Ÿ”ข.

testIntTime: a two-octet field. this is how long the test runs (downlink or uplink) with the search algorithm in use. it's the max time the search process can take, in seconds (see also TestInterval in Table 3 of [TR-471]) โฑ๏ธ.

dscpEcn: the Diffserv code point and ECN field. see also the DSCP field in [TR-471]. this spec doesn't give you an ECN-capable transport, so the sender SHALL set the ECN field to not_ECT ๐Ÿšซ. no ECN drama here, sis ๐Ÿ’….

srIndexConf: a two-octet field. this is the Configured Sending Rate Table index you're requesting, used in a Test Activation Request, for the fixed or starting sending rate. whether it's fixed or starting depends on whether CHTA_SRIDX_ISSTART is cleared or set. zero is a real, valid index, so the field SHALL be set to its maximum (CHTA_SRIDX_DEF) when you want the server's default behavior (starting with the chosen load rate adjustment algorithm at its minimum/zero index). this SHALL be equivalent to setting srIndexConf to zero and setting the CHTA_SRIDX_ISSTART bit ๐Ÿง .

useOwDelVar: a one-octet field, Boolean, default True. if it's False, use RTT (round-trip delay variation) in the load rate adjustment algorithm. if it's True, use EnableIPDV, which uses one-way delay variation for that algorithm. see EnableIPDV in Table 1 of [TR-471] ๐ŸŒŠ.

highSpeedDelta: a one-octet field. see Appendix A of [RFC9097] ๐ŸŽ๏ธ.

slowAdjThresh and seqErrThresh: two two-octet fields. see Appendix A of [RFC9097] ๐Ÿ“.

ignoreOooDup: a one-octet Boolean field for ignoring out-of-order duplicates. when True, only loss counts toward received packet sequence number errors. when False, loss, out-of-order, and duplicate totals all count as sequence number errors. the default is True (see also Table 3 of [TR-471]) ๐Ÿ™Œ.

modifierBitmap: a one-octet field. this document only assigns two bits in this bitmap, see Section 12.3.7 ๐ŸŽจ:

  • CHTA_SRIDX_ISSTART (0x01): treat srIndexConf as the starting sending rate for the load rate adjustment algorithm ๐Ÿšฆ.
  • CHTA_RAND_PAYLOAD (0x02): randomize the payload content beyond the Load PDU header ๐ŸŽฒ.

the other bit positions are left unassigned by this document ๐Ÿคทโ€โ™€๏ธ.

rateAdjAlgo: a one-octet field for the load rate adjustment algorithm being applied. see Section 12.3.8 โš™๏ธ.

srStruct: the Sending Rate structure ๐Ÿงฉ. the server uses it in a Test Activation Response for an upstream test to tell the client the (initial) Load PDU transmission parameters the client SHALL use. for a Test Activation Request or a downstream test, this structure SHALL be zeroed. there are two sets of periodic transmission parameters, which gives you dual independent transmitters for a high degree of rate granularity ๐Ÿ‘ฏ. the fields work like this:

  • txInterval1 and txInterval2: two four-octet fields for the load rate transmit interval in us (microseconds). a 100 us granularity is recommended for optimal rate selection ๐ŸŽฏ.
  • udpPayload1 and udpPayload2: two four-octet fields for the UDP payload at load rate, in bytes ๐Ÿ“ฆ.
  • burstSize1 and burstSize2: two four-octet fields for the burst size at load rate, as a dimensionless number of datagrams ๐Ÿ’ฅ.
  • udpAddon2: a four-octet field for the size of a single Load PDU sent at the end of the txInterval2 send sequence. this happens even when udpPayload2 or burstSize2 are zero and don't send anything on their own ๐Ÿ“.

subIntPeriod: a two-octet field for the Test Sub-Interval period in ms (see also Table 3 of [TR-471]). trials with subIntPeriod in a range of 100 to 10000 ms gave a default value of 1000 ms ๐Ÿ•ฐ๏ธ.

authMode, authUnixTime, authDigest, keyId, reservedAuth1, and checkSum: all the same as in Section 6.1 ๐Ÿ”.

the Test Activation Request/Response message PDU (plus the included Sending Rate structure) SHALL be organized as follows ๐Ÿ“‹:

7.2. Server Processes Test Activation Request and Generates Response โœจ

Once the server gets the Test Activation Request on the new connection, it gets to decide what happens next. It can accept, ignore, or tweak any of the test parameters ๐Ÿ’…. When the server replies, the Test Activation Response PDU is built exactly like the Request PDU, and it SHALL keep the original values it received unless they get explicitly coerced into a value the server is happy with ๐Ÿซถ.

When the server receives the Test Activation Request message, it SHALL first follow the Message Verification Procedure in Section 6.2, Paragraph 2 ๐Ÿ“‹.

7.2.1. Server Rejects or Modifies Request ๐Ÿšซ

When checking the Test Activation Request, the server MAY let the client pick its own fixed or starting send rate through srIndexConf ๐ŸŽฏ.

Or the server MAY put a cap on the fixed or starting send rate that a client can successfully ask for. If the client's Test Activation Request goes over the server's configured maximum, the server MUST either reject the request or coerce the value down to the configured maximum bit rate. Then it has to tell the client that maximum in the Test Activation Response. The client can absolutely choose to end the test if that's what makes sense ๐Ÿ™ƒ.

Other parameters where the server has the option to coerce the client into using values other than the ones in the Test Activation Request are (grouped by role):

  • Load rate adjustment algorithm: lowThresh, upperThresh, useOwDelayVar, highSpeedDelta, slowAdjThresh, seqErrThresh, highSpeedDelta, ignoreOooDup, and rateAdjAlgo
  • Test duration/intervals: trialInt, testIntTime, and subIntPeriod
  • Packet marking: dscpEcn

Coercion is basically a step toward running the test with the server-configured values. Even if the client has a favourite set, the server gives it a chance to run with different values than the ones it wanted ๐Ÿฅน. In these cases, the Command Response value SHALL be CHTA_CRSP_ACKOK.

Note that the server can also flat-out reject the request and send back an appropriate cmdResponse value. Right now the only one for that is CHTA_CRSP_BADPARAM, so see Section 12.3.9 ๐Ÿ“.

Whether that error response gets sent depends on the security mode of operation and the outcome of authDigest validation ๐Ÿ”.

If the Test Activation Request must be rejected (because the Command Response value is CHTA_CRSP_BADPARAM), then:

  • If the authDigest is valid, a Test Activation Response SHALL be sent back to the client with a matching Command Response value that explains why it got rejected.
  • If the authDigest is invalid, the Test Activation Request SHOULD fail silently. The exception is operations support, since server administrators are permitted to send an Activation Response for support operations and troubleshooting ๐Ÿ› ๏ธ.
Fail silently means the server just doesn't reply at all. Shh ๐Ÿคซ

The other situations where a server SHALL NOT communicate the appropriate Command Response code for an error condition (aka fail silently) are when:

  • the Test Activation Request PDU size is not equal to the struct controlHdrTA size shown in Figure 7,
  • the PDU ID is not 0xACE2 (Test Activation PDU), or
  • a directed attack has been detected.

In that case, the server will let Test Activation Requests terminate silently. Attack detection is beyond the scope of this specification ๐Ÿ›ก๏ธ.

7.2.2. Server Accepts Request and Generates Response โœ…

When the server sends the Test Activation Response, it SHALL set the cmdResponse field to CHTA_CRSP_ACKOK (see Section 12.3.9) ๐ŸŽ‰.

If the client has requested an upstream test, the server SHALL:

  • include the transmission parameters from the first row of the Sending Rate Table in the Sending Rate structure (if requested by setting srIndexConf to a value of CHTA_SRIDX_DEF), or
  • include the transmission parameters from the designated Configured Sending Rate Table index (srIndexConf) of the Sending Rate Table. If CHTA_SRIDX_ISSTART is set in modifierBitmap, this will be used as the starting rate for the load rate adjustment algorithm. Otherwise, it will be considered a fixed-rate test.

When generating the Test Activation Response (acceptance) for a downstream test, the server SHALL set all octets of the Sending Rate structure to zero 0๏ธโƒฃ.

If activation continues, the server prepares the new connection for an upstream or downstream test ๐Ÿ”€.

For an upstream test, the server SHALL prepare to use a single timer to send Status PDUs at the specified interval. For a downstream test, the server SHALL prepare to utilize dual timers to send Load PDUs based on:

  • the transmission parameters directly from the first row of the Sending Rate Table (if requested by srIndexConf having been set to CHTA_SRIDX_DEF), or
  • the transmission parameters from the designated Configured Sending Rate Table index (srIndexConf) of the Sending Rate Table. If CHTA_SRIDX_ISSTART is set in modifierBitmap, this will be used as the starting rate for the load rate adjustment algorithm. Otherwise, it will be considered a fixed-rate test.

The server SHALL then send the Test Activation Response back to the client, update the watchdog timer with a new timeout value, and set a test duration timer to eventually stop the test โฑ๏ธ.

7.3. Client Processes Test Activation Response ๐Ÿ“จ

When the client receives the Test Activation Response message, it SHALL first follow the Message Verification Procedure in Section 6.2, Paragraph 2 ๐Ÿ”.

After the client receives the (vetted) Test Activation Response, it first checks the Command Response value ๐Ÿ‘€.

If the client receives a Test Activation cmdResponse value that indicates an error, the client SHALL display or report a relevant message to the user or measurement system, and then exit ๐Ÿšช.

If the client receives a Test Activation cmdResponse value that is not equal to one of the codes defined in Section 12.3.9, the client MUST terminate the connection and terminate operation of the current setup procedure. No vibes allowed ๐Ÿ™…โ€โ™€๏ธ.

If the client receives a Test Activation Command Response value that indicates success (e.g., CHTA_CRSP_ACKOK; see Section 12.3.9), the client SHALL update its configuration to use any test parameters modified by the server. If the setup parameters coerced by the server are not acceptable to the client, the client ends the test ๐Ÿฅ€.

To finalize an accepted test activation, the client SHALL prepare its connection for either an upstream test with dual timers set to send Load PDUs (based on the starting transmission parameters sent by the server), or a downstream test with a single timer to send Status PDUs at the specified interval ๐Ÿ”.

Then, the client SHALL stop the test initiation timer and start a watchdog timer to detect if the server goes quiet ๐Ÿ‘‚.

The connection is now ready for testing ๐Ÿฆ‹.


8. Test Load Stream Transmission and Measurement Status Feedback Messages ๐ŸŒˆ

This section describes the Data phase of the protocol ๐Ÿ’œ. The roles of sender and receiver flip depending on whether the testing goes from server to client, or the other way around ๐Ÿ”„.

8.1. Load PDU and Roles โœจ

Okay so testing goes like this ๐Ÿ’… one endpoint sends Load PDUs based on the transmission parameters from the Sending Rate Table, and the other endpoint sends Status Feedback messages to report how the traffic is doing at the receiver ๐Ÿฆ‹ When the server is sending Status Feedback messages, they also carry the latest transmission parameters from the Sending Rate Table that the client SHALL use. ๐ŸŽ€

When a Load PDU arrives, the receiver SHALL do the following: ๐Ÿซถ

  1. Verify that the size of the message is greater than or equal to the struct loadHdr size shown in Figure 9.
  2. Validate the checksum for the Load PDU header portion of the total message (as described in Section 5.6) if the optional checkSum field is being utilized.
  3. Confirm that the PDU ID is 0xBEEF (Load PDU).

If any of the above checks fail, the message SHALL be considered invalid. ๐Ÿ’€

The watchdog timer at the receiver SHALL be reset each time a valid Load PDU is received (which includes verification of the checkSum, if in use). See non-graceful test stop in Section 9 for how each endpoint handles a watchdog timeout expiration. Note that the watchdog timer's whole purpose is to detect a connection failure or a massive congestion condition, and nothing more ๐Ÿ™ˆ

When the server is sending Load PDUs in the role of sender, it SHALL use the transmission parameters directly from the Sending Rate Table, via the index that is currently selected (which was indirectly based on the feedback in its received Status Feedback messages). ๐ŸŒธ

However, when the client is sending Load PDUs in the role of sender, it SHALL use the discrete transmission parameters that the server communicated in its periodic Status Feedback messages (and not reference a Sending Rate Table directly). This approach lets the server control the individual sending rates, as well as the algorithm used to decide when and how to adjust the rate. It's giving control freak, but in a useful way ๐Ÿ‘‘

The server uses a load rate adjustment algorithm that evaluates measurements taken locally at the Load PDU receiver. When the client is the receiver, that information gets communicated to the server via periodic Status Feedback messages. When the server is the receiver, the information is used directly (although it is also communicated to the client via its periodic Status Feedback messages). This approach is unique to this protocol ๐Ÿงš It lets you search for the maximum IP capacity and specify specific sender behaviors that other testing tools just don't have. Although the algorithm depends on the protocol, it is not part of the protocol per se โœจ

The default algorithm (B; see [Y.1540]) has three paths to its decision on the next sending rate:

  1. When there are no impairments present (no sequence errors and low delay variation), the sending rate increases.
  2. When there are low impairments present (no sequence errors but higher levels of delay variation), the same sending rate is maintained.
  3. When the impairment levels are above the thresholds set for this purpose and "congestion" is inferred, the sending rate decreases.

Algorithm B also has two modes for increasing and decreasing the sending rate:

  • A high-speed mode (fast) that reaches high sending rates quickly, but also backs off quickly when "congestion" is inferred from the measurements. Consecutive feedback intervals that have a supra-threshold count of sequence number anomalies and/or contain an upper delay variation threshold exception in all of the consecutive intervals are sufficient to declare "congestion" within a test. The threshold of consecutive feedback intervals SHALL be configurable, with a default of 3 intervals. ๐ŸŽ๏ธ
  • A single-step (slow) mode where all rate adjustments use the minimum increase or decrease of one step in the Sending Rate Table. The single-step mode continues after the first inference of "congestion" from measured impairments. ๐Ÿข

An OPTIONAL load rate adjustment algorithm (designated C) has been defined in [TR-471]. Algorithm C operates and has modes similar to B, but C uses multiplicative increases in the fast mode to reach the gigabit range quickly, and it provides the option to retry the fast mode during a test. That retry is what improves measurement accuracy in dynamic or error-prone access, such as radio access ๐Ÿ“ก๐ŸŒˆ

On the other hand, the test configuration MAY use a fixed sending rate requested by the client, using the field srIndexConf. The client MAY communicate the desired fixed rate in its Test Activation Request. ๐ŸŽฏ

The UDP PDU format layout SHALL be as follows (big-endian AB):

0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

pduIdtestActionrxStopped

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

lpduSeqNo

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

udpPayloadspduSeqErr

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

spduTime_sec

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

spduTime_nsec

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

lpduTime_sec

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

lpduTime_nsec

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

rttRespDelaycheckSum

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ . Payload Content... . +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 8: Load PDU Layout

Here's the field-by-field breakdown ๐Ÿ“

  • pduId: IANA has assigned the hex value 0xBEEF (Section 12.3.1).
  • testAction: A one-octet field designating the current test action as either TEST_ACT_TEST (testing in progress), TEST_ACT_STOP1 (first phase of graceful termination, used locally by the server), or TEST_ACT_STOP2 (second phase of graceful termination, sent by the server and reciprocated by the client). See Section 9 for additional information on test termination. ๐Ÿ›‘
  • rxStopped: A one-octet field. It's a Boolean, 0 or 1, used to tell the remote endpoint that local receive traffic (either Load or Status PDUs) has stopped. All outgoing Load or Status PDUs SHALL continue to assert this indication until traffic is received again, or the test is terminated. The time threshold to trigger this condition is expected to be a reasonable fraction of the watchdog timeout (a default of one second is recommended). โฑ๏ธ
  • lpduSeqNo: A four-octet field indicating the Load PDU sequence number (starting at 1). It's used to determine loss, out-of-order, and duplicate totals. ๐Ÿ”ข
  • udpPayload: A two-octet field indicating the total payload size of the UDP datagram, including the Load PDU message header and payload content (i.e., what the UDP socket read function would return). This field lets the Load PDU receiver keep accurate receive statistics when it uses receive truncation (only requesting the Load PDU message header octets from the protocol stack). ๐Ÿ“ฆ
  • spduSeqErr: A two-octet field indicating the Status PDU loss count, as seen by the Load PDU sender. This is determined by the Status PDU sequence number (spduSeqNo) in the most recently received Status PDU. It's used to tell the Load PDU receiver that return traffic (in the unloaded direction) is being lost. ๐Ÿ˜ฌ
  • spduTime_sec/spduTime_nsec: Two four-octet fields containing a copy of the most recent spduTime_sec/spduTime_nsec from the last Status PDU received. They're used for RTT measurements made by the Load PDU receiver. ๐Ÿ•ฐ๏ธ
  • lpduTime_sec/lpduTime_nsec: Two four-octet fields containing the local send time of the Load PDU. They're used for one-way delay variation measurements made by the Load PDU receiver. ๐Ÿ’ซ
  • rttRespDelay: A two-octet field indicating the RTT response delay, used to "adjust" the raw RTT. On the Load PDU sender, it is the number of ms from reception of the most recent Status PDU (when the latest spduTime_sec/spduTime_nsec was obtained) to the transmission of the Load PDU (where the previously obtained spduTime_sec/spduTime_nsec is returned). When the Load PDU receiver calculates RTT by subtracting the copied Status PDU send time (in the Load PDU) from the local Load PDU receive time, this value is subtracted from the raw RTT to correct for any response delay due to Load PDU scheduling. ๐Ÿงฎ
  • checkSum: An optional checksum of only the Load PDU header (see Section 5.6 for guidance). The checksum does not cover the payload content. The calculation is done as the very last step of building the PDU header, with the checkSum field set to zero. โœ…
  • Payload Content: All zeroes, all ones, or a pseudorandom binary sequence. ๐ŸŒˆ

The Load PDU SHALL be organized as follows (followed by any payload content):

//
// Load header for UDP payload of Load PDUs
//
struct loadHdr {
#define LOAD_ID 0xBEEF
        uint16_t pduId;  // PDU ID
#define TEST_ACT_TEST  0 // Test active
#define TEST_ACT_STOP1 1 // Stop indication used locally by server
#define TEST_ACT_STOP2 2 // Stop indication exchanged with client
        uint8_t testAction;  // Test action
        uint8_t rxStopped;   // Receive traffic stopped (BOOL)
        uint32_t lpduSeqNo;  // Load PDU sequence number
        uint16_t udpPayload; // UDP payload (bytes)
        uint16_t spduSeqErr; // Status PDU sequence error count
        uint32_t spduTime_sec;  // Send time in last rx'd status PDU
        uint32_t spduTime_nsec; // Send time in last rx'd status PDU
        uint32_t lpduTime_sec;  // Send time of this Load PDU
        uint32_t lpduTime_nsec; // Send time of this Load PDU
        uint16_t rttRespDelay;  // Response delay for RTT (ms)
        uint16_t checkSum;      // Header checksum
};

Figure 9: Load PDU

8.2. Status PDU ๐Ÿ“Š

The Load PDU receiver SHALL send a Status PDU to the sender during a test at the configured feedback interval, after at least one Load PDU has been received (when there is something to report on). ๐Ÿ™Œ In test scenarios with long delays between client and server, it's possible for the Status PDU send timer to fire before the first Load PDU arrives. In these cases, the Status PDU SHALL NOT be sent. ๐Ÿซ 

When the Load PDU sender receives a Status PDU message, it SHALL first follow the Message Verification Procedure listed in Section 6.2, Paragraph 2. ๐Ÿ”

The watchdog timer at the Load PDU sender SHALL be reset each time a valid Status PDU is received (which includes verification of the checkSum and/or authDigest, if in use). See non-graceful test stop in Section 9 for how each endpoint handles a watchdog timeout expiration. ๐Ÿ’–

The UDP PDU format layout SHALL be as follows (big-endian AB):

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                          rxDatagrams                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                            rxBytes                            |
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           deltaTime                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           seqErrLoss                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           seqErrOoo                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           seqErrDup                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                          delayVarMin                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                          delayVarMax                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                          delayVarSum                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                          delayVarCnt                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                         rttVarMinimum                         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                         rttVarMaximum                         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           accumTime                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |             pduId             |   testAction  |   rxStopped   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           spduSeqNo                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   .                      srStruct (28 octets)                     .
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                          subIntSeqNo                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   .                      sisSav (56 octets)                       .
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           seqErrLoss                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           seqErrOoo                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           seqErrDup                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                         clockDeltaMin                         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                          delayVarMin                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                          delayVarMax                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                          delayVarSum                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                          delayVarCnt                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                          rttMinimum                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                         rttVarSample                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  delayMinUpd  |   reserved1   |          reserved2            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                          tiDeltaTime                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                         tiRxDatagrams                         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           tiRxBytes                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                         spduTime_sec                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                         spduTime_nsec                         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          reserved3            |   reserved4   |   authMode    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                         authUnixTime                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   .                     authDigest (32 octets)                    .
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |     keyId     | reservedAuth1 |           checkSum            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                        Figure 10: Status PDU Layout

Note that the Sending Rate structure is defined in Section 7. ๐Ÿฆ‹

The primary role of the Status Feedback message is to communicate the traffic conditions at the Load PDU receiver to the Load PDU sender ๐Ÿ’– bestie, this is the whole point! While the Sub-Interval statistics saved (sisSav) structure covers the most recently saved (completed) Sub-Interval, similar fields directly in the Status PDU itself cover the most recent trial interval (the time period between Status Feedback messages, completed by this Status PDU). Both sets of statistics SHALL always be populated by the Load PDU receiver, regardless of role (client or server) โœจ

Details on the Status PDU measurement fields are provided in [RFC9097]. The authentication and checkSum fields follow the same methodology as with the Setup Request and Response. Additional information regarding fields not defined previously is as follows ๐ŸŽ€

  • pduId: IANA has assigned the hex value 0xFEED (Section 12.3.1). Iconic ๐Ÿ‘‘
  • spduSeqNo: A four-octet field containing the Status PDU sequence number (starting at 1). Used by the Load PDU sender to detect Status PDU loss (in the unloaded direction). The loss count is communicated back to the Load PDU receiver via spduSeqErr in subsequent Load PDUs.
  • subIntSeqNo: A four-octet field containing the Sub-Interval sequence number (starting at 1) that corresponds to the statistics provided in sisSav, for the last saved (completed) Sub-Interval.
  • sisSav: Sub-Interval statistics saved (completed) for the most recent Sub-Interval (as designated by the subIntSeqNo). These consist of the following fields ๐Ÿงš
    • rxDatagrams: A four-octet field Sub-Interval indicating the number of received datagrams during the Sub-Interval.
    • rxBytes: An eight-octet field indicating the Sub-Interval byte count (eight octets chosen to prevent overflow at high speeds). The eight octets are giving main character energy ๐ŸŒˆ
    • deltaTime: A four-octet field indicating the exact duration of the Sub-Interval in us. Used to calculate the received traffic rate together with rxBytes.
    • seqErrLoss/seqErrOoo/seqErrDup: Three four-octet fields populated by the loss, out-of-order, and duplicate totals. Available for both the Sub-Interval and trial interval; it is a breakout of the SeqErrors count in Table 3 of [TR-471]. seqErrOoo and seqErrDup are realized by comparing sequence numbers. A lookback list of the last n sequence numbers received is used as the basis. Each Load PDU sequence number is checked against this lookback. The number n may depend on the implementation and on typical characteristics of environments, where UDPSTP is deployed (like mobile networks or Wi-Fi). Currently, a default sequence number interval of n=32 has been chosen. Specifically for seqErrOoo, each successively received higher seqno sets the next-expected seqno to seqno+1, and anything below that is considered out of order (i.e., delayed). For example, given the sequence below, reception of 96, 97, 98, and 99 would not increment the next-expected seqno and would all be considered out of order ๐Ÿ™ˆ
93, 94, 95, 100, 96, 97, 101, 98, 99, 102, 103, ...
  • delayVarMin/delayVarMax/delayVarSum/delayVarCnt: Four four-octet fields populated by the one-way delay variation measurements of all received Load PDUs (where avg = sum/cnt). For each Load PDU received, the send time (lpduTime_sec/lpduTime_nsec) is subtracted from the local receive time, which is then normalized by subtracting the current clockDeltaMin. Available for both the Sub-Interval and trial interval.
  • rttVarMinimum/rttVarMaximum (in sisSav): Two four-octet fields populated by the minimum and maximum RTT delay variation (rttVarSample) in the Sub-Interval designated by the subIntSeqNo.
  • accumTime: The accumulated time of the test in ms, based on the duration of each Sub-Interval. Equivalent to the sum of each deltaTime (although in ms) sent in each Status PDU during the test.
  • clockDeltaMin: A four-octet field indicating the minimum clock delta (difference) since the beginning of the test. Obtained by subtracting the send time of each Load PDU (lpduTime_sec/lpduTime_nsec) from the local time that it was received. This value is initialized with the first Load PDU received and is updated with each subsequent one to maintain a current (and continuously updated) minimum. If the endpoint clocks are sufficiently synchronized, this will be the minimum one-way delay in ms. Otherwise, this value may be negative but still valid for one-way delay variation measurements for the default test duration (default is 10 seconds). If the test duration is extended to a range of minutes, where significant clock drift can occur, synchronized (or at least well-disciplined) clocks may be required. Clock drama, but make it iconic ๐Ÿ•ฐ๏ธ๐Ÿ’…
  • rttMinimum (in Status PDU): A four-octet field indicating the minimum "adjusted" RTT measured since the beginning of the test. See rttRespDelay (Section 8.1) regarding "adjusted" measurements. RTT is obtained by subtracting the copied spduTime_sec/spduTime_nsec in the received Load PDU from the local time at which it was received. This minimum SHALL be kept current (and continuously updated) via each Load PDU received with an updated spduTime_sec/spduTime_nsec. This value MUST be positive. Before an initial value can be established, and because zero is itself valid, it SHALL be set to STATUS_NODEL when communicated in the Status PDU ๐ŸŒธ

rttVarSample: this one's the most recent "adjusted" RTT delay variation measurement, and it's a four-octet field ๐Ÿงš. See rttRespDelay (Section 8.1) for how "adjusted" measurements work. To get the RTT delay variation, you subtract the current, continuously updated "adjusted" RTT minimum (sent as rttMinimum in the Status PDU) from each "adjusted" RTT measurement. Each RTT measurement is itself the local time the Load PDU arrived minus the spduTime_sec/spduTime_nsec it copied back ๐Ÿ’…. One-way delay variation gets measured for every Load PDU received, but RTT delay variation only gets sampled when a Status PDU goes out and the very next Load PDU comes back with the matching updated spduTime_sec/spduTime_nsec ๐Ÿฆ‹. If there's no new value (maybe a packet got lost ๐Ÿ˜ญ), the field SHALL be set to STATUS_NODEL when it's sent in the Status PDU. Zero is a real, valid value, so you can't use it as the "no data" signal ๐Ÿ™ˆ.

delayMinUpd: a one-octet Boolean (0 or 1) that tells you whether the clockDeltaMin and/or rttMinimum (in the Status PDU), as measured by the Load PDU receiver, got updated ๐ŸŒธ.

tiDeltaTime/tiRxDatagrams/tiRxBytes: three four-octet fields holding the trial interval time in microseconds plus the received datagram and byte counts. They're how you work out the received traffic rate for the trial interval ๐Ÿ“Šโœจ.

spduTime_sec/spduTime_nsec: two four-octet fields with the local transmit time of the Status PDU. They're supposed to get copied into spduTime_sec/spduTime_nsec in later Load PDUs once the Load PDU sender receives them, and that's what RTT measurements run on ๐Ÿซถ.

authMode: same as Section 6.1 ๐Ÿ”.

authUnixTime: same as Section 6.1 ๐Ÿ”.

authDigest: same as Section 6.1 ๐Ÿ”.

keyId: same as Section 6.1 ๐Ÿ”.

reservedAuth1: same as Section 6.1 ๐Ÿ”.

checkSum: same as Section 6.1 ๐Ÿ”.

Okay so the Status Feedback message PDU (plus the Sub-Interval statistics structure it includes) SHALL be laid out like this, no exceptions ๐Ÿ’–:

//
// Sub-Interval statistics structure for received traffic information
//
#pragma pack(push, 1)
struct subIntStats {
        uint32_t rxDatagrams;   // Received datagrams
        uint64_t rxBytes;       // Received bytes (64 bits)
        uint32_t deltaTime;     // Time delta (us)
        uint32_t seqErrLoss;    // Loss sum
        uint32_t seqErrOoo;     // Out-of-order sum
        uint32_t seqErrDup;     // Duplicate sum
        uint32_t delayVarMin;   // Delay variation minimum (ms)
        uint32_t delayVarMax;   // Delay variation maximum (ms)
        uint32_t delayVarSum;   // Delay variation sum (ms)
        uint32_t delayVarCnt;   // Delay variation count
        uint32_t rttVarMinimum; // Minimum RTT variation (ms)
        uint32_t rttVarMaximum; // Maximum RTT variation (ms)
        uint32_t accumTime;     // Accumulated time (ms)
};
#pragma pack(pop)
//
// Status Feedback header for UDP payload of status PDUs
//
struct statusHdr {
#define STATUS_ID 0xFEED
        uint16_t pduId;     // PDU ID
        uint8_t testAction; // Test action
        uint8_t rxStopped;  // Receive traffic stopped (BOOL)
        uint32_t spduSeqNo; // Status PDU sequence number
        struct sendingRate srStruct; // Sending Rate structure
        uint32_t subIntSeqNo;        // Sub-Interval sequence number
        struct subIntStats sisSav;   // Sub-Interval stats saved
        uint32_t seqErrLoss;    // Loss sum
        uint32_t seqErrOoo;     // Out-of-order sum
        uint32_t seqErrDup;     // Duplicate sum
        uint32_t clockDeltaMin; // Clock delta minimum (ms)
        uint32_t delayVarMin;   // Delay variation minimum (ms)
        uint32_t delayVarMax;   // Delay variation maximum (ms)
        uint32_t delayVarSum;   // Delay variation sum (ms)
        uint32_t delayVarCnt;   // Delay variation count
#define STATUS_NODEL UINT32_MAX // No delay data/value
        uint32_t rttMinimum;    // Min round-trip time sampled (ms)
        uint32_t rttVarSample;  // Last round-trip time sample (ms)
        uint8_t delayMinUpd;    // Delay minimum(s) updated (BOOL)
        uint8_t reserved1;      // (reserved for alignment)
        uint16_t reserved2;     // (reserved for alignment)
        uint32_t tiDeltaTime;   // Trial interval delta time (us)
        uint32_t tiRxDatagrams; // Trial interval receive datagrams
        uint32_t tiRxBytes;     // Trial interval receive bytes
        uint32_t spduTime_sec;  // Send time of this status PDU
        uint32_t spduTime_nsec; // Send time of this status PDU
        uint16_t reserved3;     // (reserved for alignment)
        uint8_t reserved4;      // (reserved for alignment)
        // ========== Integrity Verification ==========
        uint8_t authMode;      // Authentication mode
        uint32_t authUnixTime; // Authentication timestamp
        uint8_t authDigest[AUTH_DIGEST_LENGTH];
        uint8_t keyId;         // Key ID in shared table
        uint8_t reservedAuth1; // (reserved for alignment)
        uint16_t checkSum;     // Header checksum
};

Figure 11: Status PDU


9. Stopping a Test ๐Ÿ›‘

When the test duration timer (testIntTime) on the server runs out, the server SHALL set the local connection test action to TEST_ACT_STOP1 (phase 1 of graceful termination) ๐ŸŒˆ. This is just a non-reversible state that's waiting on the next message(s) from the server. Until then, any Load or Status PDUs that come in still get processed normally ๐Ÿ’œ.

Once the next Load or Status PDUs go out, the server SHALL set the local connection test action to TEST_ACT_STOP2 (phase 2 of graceful termination) and mark every outgoing PDU with a testAction value of TEST_ACT_STOP2 โœจ. While it's in this state, the server MAY reduce any Load PDU bursts to a size of one ๐Ÿ“.

When the client gets a Load or Status PDU carrying the TEST_ACT_STOP2 indication, it SHALL finalize testing, display the test results, and mark its local connection with a test action of TEST_ACT_STOP2 (so any PDUs that show up afterward can be ignored) ๐Ÿ‘‘.

Once the client's connection is set to TEST_ACT_STOP2, the very next expiry of a send timer, for either a Load or a Status PDU, SHALL result in that PDU and every PDU after it going out with a testAction value of TEST_ACT_STOP2 (that's the confirmation for the server) ๐Ÿซถ. While it's in this state, the client MAY reduce any Load PDU bursts to a size of one. The client SHALL then schedule an immediate end time for the connection ๐Ÿฆ‹.

When the server receives the TEST_ACT_STOP2 confirmation in a Load or Status PDU, the server SHALL schedule an immediate end time for the connection, which closes the socket and deallocates the associated resources ๐Ÿ’…. The TEST_ACT_STOP2 exchange is what makes the test a graceful termination ๐ŸŽ€.

If the test stops non-gracefully because of a path failure, the watchdog timeouts at each endpoint will expire (sometimes one endpoint goes first) ๐Ÿ˜ญ. Notifications SHALL be made in logs, STDOUT, and/or formatted output, and the endpoint SHALL schedule an immediate end time for the connection ๐Ÿ’€.

If an attacker clears the TEST_ACT_STOP2 indication ๐Ÿ˜ˆ, the configured test duration timer (testIntTime) at the server and client SHALL take precedence, and the endpoint SHALL schedule an immediate end time for the connection. Security folks, this one's serious, so don't skip it ๐Ÿšจ.

10. Operational Considerations for the Measurement Method ๐Ÿงช

The method needs two cooperating hosts, one playing Src (test packet sender) and the other Dst (receiver), with a measured path and a return path between them ๐Ÿ“.

The nominal duration of a measurement interval at the Destination, parameter testIntTime, MUST be constrained in a production network. This is an active test method, and it will likely cause congestion on the Src to Dst host path during a test ๐Ÿšฆ.

It is RECOMMENDED to put test endpoints as close to the intended measured link(s) as practical. The testing operator MUST set a value for the MaxHops parameter, based on the expected path length. This parameter keeps measurement traffic from wandering too far past the intended path ๐Ÿงญ.

It's obviously counterproductive to run more than one independent, concurrent test (no matter how many flows are in the test stream) when you're trying to measure the maximum capacity of a single path ๐Ÿ™ƒ. The number of concurrent, independent tests of a path SHALL be limited to one โ˜๏ธ.

The load rate adjustment algorithm only helps determine the Maximum IP-Layer Capacity for an infrequent, diagnostic, short-term measurement ๐ŸŽฏ. It is RECOMMENDED to pause non-measurement traffic that shares a subscriber's dedicated resources while testing. Measurements may not be accurate, and competing elastic traffic may take a huge hit ๐Ÿ“‰.

See Section 8 of [RFC9097] for a discussion of the Method of Measurement that goes beyond the purely operational stuff ๐Ÿ“š.

10.1. Notes on Interface Measurements ๐Ÿ”

Some extra measurements can be useful in specific situations. For example, interface byte counters measured by a client at a residential gateway are possible when the client application can reach an interface that sees all traffic to and from a service subscriber's location ๐Ÿ . Adding a byte counter at the client for the download or upload direction could measure total traffic and maybe detect when non-test traffic is present (and eating capacity) ๐Ÿ‘€. The client might not have the CPU cycles to count the interface traffic and IP-Layer Capacity at the same time, so this kind of diagnostic measurement may not be possible ๐Ÿ’ป.

11. Security Considerations ๐Ÿ”โœจ

Okay bestie, this is the serious part, so let's get into it ๐Ÿ’… Active metrics and measurements have a long history of security considerations. Any active measurement of live paths comes with its own security considerations, and those apply here too. See [RFC4656] and [RFC5357].

When we think about the privacy of users who activate measurements as a service, or users whose traffic gets measured, the sensitive info that potential observers can see is way reduced with the active techniques in this scope ๐Ÿฆ‹ Passive observation of user traffic for measurement purposes raises a lot of privacy issues. See the privacy considerations described in the LMAP Framework [RFC7594], which covers both active and passive techniques.

Below are some new considerations for capacity measurement as described in this document ๐ŸŒธ

  1. Cooperating client and server hosts and agreements to test the path between the hosts are REQUIRED. Hosts perform in either the server or the client role. One way to make sure a cooperative agreement is in place, when the optional Authorization mode is used, is the authDigest field, plus the known identity tied to the shared key that creates the authDigest field through the KDF. Other means are also possible, such as access control lists at the server.
  2. It is REQUIRED to have a user client-initiated setup handshake between cooperating hosts. This lets firewalls control inbound unsolicited UDP traffic headed to either a control port or ephemeral ports that only get created as needed. Firewalls protecting each host can keep doing their jobs normally ๐Ÿ›ก๏ธ
  3. Client-server authentication and integrity protection for feedback messages that convey measurements is RECOMMENDED. Different host limitations and testing circumstances call for different modes of operation, which are described in Section 5 ๐ŸŽ€
  4. Hosts MUST limit the number of simultaneous tests to avoid resource exhaustion and inaccurate results. Main character energy is cute, but not when it crashes the host ๐Ÿ’€
  5. Senders MUST be rate-limited. This can be done with a pre-built table that defines all the offered sending rates that will be supported. The default and optional load rate adjustment algorithm results in a "ramp up" from the lowest rate in the table. Optionally, the server could use the maxBandwidth field (and the CHSR_USDIR_BIT bit) in the Setup Request from the client to limit the maximum it will try to reach ๐Ÿšฆ
  6. Service subscribers with limited data volumes who run extensive capacity testing might feel the effects of Service Provider controls on their service. Testing with the Service Provider's measurement hosts SHOULD be limited in frequency and/or overall volume of test traffic (for example, the range of test interval duration values should be limited) ๐Ÿ“

One specific attack that has been recognized is an on-path attack on the testAction field, where the attacker sets or clears the STOP indication. Setting the indication in successive packets ends the test early. There's no threat to the Internet here, but it's seriously annoying for the testers ๐Ÿ˜ญ If an attacker clears the STOP indication, the mitigation relies on the test duration being known at the client and server, because these hosts stop all traffic once the specified test duration is complete.

Authentication methods and requirements keep evolving ๐ŸŒˆ Alternate authentication modes provide algorithm agility by defining a new mode, whose support is indicated by assigning a suitable "Test Setup PDU Authentication Mode" registry value (see Section 12.3.4).


12. IANA Considerations ๐Ÿ“‹๐Ÿ’–

Per this document, IANA has assigned a UDP port number for the Test Setup exchange in the Control phase of protocol operation, and has created a new registry group for the UDPSTP ๐Ÿงš

12.1. New User Port Number Assignment

IANA has registered the following service name in the "Service Name and Transport Protocol Port Number Registry":

Service Name:  udpstp
Port Number:  24601
Transport Protocol:  udp
Description:  UDP-based IP-Layer Capacity and Performance Measurement
   protocol
Assignee:  IESG <iesg@ietf.org>
Contact:  IETF Chair <chair@ietf.org>
Reference:  RFC 9946

The protocol uses IP-Layer Unicast. A single port number was assigned to help configure firewalls and other port-based systems for access control before dynamic ports get negotiated between client and server ๐Ÿ”ฅ

12.2. New KeyTable KDF

IANA has added the following KDF entry to the "KeyTable KDFs" registry (see <https://www.iana.org/assignments/keytable>) โœจ

     +==============+=============================+===========+
     | KDF          | Description                 | Reference |
     +==============+=============================+===========+
     | HMAC-SHA-256 | HMAC using the SHA-256 hash | [RFC6234] |
     +--------------+-----------------------------+-----------+

                  Table 1: KeyTable KDFs Registry

12.3. New UDPSTP Registry Group

IANA has created the registries in the subsections that follow, all under a new registry group called "UDP Speed Test Protocol (UDPSTP)" ๐ŸŒธ

IANA has added the following note under the "UDP Speed Test Protocol (UDPSTP)" registry group:

Note: Values reserved for Experimental Use are not expected to be used on the Internet but are expected to be used for experiments that are confined to closed environments.

12.3.1. PDU Identifier Registry

IANA has created the "PDU Identifier" registry under the "UDP Speed Test Protocol (UDPSTP)" registry group. Every UDPSTP PDU contains a two-octet pduId field that identifies the role and format of the PDU that follows. The code points in this registry are allocated according to the registration procedures [RFC8126] described in the table below ๐Ÿฆ‹

             +===============+=========================+
             | Range         | Registration Procedures |
             +===============+=========================+
             | 0x0000        | Reserved                |
             +---------------+-------------------------+
             | 0x0001-0x7F00 | Specification Required  |
             +---------------+-------------------------+
             | 0x7F01-0x7FE0 | Experimental Use        |
             +---------------+-------------------------+
             | 0x7FE1-0x7FFF | Private Use             |
             +---------------+-------------------------+
             | 0x8000-0xFFFE | IETF Review             |
             +---------------+-------------------------+
             | 0xFFFF        | Reserved                |
             +---------------+-------------------------+

                 Table 2: Registration Procedures for
                     the PDU Identifier Registry

IANA has assigned values in the "PDU Identifier" registry as follows ๐ŸŽ€

    +===============+===============================+===========+
    | Value         | Description                   | Reference |
    +===============+===============================+===========+
    | 0x0000        | Reserved                      | RFC 9946  |
    +---------------+-------------------------------+-----------+
    | 0x7F01-0x7FE0 | Reserved for Experimental Use | RFC 9946  |
    +---------------+-------------------------------+-----------+
    | 0x7FE1-0x7FFF | Reserved for Private Use      | RFC 9946  |
    +---------------+-------------------------------+-----------+
    | 0xACE1        | Test Setup PDU                | RFC 9946  |
    +---------------+-------------------------------+-----------+
    | 0xACE2        | Test Activation PDU           | RFC 9946  |
    +---------------+-------------------------------+-----------+
    | 0xBEEF        | Load PDU                      | RFC 9946  |
    +---------------+-------------------------------+-----------+
    | 0xDEAD        | Null PDU                      | RFC 9946  |
    +---------------+-------------------------------+-----------+
    | 0xFEED        | Status Feedback PDU           | RFC 9946  |
    +---------------+-------------------------------+-----------+
    | 0xFFFF        | Reserved                      | RFC 9946  |
    +---------------+-------------------------------+-----------+

        Table 3: Initial Values of the PDU Identifier Registry

12.3.2. Protocol Version Registry

IANA has created the "Protocol Version" registry under the "UDP Speed Test Protocol (UDPSTP)" registry group. UDPSTP Test Setup Request, Test Setup Response, and Test Activation Request PDUs contain a two-octet protocolVer field that identifies the version of the protocol in use. The code points in this registry are allocated according to the registration procedures [RFC8126] described in the table below ๐Ÿ’œ

              +=============+=========================+
              | Range       | Registration Procedures |
              +=============+=========================+
              | 0-19        | Reserved                |
              +-------------+-------------------------+
              | 20-40960    | IETF Review             |
              +-------------+-------------------------+
              | 40961-53248 | Specification Required  |
              +-------------+-------------------------+
              | 53249-65534 | Experimental Use        |
              +-------------+-------------------------+
              | 65535       | Reserved                |
              +-------------+-------------------------+

                 Table 4: Registration Procedures for
                    the Protocol Version Registry

IANA has assigned decimal value 20 in the "Protocol Version" registry as follows ๐ŸŒˆ

                        +=======+===========+
                        | Value | Reference |
                        +=======+===========+
                        | 20    | RFC 9946  |
                        +-------+-----------+

                        Table 5: Initial Value
                           of the Protocol
                           Version Registry

12.3.3. Test Setup PDU Modifier Bitmap Registry

okay so bestie, this registry is where IANA keeps the modifier bitmap values for the Test Setup PDU ๐Ÿฆ‹โœจ the modifierBitmap field gets its values from here, and new ones are handed out using the registration procedures from [RFC8126] described in Table 6. no cap, the whole thing is giving organized ๐Ÿ’…

              +===================+=========================+
              | Range             | Registration Procedures |
              +===================+=========================+
              | 00000000-01111111 | IETF Review             |
              +-------------------+-------------------------+
              | 10000000          | Reserved                |
              +-------------------+-------------------------+

                  Table 6: Registration Procedures for the
                  Test Setup PDU Modifier Bitmap Registry

so the lower half of the range (00000000-01111111) is IETF Review territory ๐ŸŒธ, and 10000000 is Reserved ๐Ÿ™ˆ not me reserving the top bit like that lowkey iconic

IANA has assigned bitmap values in the "Test Setup PDU Modifier Bitmap" registry like this ๐Ÿ“

        +=======+===============================+===========+
        | Value | Description                   | Reference |
        +=======+===============================+===========+
        | 0x00  | No modifications              | RFC 9946  |
        +-------+-------------------------------+-----------+
        | 0x01  | Allow Jumbo datagram sizes    | RFC 9946  |
        |       | above sending rates of 1 Gbps |           |
        +-------+-------------------------------+-----------+
        | 0x02  | Use Traditional MTU (1500     | RFC 9946  |
        |       | bytes with an IP header)      |           |
        +-------+-------------------------------+-----------+
the three starter values are 0x00 (no modifications), 0x01 (allow Jumbo datagram sizes above sending rates of 1 Gbps), and 0x02 (use Traditional MTU, 1500 bytes with an IP header). all three point back to RFC 9946 ๐ŸŽ€
            Table 7: Initial Values of the Test Setup PDU
                       Modifier Bitmap Registry

12.3.4. Test Setup PDU Authentication Mode Registry

omg okay this one is about the authMode field in the Test Setup PDU ๐Ÿงš same deal, new code points get allocated through [RFC8126] following Table 8 ๐Ÿ’–

                   +========+=========================+
                   | Range  | Registration Procedures |
                   +========+=========================+
                   | 0-59   | IETF Review             |
                   +--------+-------------------------+
                   | 60-63  | Experimental Use        |
                   +--------+-------------------------+
                   | 64-255 | Reserved                |
                   +--------+-------------------------+

                     Table 8: Registration Procedures
                          for the Test Setup PDU
                       Authentication Mode Registry

so 0-59 goes through IETF Review, 60-63 is Experimental Use ๐ŸŒˆ, and 64-255 is Reserved (hands off, bestie ๐Ÿ™…)

IANA has assigned decimal values in the "Test Setup PDU Authentication Mode" registry like so ๐Ÿซถ

   +=======+===============================================+===========+
   | Value | Description                                   | Reference |
   +=======+===============================================+===========+
   | 0     | Not used                                      | RFC 9946  |
   +-------+-----------------------------------------------+-----------+
   | 1     | Required authentication for                   | RFC 9946  |
   |       | the Control phase                             |           |
   +-------+-----------------------------------------------+-----------+
   | 2     | Required authentication for                   | RFC 9946  |
   |       | the Control and Data phases                   |           |
   +-------+-----------------------------------------------+-----------+
0 is "Not used", 1 is required auth for the Control phase, and 2 is required auth for the Control and Data phases ๐Ÿ‘‘ the vibes are very escalating
     Table 9: Initial Values of the Test Setup PDU Authentication Mode
                                  Registry

12.3.5. Test Setup PDU Command Response Field Registry

ooh this registry is for the cmdResponse field in the Test Setup PDU ๐ŸŽ€ new code points come from [RFC8126], and the rules are in Table 10 โœจ

                   +=========+=========================+
                   | Range   | Registration Procedures |
                   +=========+=========================+
                   | 0-127   | IETF Review             |
                   +---------+-------------------------+
                   | 128-239 | Specification Required  |
                   +---------+-------------------------+
                   | 240-249 | Experimental Use        |
                   +---------+-------------------------+
                   | 250-254 | Private Use             |
                   +---------+-------------------------+
                   | 255     | Reserved                |
                   +---------+-------------------------+

                     Table 10: Registration Procedures
                       for the Test Setup PDU Command
                          Response Field Registry

so 0-127 is IETF Review ๐Ÿงš, 128-239 needs a Specification Required, 240-249 is Experimental Use, 250-254 is Private Use, and 255 is Reserved ๐Ÿ™ˆ

IANA has assigned decimal values in the "Test Setup PDU Command Response Field" registry like this ๐ŸŒธ

   +=========+============================================+===========+
   | Value   | Description                                | Reference |
   +=========+============================================+===========+
   | 0       | None (used by client in Request)           | RFC 9946  |
   +---------+--------------------------------------------+-----------+
   | 1       | Acknowledgment                             | RFC 9946  |
   +---------+--------------------------------------------+-----------+
   | 2       | Bad protocol version                       | RFC 9946  |
   +---------+--------------------------------------------+-----------+
   | 3       | Invalid Jumbo datagram option              | RFC 9946  |
   +---------+--------------------------------------------+-----------+
   | 4       | Unexpected authentication in Setup Request | RFC 9946  |
   +---------+--------------------------------------------+-----------+
   | 5       | Authentication missing in Setup Request    | RFC 9946  |
   +---------+--------------------------------------------+-----------+
   | 6       | Invalid authentication method              | RFC 9946  |
   +---------+--------------------------------------------+-----------+
   | 7       | Authentication failure                     | RFC 9946  |
   +---------+--------------------------------------------+-----------+
   | 8       | Authentication time is invalid in Setup    | RFC 9946  |
   |         | Request                                    |           |
   +---------+--------------------------------------------+-----------+
   | 9       | No maximum test bit rate specified         | RFC 9946  |
   +---------+--------------------------------------------+-----------+
   | 10      | Server maximum bit rate exceeded           | RFC 9946  |
   +---------+--------------------------------------------+-----------+
   | 11      | MTU option does not match server           | RFC 9946  |
   +---------+--------------------------------------------+-----------+
   | 12      | Multi-connection parameters rejected by    | RFC 9946  |
   |         | server                                     |           |
   +---------+--------------------------------------------+-----------+
   | 13      | Connection allocation failure on server    | RFC 9946  |
   +---------+--------------------------------------------+-----------+
   | 240-249 | Reserved for Experimental Use              | RFC 9946  |
   +---------+--------------------------------------------+-----------+
   | 250-254 | Reserved for Private Use                   | RFC 9946  |
   +---------+--------------------------------------------+-----------+
   | 255     | Reserved                                   | RFC 9946  |
   +---------+--------------------------------------------+-----------+
     Table 11: Initial Values of the Test Setup PDU Command Response
                              Field Registry

ngl this table is a whole list of responses the server can send back, from 0 (None, which the client uses in its Request) through 13 (Connection allocation failure on server) ๐Ÿฅน ๐Ÿ’€

note that value 4 has to stay as it is, because it's required for backward compatibility with previous experimental versions of software that are already in use ๐Ÿซถ also, value 6 means a client messed up and used an authMode that hasn't been standardized yet (so authMode is greater than 1 or 2) ๐Ÿ™ˆ

12.3.6. Test Activation PDU Command Request Registry

last one in this part! ๐ŸŽ‰ this registry is for the cmdRequest field in the Test Activation PDU ๐Ÿฆ‹ new code points are allocated via [RFC8126] following Table 12 ๐Ÿ’…

                   +=========+=========================+
                   | Range   | Registration Procedures |
                   +=========+=========================+
                   | 0-127   | IETF Review             |
                   +---------+-------------------------+
                   | 128-239 | Specification Required  |
                   +---------+-------------------------+
                   | 240-249 | Experimental Use        |
                   +---------+-------------------------+
                   | 250-254 | Private Use             |
                   +---------+-------------------------+
                   | 255     | Reserved                |
                   +---------+-------------------------+

                     Table 12: Registration Procedures
                        for the Test Activation PDU
                          Command Request Registry

IANA has assigned decimal values in the "Test Activation PDU Command Request" registry like this ๐ŸŒˆ

          +=========+==============================+===========+
          | Value   | Description                  | Reference |
          +=========+==============================+===========+
          | 0       | No Request                   | RFC 9946  |
          +---------+------------------------------+-----------+
          | 1       | Request test in upstream     | RFC 9946  |
          |         | direction (client to server) |           |
          +---------+------------------------------+-----------+
          | 2       | Request test in downstream   | RFC 9946  |
          |         | direction (server to client) |           |
          +---------+------------------------------+-----------+
          | 240-249 | Reserved for Experimental    | RFC 9946  |
          |         | Use                          |           |
          +---------+------------------------------+-----------+
          | 250-254 | Reserved for Private Use     | RFC 9946  |
          +---------+------------------------------+-----------+
          | 255     | Reserved                     | RFC 9946  |
          +---------+------------------------------+-----------+
             Table 13: Initial Values of the Test Activation
                       PDU Command Request Registry

so 0 is No Request, 1 asks for a test in the upstream direction (client to server) ๐Ÿ“ค, and 2 asks for one in the downstream direction (server to client) ๐Ÿ“ฅ. 240-249 is Reserved for Experimental Use, 250-254 is Reserved for Private Use, and 255 is Reserved ๐Ÿ’œ

12.3.7. Test Activation PDU Modifier Bitmap Registry ๐ŸŽ€

okay so bestie, IANA made a new registry called "Test Activation PDU Modifier Bitmap" inside the "UDP Speed Test Protocol (UDPSTP)" registry group ๐Ÿฆ‹ the Test Activation PDU layout (also!) has a modifierBitmap field, and the bitmaps in here get handed out using the registration procedures from [RFC8126], as laid out in Table 14. no cap, this table is giving organization ๐Ÿ’…

Table 14: Registration Procedures for the Test Activation PDU Modifier Bitmap Registry

   +===================+=========================+
   | Range             | Registration Procedures |
   +===================+=========================+
   | 00000000-01111111 | IETF Review             |
   +-------------------+-------------------------+
   | 10000000          | Reserved                |
   +-------------------+-------------------------+

IANA also filled in the starting values for this registry, which you can see in Table 15 ๐ŸŒธ

Table 15: Initial Values of the Test Activation PDU Modifier Bitmap Registry

   +=======+===============================================+===========+
   | Value | Description                                   | Reference |
   +=======+===============================================+===========+
   | 0x00  | No modifications                              | RFC 9946  |
   +-------+-----------------------------------------------+-----------+
   | 0x01  | Set when srIndexConf is                       | RFC 9946  |
   |       | start rate for search                         |           |
   +-------+-----------------------------------------------+-----------+
   | 0x02  | Set for randomized UDP                        | RFC 9946  |
   |       | payload                                       |           |
   +-------+-----------------------------------------------+-----------+

12.3.8. Test Activation PDU Rate Adjustment Algo Registry ๐Ÿงš

next up! IANA made a "Test Activation PDU Rate Adjustment Algo" registry under that same UDPSTP group โœจ the Test Activation PDU layout has a rateAdjAlgo field, and the code points here follow the [RFC8126] registration procedures in Table 16 ๐Ÿ’œ

Table 16: Registration Procedures for the Test Activation PDU Rate Adjustment Algo Registry

                    +=======+=========================+
                    | Range | Registration Procedures |
                    +=======+=========================+
                    | A-Y   | IETF Review             |
                    +-------+-------------------------+
                    | Z     | Reserved                |
                    +-------+-------------------------+

IANA also added this note under the registry, and it's important, so read it twice ๐Ÿ™ˆ

Note: The algorithm identifier is a capitalized alphabetic UTF-8 value (A-Z), specified by the corresponding incremental numeric.

IANA assigned the capitalized alphabetic UTF-8 values, plus their matching incremental numeric values, like this (Table 17) ๐Ÿ“

Table 17: Initial Values of the Test Activation PDU Rate Adjustment Algo Registry

      +================+=======================+==============+
      | Value(Numeric) | Description           | Reference    |
      +================+=======================+==============+
      | A(n/a)         | Not used              | RFC 9946     |
      +----------------+-----------------------+--------------+
      | B(0)           | Rate algorithm Type B | [Y.1540Amd2] |
      +----------------+-----------------------+--------------+
      | C(1)           | Rate algorithm Type C | [TR-471]     |
      +----------------+-----------------------+--------------+

12.3.9. Test Activation PDU Command Response Field Registry ๐Ÿ’–

last registry in this group, bestie! IANA created the "Test Activation PDU Command Response Field" registry under the UDPSTP registry group ๐ŸŒˆ the Test Activation PDU layout (also) has a cmdResponse field, and its code points follow the [RFC8126] registration procedures in Table 18 ๐Ÿฆ‹

Table 18: Registration Procedures for the Test Activation PDU Command Response Field Registry

                   +=========+=========================+
                   | Range   | Registration Procedures |
                   +=========+=========================+
                   | 0-127   | IETF Review             |
                   +---------+-------------------------+
                   | 128-239 | Specification Required  |
                   +---------+-------------------------+
                   | 240-249 | Experimental Use        |
                   +---------+-------------------------+
                   | 250-254 | Private Use             |
                   +---------+-------------------------+
                   | 255     | Reserved                |
                   +---------+-------------------------+

and the starting decimal values are in Table 19 ๐Ÿซถ

Table 19: Initial Values of the Test Activation PDU Command Response Field Registry

        +=========+==================================+===========+
        | Value   | Description                      | Reference |
        +=========+==================================+===========+
        | 0       | None (used by client in Request) | RFC 9946  |
        +---------+----------------------------------+-----------+
        | 1       | Server acknowledgment            | RFC 9946  |
        +---------+----------------------------------+-----------+
        | 2       | Server indicates an error        | RFC 9946  |
        +---------+----------------------------------+-----------+
        | 240-249 | Reserved for Experimental Use    | RFC 9946  |
        +---------+----------------------------------+-----------+
        | 250-254 | Reserved for Private Use         | RFC 9946  |
        +---------+----------------------------------+-----------+
        | 255     | Reserved                         | RFC 9946  |
        +---------+----------------------------------+-----------+

12.4. Guidelines for Designated Experts ๐Ÿ‘‘

okay so this bit is about the designated experts who look over registry change requests ๐Ÿง it's suggested that you have multiple of them appointed, bestie ๐Ÿ’…

they should check two things: whether the proposed registration just duplicates something already in the registry, and whether the description is clear and actually fits what the registry is for โœจ

registration requests get a two-week review period with the advice of one or more designated experts ๐Ÿ“… in that window, the experts will either approve or deny the request and tell IANA what they decided. when they deny, they should explain why and, if it makes sense, suggest how to make the request work ๐Ÿ’Œ

  1. References ๐Ÿ“š

13.1. Normative References ๐Ÿ”ฅ

[C-Prog] ISO/IEC, "Programming languages -- C", ISO/IEC 9899:1999, 1999, <https://www.iso.org/standard/29237.html>.

[NIST800-108] Chen, L., "Recommendation for Key Derivation Using Pseudorandom Functions", DOI 10.6028/NIST.SP.800-108r1-upd1, NIST SP 800-108r1-upd1, August 2022, <https://nvlpubs.nist.gov/nistpubs/SpecialPublications/ NIST.SP.800-108r1-upd1.pdf>.

[RFC0768] Postel, J., "User Datagram Protocol", STD 6, RFC 768, DOI 10.17487/RFC0768, August 1980, <https://www.rfc-editor.org/info/rfc768>.

[RFC0791] Postel, J., "Internet Protocol", STD 5, RFC 791, DOI 10.17487/RFC0791, September 1981, <https://www.rfc-editor.org/info/rfc791>.

[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, <https://www.rfc-editor.org/info/rfc2119>.

[RFC5044] Culley, P., Elzur, U., Recio, R., Bailey, S., and J. Carrier, "Marker PDU Aligned Framing for TCP Specification", RFC 5044, DOI 10.17487/RFC5044, October 2007, <https://www.rfc-editor.org/info/rfc5044>.

[RFC6234] Eastlake 3rd, D. and T. Hansen, "US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)", RFC 6234, DOI 10.17487/RFC6234, May 2011, <https://www.rfc-editor.org/info/rfc6234>.

[RFC7210] Housley, R., Polk, T., Hartman, S., and D. Zhang, "Database of Long-Lived Symmetric Cryptographic Keys", RFC 7210, DOI 10.17487/RFC7210, April 2014, <https://www.rfc-editor.org/info/rfc7210>.

[RFC8085] Eggert, L., Fairhurst, G., and G. Shepherd, "UDP Usage Guidelines", BCP 145, RFC 8085, DOI 10.17487/RFC8085, March 2017, <https://www.rfc-editor.org/info/rfc8085>.

[RFC8126] Cotton, M., Leiba, B., and T. Narten, "Guidelines for Writing an IANA Considerations Section in RFCs", BCP 26, RFC 8126, DOI 10.17487/RFC8126, June 2017, <https://www.rfc-editor.org/info/rfc8126>.

[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, <https://www.rfc-editor.org/info/rfc8174>.

[RFC8899] Fairhurst, G., Jones, T., Tรผxen, M., Rรผngeler, I., and T. Vรถlker, "Packetization Layer Path MTU Discovery for Datagram Transports", RFC 8899, DOI 10.17487/RFC8899, September 2020, <https://www.rfc-editor.org/info/rfc8899>.

[RFC9097] Morton, A., Geib, R., and L. Ciavattone, "Metrics and Methods for One-Way IP Capacity", RFC 9097, DOI 10.17487/RFC9097, November 2021, <https://www.rfc-editor.org/info/rfc9097>.

[TR-471] Broadband Forum, "Maximum IP-Layer Capacity Metric, Related Metrics, and Measurements", TR-471, Issue 4, September 2024, <https://www.broadband-forum.org/pdfs/tr-471-4-0-0.pdf>.

[Y.1540] ITU-T, "Internet protocol data communication service - IP packet transfer and availability performance parameters", ITU-T Recommendation Y.1540, December 2019, <https://www.itu.int/rec/T-REC-Y.1540-201912-I/en>.

[Y.1540Amd2] ITU-T, "Internet protocol data communication service - IP packet transfer and availability performance parameters, Amendment 2 - Revised Annex B: Additional search algorithms for IP-based capacity parameters and methods of measurement", ITU-T Recommendation Y.1540 Amd. 2, March 2023, <https://www.itu.int/rec/T-REC-Y.1540-202303-I!Amd2/en>.

13.2. Informative References โœจ

Okay bestie, here are the informative references, the extra reading that backs up the spec. ๐Ÿ“š Each one gets its full details so you can go find it yourself ๐Ÿ’–

[EVP_KDF-KB] "EVP_KDF-KB - The Key-Based EVP_KDF implementation", OpenSSL Documentation, <https://docs.openssl.org/master/man7/EVP_KDF-KB/>.

[RFC3148] Mathis, M. and M. Allman, "A Framework for Defining Empirical Bulk Transfer Capacity Metrics", RFC 3148, DOI 10.17487/RFC3148, July 2001, <https://www.rfc-editor.org/info/rfc3148>.

[RFC4656] Shalunov, S., Teitelbaum, B., Karp, A., Boote, J., and M. Zekauskas, "A One-way Active Measurement Protocol (OWAMP)", RFC 4656, DOI 10.17487/RFC4656, September 2006, <https://www.rfc-editor.org/info/rfc4656>.

[RFC5136] Chimento, P. and J. Ishac, "Defining Network Capacity", RFC 5136, DOI 10.17487/RFC5136, February 2008, <https://www.rfc-editor.org/info/rfc5136>.

[RFC5357] Hedayat, K., Krzanowski, R., Morton, A., Yum, K., and J. Babiarz, "A Two-Way Active Measurement Protocol (TWAMP)", RFC 5357, DOI 10.17487/RFC5357, October 2008, <https://www.rfc-editor.org/info/rfc5357>.

[RFC7497] Morton, A., "Rate Measurement Test Protocol Problem Statement and Requirements", RFC 7497, DOI 10.17487/RFC7497, April 2015, <https://www.rfc-editor.org/info/rfc7497>.

[RFC7594] Eardley, P., Morton, A., Bagnulo, M., Burbridge, T., Aitken, P., and A. Akhter, "A Framework for Large-Scale Measurement of Broadband Performance (LMAP)", RFC 7594, DOI 10.17487/RFC7594, September 2015, <https://www.rfc-editor.org/info/rfc7594>.

[RFC8337] Mathis, M. and A. Morton, "Model-Based Metrics for Bulk Transport Capacity", RFC 8337, DOI 10.17487/RFC8337, March 2018, <https://www.rfc-editor.org/info/rfc8337>.

[RFC8762] Mirsky, G., Jun, G., Nydell, H., and R. Foote, "Simple Two-Way Active Measurement Protocol", RFC 8762, DOI 10.17487/RFC8762, March 2020, <https://www.rfc-editor.org/info/rfc8762>.

[RFC9145] Boucadair, M., Reddy.K, T., and D. Wing, "Integrity Protection for the Network Service Header (NSH) and Encryption of Sensitive Context Headers", RFC 9145, DOI 10.17487/RFC9145, December 2021, <https://www.rfc-editor.org/info/rfc9145>.


Appendix A. KDF Example (OpenSSL) ๐Ÿ’…

Here's a little code treat ๐Ÿ“ It's a KDF example in OpenSSL, and the function turns derived key material into the individual client and server authentication keys. Here's the code:

<CODE BEGINS>

//
// Output individual authentication keys of length SHA256_KEY_LEN
// from derived key material.
//
// Return Values: 0 = Failure, 1 = Success
//
int kdf_hmac_sha256(char *Kin, uint32_t authUnixTime,
  unsigned char *cAuthKey,   // Client key
  unsigned char *sAuthKey) { // Server key

  int var, keylen = SHA256_KEY_LEN * 2;
  char context[16];
  unsigned char *keyptr, keybuf[keylen];
  EVP_KDF *kdf = NULL;
  EVP_KDF_CTX *kctx = NULL;
  OSSL_PARAM params[16], *p = params;

  //
  // Fetch KDF algorithm and create context
  //
  if ((kdf = EVP_KDF_fetch(NULL, "KBKDF", NULL)) == NULL) {
    return 0;
  }
  if ((kctx = EVP_KDF_CTX_new(kdf)) == NULL) {
    EVP_KDF_free(kdf);
    return 0;
  }

  //
  // Set parameters for KBKDF
  // ---------------------------------------------------------
  *p++ = OSSL_PARAM_construct_utf8_string(
    OSSL_KDF_PARAM_MODE, "COUNTER", 0);
  *p++ = OSSL_PARAM_construct_utf8_string(
    OSSL_KDF_PARAM_MAC, "HMAC", 0);
  *p++ = OSSL_PARAM_construct_utf8_string(
    OSSL_KDF_PARAM_DIGEST, "SHA256", 0);
  *p++ = OSSL_PARAM_construct_octet_string(
    OSSL_KDF_PARAM_KEY, Kin, strlen(Kin));
  *p++ = OSSL_PARAM_construct_octet_string(
    OSSL_KDF_PARAM_SALT, "UDPSTP", 6);
  var = snprintf(context, sizeof(context), "%u", authUnixTime);
  *p++ = OSSL_PARAM_construct_octet_string(
    OSSL_KDF_PARAM_INFO, context, var);
  //
  // Confirm the following are enabled
  //
  var = 1;
  *p++ = OSSL_PARAM_construct_int(OSSL_KDF_PARAM_KBKDF_USE_L, &var);
  *p++ = OSSL_PARAM_construct_int(
    OSSL_KDF_PARAM_KBKDF_USE_SEPARATOR, &var);
  //
  // Set counter length in bits (available as of OpenSSL 3.1)
  //
  var = 32; // Length of 32 is backward compatible with OpenSSL 3.0
  *p++ = OSSL_PARAM_construct_int(OSSL_KDF_PARAM_KBKDF_R, &var);
  *p++ = OSSL_PARAM_construct_end();
  // ---------------------------------------------------------

  //
  // Derive key material
  //
  if (EVP_KDF_derive(kctx, keybuf, keylen, params) < 1) {
    EVP_KDF_CTX_free(kctx);
    EVP_KDF_free(kdf);
    return 0;
  }

  //
  // Output individual keys
  //
  keyptr = keybuf;
  memcpy(cAuthKey, keyptr, SHA256_KEY_LEN);
  keyptr += SHA256_KEY_LEN;
  memcpy(sAuthKey, keyptr, SHA256_KEY_LEN);

  //
  // Cleanup
  //
  EVP_KDF_CTX_free(kctx);
  EVP_KDF_free(kdf);
  return 1;
}

<CODE ENDS>

Figure 12: KDF Example Code Snippet


Acknowledgments ๐Ÿซถ

This document was edited by Al Morton, who sadly passed away before being able to finalize this work. Ruediger Geib only joined later to help finalize this specification. ๐Ÿฆ‹

Thanks to Lincoln Lavoie, Can Desem, Greg Mirsky, Bjoern Ivar Teigen, Ken Kerpez, and Chen Li for reviewing this specification and providing helpful suggestions and areas for further development. Mohamed Boucadair's AD review improved comprehensibility of the document, and he provided helpful guidance well through the final review stages. Tommy Pauly shepherded this document. Further comments by Gorry Fairhurst, ร‰ric Vyncke, Roman Danyliw, Gunter Van de Velde, Deb Cooley, Tianran Zhou, Andy Newton, Giuseppe Fioccola, Lars Eggert, Erik Kline, and Benson Muite helped to shape the document. David Dong and Amanda Baber provided early reviews of the IANA Considerations section. ๐Ÿ’œ

Starting with the early SEC-DIR review, Brian Weis provided constructive guidance regarding numerous security-related protocol issues. The Crypto Forum Research Group reviewed these parts, again providing guidance. Magnus Westerlund's review resulted in further changes and refinements. Ultimately, Paul Wouters' feedback was critical in finalizing the chosen security approach. ๐Ÿ”ฅ


Authors' Addresses ๐Ÿฆ‹

Al Morton
AT&T Labs


Len Ciavattone
AT&T Labs
Middletown, NJ
United States of America
Email: lenciavattone@gmail.com


Ruediger Geib (editor)
Deutsche Telekom
Deutsche Telekom Allee 9
64295 Darmstadt
Germany
Phone: +49 6151 5812747
Email: Ruediger.Geib@telekom.de
๐Ÿ“ฆ where this page comes from