Internet-Draft QUIC event definitions for qlog November 2026
Marx, et al. Expires 6 May 2027 [Page]
Workgroup:
QUIC
Internet-Draft:
draft-ietf-quic-qlog-quic-events-14
Published:
Intended Status:
Standards Track
Expires:
Authors:
R. Marx, Ed.
Akamai
L. Niccolini, Ed.
Meta
M. Seemann, Ed.
L. Pardue, Ed.
Apple

QUIC event definitions for qlog

Abstract

This document describes a qlog event schema containing concrete qlog event definitions and their metadata for the core QUIC protocol and selected extensions.

Note to Readers

  • Note to RFC editor: Please remove this section before publication.

Feedback and discussion are welcome at https://github.com/quicwg/qlog. Readers are advised to refer to the "editor's draft" at that URL for an up-to-date version of this document.

Status of This Memo

This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.

Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.

Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."

This Internet-Draft will expire on 6 May 2027.

▲

Table of Contents

1. Introduction

This document defines a qlog event schema (Section 8 of [QLOG-MAIN]) containing concrete events for the core QUIC protocol (see [QUIC-TRANSPORT], [QUIC-RECOVERY], and [QUIC-TLS]) and some of its extensions (see [QUIC-DATAGRAM], [GREASEBIT], and [RESET-STREAM-AT]).

The event namespace with identifier quic is defined; see Section 2. In this namespace multiple events derive from the qlog abstract Event class (Section 7 of [QLOG-MAIN]), each extending the data field and defining their name field values and semantics. The data types that the events use are defined in Section 8.

1.1. Use of Group IDs

QUIC's Original Destination Connection ID (ODCID) can be used as the value of the qlog group_id field. The ODCID is the connection ID that the client chooses for its first Initial packet. It stays the same for the whole connection, unlike the connection IDs that endpoints use, which can change. It can also link packets such as Retry and Version Negotiation to the connection.

The ODCID can also be used as the qlog filename or file identifier, optionally suffixed by the vantage point type. For example, abcd1234_server.qlog would contain the server-side trace of the connection with ODCID abcd1234.

1.2. Raw Packet and Frame Information

Protected QUIC packets (Initial, 0-RTT, Handshake, and 1-RTT) end with an AEAD authentication tag. Every cipher suite that QUIC version 1 can use has a 16-byte tag; see Section 5.3 of [QUIC-TLS]. When calculating the lengths in RawInfo fields (Section 10 of [QLOG-MAIN]) for these packets, the tag is treated as a 16-byte trailer.

1.3. Events without a Known Connection

A qlog trace is typically associated with a single QUIC connection. However, some events cannot be tied to a specific connection, especially on the server. For example, a packet_dropped event (Section 5.7) with the trigger "connection_unknown" is for a packet that matches no known connection.

There are various ways to handle these events, each with tradeoffs between file size overhead, flexibility, ease of use, and ease of implementation. Some options include:

  • Log them in a separate endpoint-wide trace, or use a special group_id value, not associated with a single connection.
  • Log them in the most recently used trace.
  • Use additional heuristics to identify the connection, for example the four-tuple in addition to the connection ID.
  • Buffer events until they can be assigned to a connection, for example events about the Version Negotiation and Retry packets that a server sends before it creates connection state.

1.4. Notational Conventions

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

The event and data structure definitions in this document are expressed in the Concise Data Definition Language [CDDL] and its extensions described in Section 1.2 of [QLOG-MAIN].

This document uses the following from [QLOG-MAIN]: the event namespace and event type concepts; the name, data, tuple, group_id, and time-related fields; and the RawInfo type.

Events are defined with an importance level as described in Section 8.3 of [QLOG-MAIN].

As is the case for [QLOG-MAIN], the qlog schema definitions in this document are intentionally agnostic to serialization formats. The choice of format is an implementation decision.

2. Event Schema Definition

This document describes how the core QUIC protocol and selected extensions can be expressed in qlog using a newly defined event schema. Per the requirements in Section 8 of [QLOG-MAIN], this document registers the quic namespace. The event schema URI is urn:ietf:params:qlog:events:quic.

2.1. Draft Event Schema Identification

This section is to be removed before publishing as an RFC.

Only implementations of the final, published RFC can use the events belonging to the event schema with the URI urn:ietf:params:qlog:events:quic. Until such an RFC exists, implementations MUST NOT identify themselves using this URI.

Implementations of draft versions of the event schema MUST append the string "-" and the corresponding draft number to the URI. For example, draft 07 of this document is identified using the URI urn:ietf:params:qlog:events:quic-07.

The namespace identifier itself is not affected by this requirement.

3. QUIC Event Overview

Table 1 summarizes the name value of each event type that is defined in this specification.

Table 1: QUIC Events
Name value Importance Definition
quic:server_listening Extra Section 4.1
quic:connection_started Base Section 4.2
quic:connection_closed Base Section 4.3
quic:connection_id_updated Base Section 4.4
quic:spin_bit_updated Base Section 4.5
quic:connection_state_updated Base Section 4.6
quic:tuple_assigned Base Section 4.7
quic:mtu_updated Extra Section 4.8
quic:version_information Core Section 5.1
quic:alpn_information Core Section 5.2
quic:parameters_set Core Section 5.3
quic:parameters_restored Base Section 5.4
quic:packet_sent Core Section 5.5
quic:packet_received Core Section 5.6
quic:packet_dropped Base Section 5.7
quic:packet_buffered Base Section 5.8
quic:packets_acked Extra Section 5.9
quic:udp_datagrams_sent Extra Section 5.10
quic:udp_datagrams_received Extra Section 5.11
quic:udp_datagram_dropped Extra Section 5.12
quic:stream_state_updated Base Section 5.13
quic:frames_created Extra Section 5.14.1
quic:frames_parsed Extra Section 5.14.2
quic:stream_data_moved Base Section 5.15
quic:datagram_data_moved Base Section 5.16
quic:datagram_dropped Base Section 5.17
quic:connection_data_blocked_updated Extra Section 5.18
quic:stream_data_blocked_updated Extra Section 5.19
quic:datagram_data_blocked_updated Extra Section 5.20
quic:migration_state_updated Extra Section 5.21
quic:timer_updated Extra Section 5.22
quic:key_updated Base Section 6.1
quic:key_discarded Base Section 6.2
quic:recovery_parameters_set Base Section 7.1
quic:recovery_metrics_updated Core Section 7.2
quic:congestion_state_updated Base Section 7.3
quic:packet_lost Core Section 7.4
quic:marked_for_retransmit Extra Section 7.5
quic:ecn_state_updated Extra Section 7.6

QUIC events extend the $ProtocolEventData extension point defined in Section 8.2 of [QLOG-MAIN]. Additionally, they allow for direct extensibility by their use of per-event extension points via the $$ CDDL "group socket" syntax, as also described in Section 8.2 of [QLOG-MAIN].

QuicEventData = QUICServerListening /
                QUICConnectionStarted /
                QUICConnectionClosed /
                QUICConnectionIDUpdated /
                QUICSpinBitUpdated /
                QUICConnectionStateUpdated /
                QUICTupleAssigned /
                QUICMTUUpdated /
                QUICVersionInformation /
                QUICALPNInformation /
                QUICParametersSet /
                QUICParametersRestored /
                QUICPacketSent /
                QUICPacketReceived /
                QUICPacketDropped /
                QUICPacketBuffered /
                QUICPacketsAcked /
                QUICUDPDatagramsSent /
                QUICUDPDatagramsReceived /
                QUICUDPDatagramDropped /
                QUICStreamStateUpdated /
                QUICFramesCreated /
                QUICFramesParsed /
                QUICStreamDataMoved /
                QUICDatagramDataMoved /
                QUICDatagramDropped /
                QUICConnectionDataBlockedUpdated /
                QUICStreamDataBlockedUpdated /
                QUICDatagramDataBlockedUpdated /
                QUICMigrationStateUpdated /
                QUICTimerUpdated /
                QUICKeyUpdated /
                QUICKeyDiscarded /
                QUICRecoveryParametersSet /
                QUICRecoveryMetricsUpdated /
                QUICCongestionStateUpdated /
                QUICPacketLost /
                QUICMarkedForRetransmit /
                QUICECNStateUpdated

$ProtocolEventData /= QuicEventData
Figure 1: QuicEventData definition and ProtocolEventData extension

The concrete QUIC event types are further defined below, their type identifier is the heading name. The subdivisions in sections on Connectivity, Transport, Security, and Recovery are purely for readability.

4. Connectivity Events

4.1. server_listening

The server_listening event is emitted when the server starts accepting connections. It has Extra importance level.

QUICServerListening = {
    ? address_v4: IPAddressPort
    ? address_v6: IPAddressPort

    ; the server will always answer client Initial packets with a Retry
    ; packet (no 1-RTT connection setups by choice)
    ? retry_required: bool

    * $$quic-serverlistening-extension
}
Figure 2: QUICServerListening definition

Some QUIC stacks do not handle sockets directly, so they cannot log IP addresses or ports.

4.2. connection_started

The connection_started event is emitted when a client attempts a new connection, or when a server accepts one. It overlaps with the connection_state_updated event, but it also logs the local and remote endpoint information. It has Base importance level.

QUICConnectionStarted = {
    local: TupleEndpointInfo
    remote: TupleEndpointInfo

    * $$quic-connectionstarted-extension
}
Figure 3: QUICConnectionStarted definition

Some QUIC stacks do not handle sockets directly, so they cannot log IP addresses or ports. All fields of TupleEndpointInfo (Figure 45) are optional, so such a stack can log local and remote with only the fields it knows, such as connection_ids.

4.3. connection_closed

The connection_closed event is emitted when a connection closes, typically because of an error or a timeout. It has Base importance level.

This event overlaps with the connection_state_updated event and the CONNECTION_CLOSE frame. However, when analyzing large deployments, it can be useful to have a single event for each closure, with a reason field for more detail. This event can also log closures due to timeouts or explicit application actions, such as racing several connections and aborting the slowest, which the other events cannot easily capture.

The connection_closed event is logged when the local endpoint does one of the following; see Section 10 of [QUIC-TRANSPORT]:

  • silently discards the connection after an idle timeout;
  • sends a CONNECTION_CLOSE frame, and enters the closing state;
  • receives a CONNECTION_CLOSE frame, and enters the draining state; or
  • receives a Stateless Reset, and enters the draining state.

Later changes, such as leaving the closing or draining state, are logged with the connection_state_updated event.

QUIC has two kinds of connection error, transport errors and application errors, each with defined codes and semantics; see Section 20 of [QUIC-TRANSPORT]. An implementation can also have internal errors, which it might or might not map to these codes. So this event can carry two levels of error: connection_error or application_error for the QUIC error, and internal_code for a more specific internal error.

If an error code has no known string, connection_error or application_error can be "unknown", and error_code then holds the numerical value without variable-length integer encoding.

QUICConnectionClosed = {

    ; which side closed the connection
    ? initiator: Initiator
    ? connection_error: $TransportError /
                        CryptoError
    ? application_error: $ApplicationError

    ; if connection_error or application_error === "unknown"
    ? error_code: uint64

    ? internal_code: uint64
    ? reason: text
    ? trigger:
        "idle_timeout" /
        "application" /
        "error" /
        "version_mismatch" /
        ; when received from peer
        "stateless_reset" /
        "aborted" /
        ; when it is unclear what triggered the CONNECTION_CLOSE
        "unspecified"

    * $$quic-connectionclosed-extension
}
Figure 4: QUICConnectionClosed definition

Implementations SHOULD use the most descriptive trigger value that they can determine. This is often clear at the endpoint that closes the connection, but can be less clear at the endpoint that receives the CONNECTION_CLOSE frame.

4.4. connection_id_updated

The connection_id_updated event is emitted when either endpoint updates its current connection ID. This typically happens rarely during a connection, so logging this event is more efficient than logging the connection ID in every packet_sent or packet_received event. It has Base importance level.

The connection_id_updated event is viewed from the perspective of the endpoint applying the new ID. When the endpoint receives a new connection ID from the peer, the initiator field is "remote". When the endpoint updates its own connection ID, the initiator field is "local".

QUICConnectionIDUpdated = {
    initiator: Initiator
    ? old: ConnectionID
    ? new: ConnectionID

    * $$quic-connectionidupdated-extension
}
Figure 5: QUICConnectionIDUpdated definition

4.5. spin_bit_updated

The spin_bit_updated event is emitted when the value of the QUIC latency spin bit changes; see Section 17.4 of [QUIC-TRANSPORT]. It SHOULD NOT be emitted when the spin bit is set to the value it already has. It has Base importance level.

QUICSpinBitUpdated = {
    state: bool

    * $$quic-spinbitupdated-extension
}
Figure 6: QUICSpinBitUpdated definition

4.6. connection_state_updated

The connection_state_updated event is emitted when the connection changes state, to track progress through the QUIC handshake and connection close. It has Base importance level.

[QUIC-TRANSPORT] does not define a complete set of connection states or their transitions, though it names some, such as the closing and draining states. This document defines the states most likely to be useful for debugging QUIC connections; the list is not exhaustive.

QUIC implementations SHOULD log the BaseConnectionStates, and can add the GranularConnectionStates when more in-depth debugging is required. Tools SHOULD handle both kinds equally.

QUICConnectionStateUpdated = {
    ? old: $ConnectionState
    new: $ConnectionState

    * $$quic-connectionstateupdated-extension
}

BaseConnectionStates =
    ; Initial packet sent/received
    "attempted" /

    ; Handshake packet sent/received
    "handshake_started" /

    ; Both sent a TLS Finished message
    ; and verified the peer's TLS Finished message
    ; 1-RTT packets can be sent
    ; RFC 9001 Section 4.1.1
    "handshake_complete" /

    ; CONNECTION_CLOSE sent/received,
    ; stateless reset received or idle timeout
    "closed"

GranularConnectionStates =
    ; RFC 9000 Section 8.1
    ; client sent Handshake packet OR
    ; client used connection ID chosen by the server OR
    ; client used valid address validation token
    "peer_validated" /

    ; 1-RTT data can be sent by the server,
    ; but handshake is not done yet
    ; (server has sent TLS Finished; sometimes called 0.5 RTT data)
    "early_write" /

    ; HANDSHAKE_DONE sent/received.
    ; RFC 9001 Section 4.1.2
    "handshake_confirmed" /

    ; CONNECTION_CLOSE sent
    "closing" /

    ; CONNECTION_CLOSE or Stateless Reset received
    "draining" /

    ; draining or closing period done, connection state discarded
    "closed"

$ConnectionState /= BaseConnectionStates / GranularConnectionStates
Figure 7: QUICConnectionStateUpdated definition

The connection_state_updated event overlaps with the connection_closed and connection_started events, and with the frames logged in events such as packet_received. Still, it can be useful to log state transitions separately, especially if they map to an internal state machine. Implementations can therefore use other ConnectionState values that match their internal logic. Tools SHOULD handle these custom states in a similar way to the states defined in this document.

4.7. tuple_assigned

The tuple_assigned event associates a TupleID with the details of a network tuple, such as its IP addresses, ports, and connection IDs. It has Base importance level.

As described in Section 7.2 of [QLOG-MAIN], each qlog event can be linked to a single network tuple by its top-level tuple field, whose value is a TupleID. Encoding tuple details, such as IP addresses or connection IDs, into the TupleID itself can be cumbersome. This event records them separately, so TupleIDs can stay short, and their details can be updated as the connection evolves.

QUICTupleAssigned = {
    tuple_id: TupleID

    ; the information for traffic going towards the remote receiver
    ? tuple_remote: TupleEndpointInfo

    ; the information for traffic coming in at the local endpoint
    ? tuple_local: TupleEndpointInfo

    * $$quic-tupleassigned-extension
}
Figure 8: QUICTupleAssigned definition

Implementations choose their own tuple_id values. For example, a TupleID can be an incrementing integer, the first Destination Connection ID associated with the tuple (or its sequence number), or the two endpoint IP addresses or a hash of them.

The empty string ("") is a valid TupleID, and it is the default for events that do not set the tuple field. So the initial tuple of a QUIC connection, on which the handshake occurs (see Section 4.2), is implicitly associated with the TupleID "". Details for this default tuple can be logged with a tuple_assigned event whose tuple_id is "".

Because a tuple's details can change during the connection, a TupleID can appear in several tuple_assigned events, and the latest one holds its current details. The first tuple_assigned event for a TupleID indicates that the TupleID was created, and later ones indicate updates. An event with both tuple_local and tuple_remote omitted indicates that the TupleID was abandoned.

4.8. mtu_updated

The mtu_updated event indicates that the estimated Path MTU was updated. This happens as part of Path MTU discovery; see Section 14 of [QUIC-TRANSPORT]. It has Extra importance level.

QUICMTUUpdated = {
    ? old: uint32
    new: uint32

    ; at some point, MTU discovery stops, as a "good enough"
    ; packet size has been found
    ? done: bool

    * $$quic-mtuupdated-extension
}
Figure 9: QUICMTUUpdated definition

The done field defaults to false.

5. Transport Events

5.1. version_information

The version_information event supports QUIC version negotiation; see Section 6 of [QUIC-TRANSPORT]. It has Core importance level.

Each QUIC endpoint supports one or more versions. The client sends its first Initial packet using one of them. A server that does not support that version can reply with a Version Negotiation packet, which lists the versions the server supports. This event records the versions that each endpoint supports, and the chosen version if there is one.

QUICVersionInformation = {
    ? server_versions: [+ QuicVersion]
    ? client_versions: [+ QuicVersion]
    ? chosen_version: QuicVersion

    * $$quic-versioninformation-extension
}
Figure 10: QUICVersionInformation definition

A client logs this event when it sends its first Initial packet. The event includes client_versions, and chosen_version holds the version used in that packet.

If the client then processes a Version Negotiation packet, it logs this event again with client_versions. The event also includes server_versions, which holds the versions from that packet. If the client selects one of them, chosen_version holds it; otherwise, chosen_version is absent.

A Version Negotiation packet can also list reserved versions, which an endpoint adds to check that its peer ignores unknown versions; see Section 6.3 of [QUIC-TRANSPORT]. Because server_versions holds the versions from the packet, it can include reserved versions.

A server logs this event when it receives a client's first Initial packet. The event includes server_versions. If the server supports the client's version, chosen_version holds the version that the server uses for the connection. Otherwise, chosen_version is absent, and client_versions holds the version the client used.

5.2. alpn_information

The alpn_information event supports Application-Layer Protocol Negotiation (ALPN) over the QUIC transport; see [RFC7301] and Section 8.1 of [QUIC-TLS]. It has Core importance level.

QUIC endpoints are configured with a list of supported ALPN identifiers. During the TLS handshake, the client sends its list, and the server chooses one identifier from both lists and sends it back. If no match is found, the connection is closed.

ALPN identifiers are byte sequences, which are often valid UTF-8. The ALPNIdentifier type can hold either form: byte_value holds the raw bytes, and string_value holds the identifier as text. Implementations SHOULD log at least one form and MAY log both.

QUICALPNInformation = {
    ? server_alpns: [* ALPNIdentifier]
    ? client_alpns: [* ALPNIdentifier]
    ? chosen_alpn: ALPNIdentifier

    * $$quic-alpninformation-extension
}

ALPNIdentifier = {
  ? byte_value: hexstring
  ? string_value: text
}
Figure 11: QUICALPNInformation definition

A client usually logs this event twice: first with client_alpns when it offers its list, then with chosen_alpn when it learns the server's choice. It can instead log a single event with both fields once it learns the choice.

A server logs this event once, after it chooses, with client_alpns, server_alpns, and chosen_alpn.

5.3. parameters_set

The parameters_set event groups settings from several sources, such as transport parameters and the TLS cipher suite, into a single event. This keeps the number of events low, and lets a reader reason about what the settings do without needing to know which mechanism set each one. It has Core importance level.

Most of these settings are set once and never change. However, they are often set at different times during the connection, so a trace can contain several parameters_set events with different fields set.

Some settings have two values: one set by the local endpoint, and one requested by the peer. The initiator field says which side the settings in an event come from, so it MUST be correct for every setting in that event. Local and remote settings MUST be logged in separate events. If the local endpoint changes its behavior based on the peer's settings, the outcome can be logged with a new event type, or with fields added through $$quic-parametersset-extension; see Section 8.2 of [QLOG-MAIN].

The initiator field is required. Settings that both endpoints negotiate, such as tls_cipher, are logged in both the local and the remote event.

Each setting starts the connection with its default value, or with no value if it has no default. A parameters_set event that includes a setting replaces its previous value; settings that the event omits keep their values.

The grease_quic_bit and reset_stream_at settings are true if the transport parameter is present, and default to false.

Implementations are not required to recognize, process, or support every parameter that they receive. For example, QUIC implementations ignore transport parameters that they do not support; see Section 7.4.2 of [QUIC-TRANSPORT]. The unknown_parameters field can log the raw values of such parameters, for example reserved (GREASE) parameters, private extensions, or experiments.

In the case of connection resumption and 0-RTT, some of the server's parameters are stored up-front at the client and used for the initial connection startup. They are later updated with the server's reply. In these cases, utilize the separate parameters_restored event to indicate the initial values, and this event to indicate the updated values, as normal.

QUICParametersSet = {
    initiator: Initiator

    ; true if valid session ticket was received
    ? resumption_allowed: bool

    ; true if early data extension was enabled on the TLS layer
    ? early_data_enabled: bool

    ; e.g., "AES_128_GCM_SHA256"
    ? tls_cipher: text

    ; RFC9000
    ? original_destination_connection_id: ConnectionID
    ? initial_source_connection_id: ConnectionID
    ? retry_source_connection_id: ConnectionID
    ? stateless_reset_token: StatelessResetToken
    ? disable_active_migration: bool
    ? max_idle_timeout: uint64
    ? max_udp_payload_size: uint64
    ? ack_delay_exponent: uint64
    ? max_ack_delay: uint64
    ? active_connection_id_limit: uint64
    ? initial_max_data: uint64
    ? initial_max_stream_data_bidi_local: uint64
    ? initial_max_stream_data_bidi_remote: uint64
    ? initial_max_stream_data_uni: uint64
    ? initial_max_streams_bidi: uint64
    ? initial_max_streams_uni: uint64
    ? preferred_address: PreferredAddress
    ? unknown_parameters: [* UnknownParameter]

    ; RFC9221
    ? max_datagram_frame_size: uint64

    ; RFC9287
    ? grease_quic_bit: bool

    ; draft-ietf-quic-reliable-stream-reset
    ? reset_stream_at: bool

    * $$quic-parametersset-extension
}

PreferredAddress = {
    ? address_v4: IPAddressPort
    ? address_v6: IPAddressPort
    connection_id: ConnectionID
    stateless_reset_token: StatelessResetToken
}

UnknownParameter = {
    id: uint64
    ? value: hexstring
}
Figure 12: QUICParametersSet definition

5.4. parameters_restored

When using QUIC 0-RTT, clients are expected to remember and restore the server's transport parameters from the previous connection. The parameters_restored event is used to indicate which parameters were restored and to which values when utilizing 0-RTT. It has Base importance level.

Note that not all transport parameters should be restored (many are even prohibited from being re-utilized). The ones listed here are the ones expected to be useful for correct 0-RTT usage.

QUICParametersRestored = {

    ; RFC9000
    ? disable_active_migration: bool
    ? max_idle_timeout: uint64
    ? max_udp_payload_size: uint64
    ? active_connection_id_limit: uint64
    ? initial_max_data: uint64
    ? initial_max_stream_data_bidi_local: uint64
    ? initial_max_stream_data_bidi_remote: uint64
    ? initial_max_stream_data_uni: uint64
    ? initial_max_streams_bidi: uint64
    ? initial_max_streams_uni: uint64

    ; RFC9221
    ? max_datagram_frame_size: uint64

    ; RFC9287
    ; can only be restored at the client.
    ; servers MUST NOT restore this parameter!
    ? grease_quic_bit: bool

    * $$quic-parametersrestored-extension
}
Figure 13: QUICParametersRestored definition

5.5. packet_sent

The packet_sent event indicates that a QUIC packet was sent. It has Core importance level.

QUICPacketSent = {
    header: PacketHeader
    ? frames: [* $QuicFrame]

    ; only if header.packet_type === "stateless_reset"
    ? stateless_reset_token: StatelessResetToken

    ; only if header.packet_type === "version_negotiation"
    ? supported_versions: [+ QuicVersion]
    ? raw: RawInfo
    ? datagram_payload_checksum: uint32
    ? is_mtu_probe_packet: bool

    ? trigger:
      ; RFC 9002 Section 6.1.1
      "retransmit_reordered" /
      ; RFC 9002 Section 6.1.2
      "retransmit_timeout" /
      ; RFC 9002 Section 6.2.4
      "pto_probe" /
      ; RFC 9002 6.2.3
      "retransmit_crypto" /
      ; needed for some CCs to figure out bandwidth allocations
      ; when there are no normal sends
      "cc_bandwidth_probe"

    * $$quic-packetsent-extension
}
Figure 14: QUICPacketSent definition

The encryption level and packet number space are not logged explicitly, because header.packet_type implies them for the packet types that have them.

The datagram_payload_checksum field is described in Section 9.

The is_mtu_probe_packet field defaults to false.

5.6. packet_received

The packet_received event indicates that a QUIC packet was received. It has Core importance level.

QUICPacketReceived = {
    header: PacketHeader
    ? frames: [* $QuicFrame]

    ; only if header.packet_type === "stateless_reset"
    ? stateless_reset_token: StatelessResetToken

    ; only if header.packet_type === "version_negotiation"
    ? supported_versions: [+ QuicVersion]
    ? raw: RawInfo
    ? datagram_payload_checksum: uint32

    ? trigger:
        ; if packet was buffered because it couldn't be
        ; decrypted before
        "keys_available"

    * $$quic-packetreceived-extension
}
Figure 15: QUICPacketReceived definition

The encryption level and packet number space are not logged explicitly, because header.packet_type implies them for the packet types that have them.

The datagram_payload_checksum field is described in Section 9.

5.7. packet_dropped

The packet_dropped event indicates that a QUIC packet was dropped. It has Base importance level.

QUICPacketDropped = {

    ; Primarily packet_type should be filled here,
    ; as other fields might not be decryptable or parsable
    ? header: PacketHeader
    ? raw: RawInfo
    ? datagram_payload_checksum: uint32
    ? details: {* text => any}
    ? trigger:
        "internal_error" /
        "rejected" /
        "unsupported" /
        "invalid" /
        "duplicate" /
        "connection_unknown" /
        "decryption_failure" /
        "keys_unavailable" /
        "general"

    * $$quic-packetdropped-extension
}
Figure 16: QUICPacketDropped definition

The trigger field gives a general reason for dropping the packet, and the details field can hold additional implementation-specific information. Some example situations for each trigger value include:

  • "internal_error": not initialized, out of memory
  • "rejected": limits reached, DDoS protection, unwilling to track more paths
  • "unsupported": unknown or unsupported version
  • "invalid": packet parsing or validation error
  • "duplicate": duplicate packet
  • "connection_unknown": packet does not relate to a known connection or connection ID
  • "decryption_failure": decryption failed
  • "keys_unavailable": decryption keys were unavailable
  • "general": situations not clearly covered in the other categories

Packets dropped as "unsupported" or "connection_unknown" often cannot be tied to a connection; see Section 1.3.

The datagram_payload_checksum field is described in Section 9.

5.8. packet_buffered

The packet_buffered event is emitted when a packet is buffered because it cannot be processed yet, for example because the keys to decrypt it are not yet available. Its full contents can be logged later, in a packet_received event, once it is processed. It has Base importance level.

QUICPacketBuffered = {

    ; primarily packet_type should be filled here as other elements
    ; might not be available yet
    ? header: PacketHeader
    ? raw: RawInfo
    ? datagram_payload_checksum: uint32
    ? trigger:
        ; indicates the parser cannot keep up, temporarily buffers
        ; packet for later processing
        "backpressure" /
        ; if packet cannot be decrypted because the proper keys were
        ; not yet available
        "keys_unavailable"

    * $$quic-packetbuffered-extension
}
Figure 17: QUICPacketBuffered definition

The datagram_payload_checksum field is described in Section 9.

5.9. packets_acked

The packets_acked event is emitted when one or more sent packets are acknowledged by the peer for the first time. It has Extra importance level.

Successive ACK frames can acknowledge the same packet, but this event is emitted only for the first acknowledgment of each packet. This information can also be deduced from received ACK frames, though finding the first acknowledgment needs extra processing. Implementations that do not log frame contents can use this event instead.

QUICPacketsAcked = {
    ? packet_number_space: $PacketNumberSpace
    ? packet_numbers: [+ uint64]

    * $$quic-packetsacked-extension
}
Figure 18: QUICPacketsAcked definition

The packet_number_space field defaults to "application_data", the packet number space that most packets in a typical QUIC connection use.

5.10. udp_datagrams_sent

The udp_datagrams_sent event indicates that one or more UDP datagrams were passed to the network socket. It can show how QUIC packet buffers are drained to the operating system. It has Extra importance level.

QUICUDPDatagramsSent = {

    ; to support passing multiple at once
    ? count: uint16

    ; The RawInfo fields do not include the UDP headers,
    ; only the UDP payload
    ? raw: [+ RawInfo]

    ; ECN bits in the IP header
    ? ecn: [+ ECN]

    ? datagram_payload_checksums: [+ uint32]

    * $$quic-udpdatagramssent-extension
}
Figure 19: QUICUDPDatagramsSent definition

The RawInfo fields exclude UDP headers, because QUIC implementations do not always handle UDP directly.

If an event omits the ecn field, the ECN bits are the same as on the last udp_datagrams_sent event.

The datagram_payload_checksums field is described in Section 9.

5.11. udp_datagrams_received

The udp_datagrams_received event indicates that one or more UDP datagrams were received from the network socket. It can show how the operating system passes datagrams to the user-space stack. It has Extra importance level.

QUICUDPDatagramsReceived = {

    ; to support passing multiple at once
    ? count: uint16

    ; The RawInfo fields do not include the UDP headers,
    ; only the UDP payload
    ? raw: [+ RawInfo]

    ; ECN bits in the IP header
    ? ecn: [+ ECN]

    ? datagram_payload_checksums: [+ uint32]

    * $$quic-udpdatagramsreceived-extension
}
Figure 20: QUICUDPDatagramsReceived definition

If an event omits the ecn field, the ECN bits are the same as on the last udp_datagrams_received event.

The datagram_payload_checksums field is described in Section 9.

5.12. udp_datagram_dropped

The udp_datagram_dropped event indicates that a UDP datagram was dropped, typically because it does not contain a valid QUIC packet. If the datagram contains a valid QUIC packet that is dropped for another reason, the packet_dropped event (Section 5.7) is used instead. It has Extra importance level.

QUICUDPDatagramDropped = {

    ; The RawInfo fields do not include the UDP headers,
    ; only the UDP payload
    ? raw: RawInfo

    * $$quic-udpdatagramdropped-extension
}
Figure 21: QUICUDPDatagramDropped definition

5.13. stream_state_updated

The stream_state_updated event is emitted when the state of a QUIC stream changes; see Section 3 of [QUIC-TRANSPORT]. Most state changes can be inferred from the frames that are sent and received, but an explicit event can be easier to use. It has Base importance level.

QUIC stream IDs encode whether a stream is unidirectional or bidirectional (see Section 2.1 of [QUIC-TRANSPORT]), but the optional stream_type field states it directly.

Section 3 of [QUIC-TRANSPORT] describes the sending and receiving parts of a stream, each with its own state machine. The stream_side field says which part's state changed. If both parts change state at the same time, for example when both become "closed", two events with different stream_side values SHOULD be logged.

StreamType = "unidirectional" /
             "bidirectional"

QUICStreamStateUpdated = {
    stream_id: uint64
    ? stream_type: StreamType
    ? old: $StreamState
    new: $StreamState
    stream_side: "sending" /
                 "receiving"
    ? trigger:
      ; stream state change was initiated by a local action
      "local" /
      ; stream state change was initiated by a remote action
      "remote"

    * $$quic-streamstateupdated-extension
}

BaseStreamStates =  "idle" /
                    "open" /
                    "closed"

GranularStreamStates =
    ; bidirectional stream states, RFC 9000 Section 3.4.
    "half_closed_local" /
    "half_closed_remote" /
    ; sending-side stream states, RFC 9000 Section 3.1.
    "ready" /
    "send" /
    "data_sent" /
    "reset_sent" /
    ; receive-side stream states, RFC 9000 Section 3.2.
    "receive" /
    "size_known" /
    "data_read" /
    "reset_read" /
    ; both-side states
    "data_received" /
    "reset_received" /
    ; qlog-defined: memory actually freed
    "destroyed"

$StreamState /= BaseStreamStates / GranularStreamStates
Figure 22: QUICStreamStateUpdated definition

QUIC implementations SHOULD log the BaseStreamStates, and can add the GranularStreamStates when more in-depth debugging is required. Tools SHOULD handle both kinds equally.

The trigger field can show which endpoint initiated a state change, for example when a stream is closed because of a RESET_STREAM or STOP_SENDING frame.

5.15. stream_data_moved

The stream_data_moved event indicates that QUIC stream data moved between layers. It has Base importance level.

For example, data can move from an application protocol such as HTTP/3 to QUIC stream buffers, or the other way. Logging these moves can show the flow of data, how long data stays in each buffer, and the overhead introduced by each layer.

This event can also show whether received stream data is passed to the application protocol immediately or in batches. Data can be passed, for example, once per received packet, or in a batch after all received packets are processed. This can help identify bottlenecks, flow control issues, or scheduling problems.

The raw.length field gives the number of bytes moved. Because this event covers stream data only, there are no packet or frame headers, and raw.length MUST reflect that.

The additional_info field can log information about the stream state. For example, an application that moves data into the transport and ends the stream at the same time can log "fin_set". A transport that has received an instruction to reset a stream can tell the application with "stream_reset". In both cases, raw.length can be omitted or be zero.

This event covers only data in QUIC streams. For data in QUIC datagrams, see the datagram_data_moved event (Section 5.16).

QUICStreamDataMoved = {
    ? stream_id: uint64
    ? offset: uint64

    ? from: $DataLocation
    ? to: $DataLocation

    ? additional_info: $DataMovedAdditionalInfo

    ? raw: RawInfo

    * $$quic-streamdatamoved-extension
}

$DataLocation /=  "application" /
                  "transport" /
                  "network"

$DataMovedAdditionalInfo /= "fin_set" /
                            "stream_reset"
Figure 25: QUICStreamDataMoved definition

5.16. datagram_data_moved

The datagram_data_moved event indicates that QUIC datagram data (see [QUIC-DATAGRAM]) moved between layers. It has Base importance level.

For example, data can move from an application protocol such as WebTransport to QUIC buffers, or the other way. Logging these moves can show the flow of data, how long data stays in each buffer, and the overhead introduced by each layer.

This event can also show whether received datagram data is passed to the application protocol immediately or in batches. Data can be passed, for example, once per received packet, or in a batch after all received packets are processed. This can help identify bottlenecks or scheduling problems.

The raw.length field gives the number of bytes moved. Because this event covers datagram data only, there are no packet or frame headers, and raw.length MUST reflect that.

This event covers only data in QUIC datagrams. For data in QUIC streams, see the stream_data_moved event (Section 5.15).

QUICDatagramDataMoved = {
    ? from: $DataLocation
    ? to: $DataLocation
    ? raw: RawInfo

    * $$quic-datagramdatamoved-extension
}
Figure 26: QUICDatagramDataMoved definition

5.17. datagram_dropped

The datagram_dropped event indicates that QUIC DATAGRAM data (see [RFC9221]) was discarded locally, before sending or after reception. It has Base importance level.

The direction field identifies the sending or receiving side, and trigger indicates the reason for discarding the data. The raw.length field MUST count discarded payload bytes, excluding packet and frame headers.

Received DATAGRAM data can be discarded while its packet is still acknowledged. For drops of entire QUIC packets, use packet_dropped (Section 5.7).

QUICDatagramDropped = {
    ? direction: "sending" /
                 "receiving"
    ? raw: RawInfo
    ? trigger:
        ; insufficient space in a send or receive buffer
        "buffer_full" /
        ; datagram data exceeds the maximum payload size
        "too_large" /
        text

    * $$quic-datagramdropped-extension
}
Figure 27: QUICDatagramDropped definition

5.18. connection_data_blocked_updated

The connection_data_blocked_updated event indicates that the QUIC connection became blocked or unblocked for sending data. It has Extra importance level.

Depending on the reason, a blocked connection cannot send stream data, datagram data, or both, until that reason is resolved. The stream_data_blocked_updated and datagram_data_blocked_updated events give more detail for each type of data.

QUICConnectionDataBlockedUpdated = {
    ? old: $BlockedState
    new: $BlockedState

    ? reason: $BlockedReason

    * $$quic-connectiondatablockedupdated-extension
}

$BlockedState /= "blocked" /
               "unblocked"

$BlockedReason /= "scheduling" /
                  "pacing" /
                  "amplification_protection" /
                  "congestion_control" /
                  "connection_flow_control" /
                  "stream_flow_control" /
                  "stream_id" /
                  "application"
Figure 28: QUICConnectionDataBlockedUpdated definition

5.19. stream_data_blocked_updated

The stream_data_blocked_updated event indicates that a QUIC stream became blocked or unblocked for sending. It has Extra importance level.

QUICStreamDataBlockedUpdated = {
    ? old: $BlockedState
    new: $BlockedState

    stream_id: uint64

    ? reason: $BlockedReason

    * $$quic-streamdatablockedupdated-extension
}
Figure 29: QUICStreamDataBlockedUpdated definition

5.20. datagram_data_blocked_updated

The datagram_data_blocked_updated event indicates that QUIC datagrams became blocked or unblocked for sending. It has Extra importance level.

QUICDatagramDataBlockedUpdated = {
    ? old: $BlockedState
    new: $BlockedState

    ? reason: $BlockedReason

    * $$quic-datagramdatablockedupdated-extension
}
Figure 30: QUICDatagramDataBlockedUpdated definition

5.21. migration_state_updated

The migration_state_updated event logs the progress of a connection migration by a client; see Section 9 of [QUIC-TRANSPORT]. It has Extra importance level.

Most details of a migration can be inferred from PATH_CHALLENGE and PATH_RESPONSE frames, together with tuple_assigned events. However, it can be useful to log the progress of the migration, and any decisions made, in a single event.

The MigrationState values follow the steps in Section 9 of [QUIC-TRANSPORT]: the probing_* values cover probing a new path (Section 9.1 of [QUIC-TRANSPORT]), and the migration_* values cover migrating the connection (Section 9.2 of [QUIC-TRANSPORT]).

Implementations that log per-path information in migration_state_updated events SHOULD also emit tuple_assigned events, to serve as the ground truth for tuple information.

QUICMigrationStateUpdated = {
    ? old: MigrationState
    new: MigrationState

    ? tuple_id: TupleID

    ; the information for traffic going towards the remote receiver
    ? tuple_remote: TupleEndpointInfo

    ; the information for traffic coming in at the local endpoint
    ? tuple_local: TupleEndpointInfo

    * $$quic-migrationstateupdated-extension
}

; Note that MigrationState does not describe a full state machine
; These entries are not necessarily chronological,
; nor will they always all appear during
; a connection migration attempt.
MigrationState =
    ; probing packets are sent, migration not initiated yet
    "probing_started" /
    ; did not get reply to probing packets,
    ; discarding path as an option
    "probing_abandoned" /
    ; received reply to probing packets, path is migration candidate
    "probing_successful" /
    ; non-probing packets are sent, attempting migration
    "migration_started" /
    ; something went wrong during the migration, abandoning attempt
    "migration_abandoned" /
    ; new path is now fully used, old path is discarded
    "migration_complete"
Figure 31: QUICMigrationStateUpdated definition

5.22. timer_updated

The timer_updated event is emitted when a timer changes state. It has Extra importance level.

The event_type field takes one of three values:

  • "set": the timer is set, and delta gives the time until it triggers
  • "expired": the timer expired
  • "cancelled": the timer was cancelled

An update to an active timer's timeout is logged as a new "set" event.

The recovery timer types correspond to timers in Section 6 of [QUIC-RECOVERY] and Appendix A.8 of [QUIC-RECOVERY]:

  • "ack": the time threshold loss detection timeout
  • "pto": the probe timeout
  • "loss_timeout": the single, multi-modal loss detection timer that covers both

QUIC uses a variety of timers, and how they are used can depend on the implementation. Implementers can add fields to this event through $$quic-timerupdated-extension, or add timer types through $TimerType.

; a non-exhaustive list of typically employed timers
$TimerType /= "ack" /
              "pto" /
              "loss_timeout" /
              "path_validation" /
              "handshake_timeout" /
              "idle_timeout"

QUICTimerUpdated = {
    ? timer_type: $TimerType

    ; to disambiguate in case there are multiple timers
    ; of the same type
    ? timer_id: uint64

    ; if used for recovery timers, this can be useful information
    ? packet_number_space: $PacketNumberSpace
    event_type: "set" /
                "expired" /
                "cancelled"

    ; if event_type === "set": delta time is in ms from
    ; this event's timestamp until when the timer should trigger
    ? delta: float32

    * $$quic-timerupdated-extension
}
Figure 32: QUICTimerUpdated definition

6. Security Events

6.1. key_updated

The key_updated event indicates that a key was installed or updated. It has Base importance level.

This happens when TLS provides new keys during the handshake, and at each 1-RTT key update; see Section 6 of [QUIC-TLS].

QUICKeyUpdated = {
    key_type: $KeyType
    ? old: hexstring
    ? new: hexstring

    ; needed for 1RTT key updates
    ? key_phase: uint64
    ? trigger:
        ; (e.g., initial, handshake and 0-RTT keys
        ; are generated by TLS)
        "tls" /
        "remote_update" /
        "local_update"

    * $$quic-keyupdated-extension
}
Figure 33: QUICKeyUpdated definition

The key_phase field holds the full key phase, which identifies the generation of keys; it corresponds to @M and @N in Figure 9 of Section 6 of [QUIC-TLS]. The Key Phase bit in the packet header is the least significant bit of this value.

6.2. key_discarded

The key_discarded event indicates that a key was discarded. It has Base importance level.

Keys are discarded, for example, when the connection moves to a new encryption level during the handshake, or after a key update; see Section 4.9 of [QUIC-TLS].

QUICKeyDiscarded = {
    key_type: $KeyType
    ? key: hexstring

    ; needed for 1RTT key updates
    ? key_phase: uint64
    ? trigger:
        ; (e.g., initial, handshake and 0-RTT keys
        ; are generated by TLS)
        "tls" /
        "remote_update" /
        "local_update"

    * $$quic-keydiscarded-extension
}
Figure 34: QUICKeyDiscarded definition

7. Recovery Events

Most of the events in this section are kept generic to support different recovery approaches and congestion control algorithms. The old, new, and trigger fields of congestion_state_updated, for instance, are free text, so their values depend on the algorithm.

Tools that process these events SHOULD try to handle such values even when they don't recognize them. For example, a tool that plots congestion states on a timeline can label an unknown state with the value of the new field.

7.1. recovery_parameters_set

The recovery_parameters_set event groups initial parameters from both loss detection and congestion control into a single event. It has Base importance level.

These parameters are typically set once and never change. Implementations that change them during the connection MAY emit the recovery_parameters_set event more than once.

The reordering_threshold field contains the packet threshold for loss detection, or "disabled" if packet-threshold loss detection is disabled.

QUICRecoveryParametersSet = {

    ; Loss detection, see RFC 9002 Appendix A.2
    ; in amount of packets
    ? reordering_threshold: uint16 / "disabled"

    ; as RTT multiplier
    ? time_threshold: float32

    ; in ms
    timer_granularity: uint16

    ; in ms
    ? initial_rtt:float32

    ; congestion control, see RFC 9002 Appendix B.2
    ; in bytes. Note that this could be updated after pmtud
    ? max_datagram_size: uint32

    ; in bytes
    ? initial_congestion_window: uint64

    ; Note that this could change when max_datagram_size changes
    ; in bytes
    ? minimum_congestion_window: uint64
    ? loss_reduction_factor: float32

    ; as PTO multiplier
    ? persistent_congestion_threshold: uint16

    * $$quic-recoveryparametersset-extension
}
Figure 35: QUICRecoveryParametersSet definition

Parameters that this document does not define, such as those of other loss detection and recovery algorithms, can be added through $$quic-recoveryparametersset-extension.

7.2. recovery_metrics_updated

The recovery_metrics_updated event is emitted when one or more of the observable recovery metrics change value. It has Core importance level.

Implementations SHOULD group all metric updates that happen at or around the same time into a single recovery_metrics_updated event. For example, if min_rtt and smoothed_rtt change at the same time, they are logged in one event, not two. Each event is only guaranteed to contain at least one of the listed metrics.

QUICRecoveryMetricsUpdated = {

    ; Loss detection, see RFC 9002 Appendix A.3
    ; all following rtt fields are expressed in ms
    ? min_rtt: float32
    ? smoothed_rtt: float32
    ? latest_rtt: float32
    ? rtt_variance: float32
    ? pto_count: uint16

    ; Congestion control, see RFC 9002 Appendix B.2.
    ; in bytes
    ? congestion_window: uint64
    ? bytes_in_flight: uint64

    ; in bytes
    ? ssthresh: uint64

    ; qlog defined
    ; sum of all packet number spaces
    ? packets_in_flight: uint64

    ; in bits per second
    ? pacing_rate: uint64

    * $$quic-recoverymetricsupdated-extension
}
Figure 36: QUICRecoveryMetricsUpdated definition

To make logging easier, implementations MAY log values that are the same as previously reported values; for example, two consecutive recovery_metrics_updated events can report the same min_rtt. However, implementations SHOULD try to log only values that changed.

Metrics that this document does not define, such as those of other loss detection and recovery algorithms, can be added through $$quic-recoverymetricsupdated-extension.

7.3. congestion_state_updated

The congestion_state_updated event indicates when the congestion controller enters a new state that changes its behavior. It has Base importance level.

This document does not define values for the event's fields. Congestion control algorithms have different states and different causes for moving between them, and stacks can implement the same algorithm differently. For example, the NewReno controller in Section 7.3 of [QUIC-RECOVERY] has three states: slow start, recovery, and congestion avoidance.

QUICCongestionStateUpdated = {
    ? old: text
    new: text
    ? trigger: text

    * $$quic-congestionstateupdated-extension
}
Figure 37: QUICCongestionStateUpdated definition

The trigger field SHOULD be logged if a state change can have more than one cause. For example, NewReno enters recovery when it detects packet loss or when the ECN-CE count increases, so the trigger field shows which of the two caused the change.

7.4. packet_lost

The packet_lost event is emitted when loss detection declares a packet lost. It has Core importance level.

QUICPacketLost = {

    ; should include at least the packet_type and packet_number
    ? header: PacketHeader

    ? frames: [* $QuicFrame]
    ? is_mtu_probe_packet: bool
    ? trigger:
        "reordering_threshold" /
        "time_threshold" /
        "pto_expired"

    * $$quic-packetlost-extension
}
Figure 38: QUICPacketLost definition

The frames field lists the frames that the lost packet carried. It is optional, because not all implementations keep sent packets in full. The data that is then marked for retransmission can be logged with the marked_for_retransmit event (Section 7.5).

The is_mtu_probe_packet field marks a packet that was sent as a Path MTU probe. The loss of such a packet is not a reliable sign of congestion; see Section 14.4 of [QUIC-TRANSPORT]. It defaults to false.

The trigger field SHOULD be logged. Its value shows how loss detection declared the packet lost:

7.5. marked_for_retransmit

The marked_for_retransmit event indicates which data was marked for retransmission after packet loss was detected. It has Extra importance level.

This single event covers all types of retransmittable data, which it logs using the existing QUIC frame definitions.

A packet_lost event (Section 7.4) records that a packet was declared lost, and can list the frames it carried. A marked_for_retransmit event records what the implementation then marks for retransmission. The two can differ, because not every frame is retransmitted (ACK and PADDING frames never are), and retransmitted data need not be sent in the same frames; see Section 13.3 of [QUIC-TRANSPORT].

An implementation that retransmits the frames of a lost packet unchanged can log them here, or skip this event and use the frames in the packet_lost event instead. An implementation with more complex logic, such as one that marks ranges of a stream's send buffer as in flight, or one that does not keep sent frames in full, can log frames that describe the data it marked for retransmission. These frames need not match any frame that was sent or will be sent.

If an implementation logs packet_sent events, tools can infer much of this data from them. For example, overlapping stream offsets and lengths show when data was retransmitted.

QUICMarkedForRetransmit = {
    frames: [+ $QuicFrame]

    * $$quic-markedforretransmit-extension
}
Figure 39: QUICMarkedForRetransmit definition

7.6. ecn_state_updated

The ecn_state_updated event indicates a progression in the ECN state machine as described in Appendix A.4 of [QUIC-TRANSPORT]. It has Extra importance level.

QUICECNStateUpdated = {
   ? old: ECNState
    new: ECNState
    ? trigger: text

    * $$quic-ecnstateupdated-extension
}

ECNState =
  ; ECN testing in progress
  "testing" /
  ; ECN state unknown, waiting for acknowledgements
  ; for testing packets
  "unknown" /
  ; ECN testing failed
  "failed" /
  ; testing was successful
  "capable"
Figure 40: QUICECNStateUpdated definition

The trigger field can be used to log why the state changed, for example which ECN validation check in Section 13.4.2 of [QUIC-TRANSPORT] failed.

8. QUIC Data Type Definitions

8.3. Initiator

Initiator = "local" /
            "remote"
Figure 43: Initiator definition

8.4. IPAddressPort

; an IP address can either be a "human readable" form
; (e.g., "127.0.0.1" for v4 or
; "2001:0db8:85a3:0000:0000:8a2e:0370:7334" for v6) or
; use a raw byte-form (as the string forms can be ambiguous).
; Additionally, the IP address and port pair can be logged as an
; application-defined redacted string for privacy or security reasons.
IPAddressPort = {
    ip: text / hexstring
    port: uint16
} / text
Figure 44: IPAddressPort definition

8.5. TupleEndpointInfo

TupleEndpointInfo describes one half, or direction, of a four-tuple. A full tuple has two halves: the server sends to the client's IP address and port, and the client sends to the server's IP address and port. Each direction uses its own destination connection ID.

Structures that log tuple information SHOULD therefore include two TupleEndpointInfo instances, one for each half of the tuple.

TupleEndpointInfo = {
    ? address_v4: IPAddressPort
    ? address_v6: IPAddressPort

    ; Even though usually only a single ConnectionID
    ; is associated with a given tuple/path at a time,
    ; there are situations where there can be an overlap
    ; or a need to keep track of previous ConnectionIDs
    ? connection_ids: [+ ConnectionID]

    * $$quic-tupleendpointinfo-extension
}
Figure 45: TupleEndpointInfo definition

8.6. PacketType

$PacketType /=  "initial" /
                "handshake" /
                "0RTT" /
                "1RTT" /
                "retry" /
                "version_negotiation" /
                "stateless_reset" /
                "unknown"
Figure 46: PacketType definition

8.7. PacketNumberSpace

$PacketNumberSpace /= "initial" /
                      "handshake" /
                      "application_data"
Figure 47: PacketNumberSpace definition

8.8. PacketHeader

If a packet type has no known $PacketType string, packet_type can be "unknown", and packet_type_bytes then holds the numerical value without variable-length integer encoding.

PacketHeader omits the fixed bit and the reserved bits because QUIC version 1 gives them fixed values; see Section 17 of [QUIC-TRANSPORT]. Their values can be logged in the raw.data field of the event that logs the PacketHeader.

QUIC extensions that use these bits, such as the QUIC Bit greasing extension ([GREASEBIT]), can log that use with new events, similar to spin_bit_updated, or with fields added to PacketHeader through $$quic-packetheader-extension.

For long header packets of type initial, handshake, and 0RTT, the length field of the packet header is logged in the qlog raw.length field, and the value signifies the length of the packet number plus the payload.

PacketHeader = {
    packet_type: $PacketType

    ; only if packet_type === "unknown"
    ? packet_type_bytes: uint64

    ; only if packet_type === "1RTT"
    ? spin_bit: bool

    ; only if packet_type === "1RTT", and if the key phase was
    ; determined from the key_phase_bit
    ? key_phase: uint64

    ; only if packet_type === "1RTT", and if key_phase is not set
    ? key_phase_bit: bool

    ; only if packet_type === "initial" || "handshake" || "0RTT" ||
    ;                         "1RTT"
    ? packet_number_length: uint8

    ; only if packet_type === "initial" || "handshake" || "0RTT" ||
    ;                         "1RTT"
    ? packet_number: uint64

    ; only if packet_type === "initial" || "retry"
    ? token: Token

    ; only if present in the header.
    ; if correctly using connection_id_updated events,
    ; dcid can be skipped for 1RTT packets
    ? version: QuicVersion
    ? scil: uint8
    ? dcil: uint8
    ? scid: ConnectionID
    ? dcid: ConnectionID

    * $$quic-packetheader-extension
}
Figure 48: PacketHeader definition

8.9. Token

Token = {
    ? type: $TokenType

    ; decoded fields included in the token
    ; (typically: peer's IP address, creation time)
    ? details: {
      * text => any
    }
    ? raw: RawInfo

    * $$quic-token-extension
}

$TokenType /= "retry" /
              "resumption"
Figure 49: Token definition

The token carried in an Initial packet is either a token from a Retry packet, or one that the server provided earlier in a NEW_TOKEN frame, for address validation in a later connection; see Section 8.1.3 of [QUIC-TRANSPORT]. Both kinds typically encode metadata that the server uses to check the token, but the metadata and its format are implementation-specific. The details field can log that metadata.

8.10. StatelessResetToken

; always 16 bytes
StatelessResetToken = hexstring
Figure 50: StatelessResetToken definition

The stateless reset token is defined in Section 10.3 of [QUIC-TRANSPORT].

8.11. KeyType

$KeyType /= "server_initial_secret" /
            "client_initial_secret" /
            "server_handshake_secret" /
            "client_handshake_secret" /
            "server_0rtt_secret" /
            "client_0rtt_secret" /
            "server_1rtt_secret" /
            "client_1rtt_secret"
Figure 51: KeyType definition

8.12. ECN

ECN holds the ECN codepoint carried in the IP header; see Section 13.4 of [QUIC-TRANSPORT].

ECN = "Not-ECT" / "ECT(1)" / "ECT(0)" / "CE"
Figure 52: ECN definition

8.13. QUIC Frames

The generic $QuicFrame is defined here as a CDDL "type socket" extension point. It can be extended to support additional QUIC frame types.

; The QuicFrame is any key-value map (e.g., JSON object)
$QuicFrame /= {
    * text => any
}
Figure 53: QuicFrame type socket definition

The QUIC frame types defined in this document are as follows:

QuicBaseFrames =  PaddingFrame /
                  PingFrame /
                  AckFrame /
                  ResetStreamFrame /
                  ResetStreamAtFrame /
                  StopSendingFrame /
                  CryptoFrame /
                  NewTokenFrame /
                  StreamFrame /
                  MaxDataFrame /
                  MaxStreamDataFrame /
                  MaxStreamsFrame /
                  DataBlockedFrame /
                  StreamDataBlockedFrame /
                  StreamsBlockedFrame /
                  NewConnectionIDFrame /
                  RetireConnectionIDFrame /
                  PathChallengeFrame /
                  PathResponseFrame /
                  ConnectionCloseFrame /
                  HandshakeDoneFrame /
                  UnknownFrame /
                  DatagramFrame

$QuicFrame /= QuicBaseFrames
Figure 54: QuicBaseFrames definition

8.13.1. PaddingFrame

A PADDING frame is a single byte with the value 0, so each padding byte is its own frame; see Section 19.1 of [QUIC-TRANSPORT]. Logging each one as a separate PaddingFrame could add hundreds of entries to a single packet event, for example when a client pads an Initial packet to 1200 bytes. Implementations SHOULD instead log a single PaddingFrame for the packet, and set its raw.payload_length field to the number of PADDING frames in the packet.

PaddingFrame = {
    frame_type: "padding"
    ? raw: RawInfo
}
Figure 55: PaddingFrame definition

8.13.2. PingFrame

PingFrame = {
    frame_type: "ping"
    ? raw: RawInfo
}
Figure 56: PingFrame definition

8.13.3. AckFrame

; either a single number (e.g., [1]) or two numbers (e.g., [1,2]).
; For two numbers:
; the first number is "from": lowest packet number in interval
; the second number is "to": up to and including the highest
; packet number in the interval
AckRange = [1*2 uint64]

AckFrame = {
    frame_type: "ack"

    ; in ms
    ? ack_delay: float32

    ; e.g., looks like [[1,2],[4,5], [7], [10,22]] serialized
    ? acked_ranges: [+ AckRange]

    ; ECN (explicit congestion notification) related fields
    ; (not always present)
    ? ect1: uint64
    ? ect0: uint64
    ? ce: uint64
    ? raw: RawInfo
}
Figure 57: AckFrame definition

The ranges in acked_ranges do not have to be in order; for example, [[5,9],[1,4]] is valid.

The two numbers in a range can be equal; for example, [120,120] means that packet 120 was acknowledged. In that case, implementations SHOULD log [120] instead, and tools MUST handle both forms.

8.13.4. ResetStreamFrame

If an error code has no known ApplicationError string, error can be "unknown", and error_code then holds the numerical value without variable-length integer encoding.

ResetStreamFrame = {
    frame_type: "reset_stream"
    stream_id: uint64
    error: $ApplicationError

    ; if error === "unknown"
    ? error_code: uint64

    ; in bytes
    final_size: uint64
    ? raw: RawInfo
}
Figure 58: ResetStreamFrame definition

8.13.5. ResetStreamAtFrame

The RESET_STREAM_AT frame is defined in Section 4 of [RESET-STREAM-AT].

If an error code has no known ApplicationError string, error can be "unknown", and error_code then holds the numerical value without variable-length integer encoding.

ResetStreamAtFrame = {
    frame_type: "reset_stream_at"
    stream_id: uint64
    error: $ApplicationError

    ; if error === "unknown"
    ? error_code: uint64

    ; in bytes
    final_size: uint64

    ; in bytes
    reliable_size: uint64

    ? raw: RawInfo
}
Figure 59: ResetStreamAtFrame definition

8.13.6. StopSendingFrame

If an error code has no known ApplicationError string, error can be "unknown", and error_code then holds the numerical value without variable-length integer encoding.

StopSendingFrame = {
    frame_type: "stop_sending"
    stream_id: uint64
    error: $ApplicationError

    ; if error === "unknown"
    ? error_code: uint64

    ? raw: RawInfo
}
Figure 60: StopSendingFrame definition

8.13.7. CryptoFrame

The length field of the CRYPTO frame MUST be logged in the qlog raw.length field. The other sub-fields of the raw field are optional.

CryptoFrame = {
    frame_type: "crypto"
    offset: uint64
    raw: RawInfo
}
Figure 61: CryptoFrame definition

8.13.8. NewTokenFrame

NewTokenFrame = {
  frame_type: "new_token"
  token: Token
  ? raw: RawInfo
}
Figure 62: NewTokenFrame definition

8.13.9. StreamFrame

If the STREAM frame contains a length field, it MUST be logged in the qlog raw.length field. If it does not, the implementation MAY calculate the actual frame byte length itself and log that in raw.length if necessary.

The offset field defaults to 0, and the fin field defaults to false.

StreamFrame = {
    frame_type: "stream"
    stream_id: uint64
    ? offset: uint64
    ? fin: bool
    ? raw: RawInfo
}
Figure 63: StreamFrame definition

8.13.10. MaxDataFrame

MaxDataFrame = {
  frame_type: "max_data"
  maximum: uint64
  ? raw: RawInfo
}
Figure 64: MaxDataFrame definition

8.13.11. MaxStreamDataFrame

MaxStreamDataFrame = {
  frame_type: "max_stream_data"
  stream_id: uint64
  maximum: uint64
  ? raw: RawInfo
}
Figure 65: MaxStreamDataFrame definition

8.13.12. MaxStreamsFrame

MaxStreamsFrame = {
  frame_type: "max_streams"
  stream_type: StreamType
  maximum: uint64
  ? raw: RawInfo
}
Figure 66: MaxStreamsFrame definition

8.13.13. DataBlockedFrame

DataBlockedFrame = {
  frame_type: "data_blocked"
  limit: uint64
  ? raw: RawInfo
}
Figure 67: DataBlockedFrame definition

8.13.14. StreamDataBlockedFrame

StreamDataBlockedFrame = {
  frame_type: "stream_data_blocked"
  stream_id: uint64
  limit: uint64
  ? raw: RawInfo
}
Figure 68: StreamDataBlockedFrame definition

8.13.15. StreamsBlockedFrame

StreamsBlockedFrame = {
  frame_type: "streams_blocked"
  stream_type: StreamType
  limit: uint64
  ? raw: RawInfo
}
Figure 69: StreamsBlockedFrame definition

8.13.16. NewConnectionIDFrame

NewConnectionIDFrame = {
  frame_type: "new_connection_id"
  sequence_number: uint64
  retire_prior_to: uint64

  ; mainly used if e.g., for privacy reasons the full
  ; connection_id cannot be logged
  ? connection_id_length: uint8
  connection_id: ConnectionID
  ? stateless_reset_token: StatelessResetToken
  ? raw: RawInfo
}
Figure 70: NewConnectionIDFrame definition

8.13.17. RetireConnectionIDFrame

RetireConnectionIDFrame = {
  frame_type: "retire_connection_id"
  sequence_number: uint64
  ? raw: RawInfo
}
Figure 71: RetireConnectionIDFrame definition

8.13.18. PathChallengeFrame

PathChallengeFrame = {
  frame_type: "path_challenge"

  ; always 8 bytes
  ? data: hexstring
  ? raw: RawInfo
}
Figure 72: PathChallengeFrame definition

8.13.19. PathResponseFrame

PathResponseFrame = {
  frame_type: "path_response"

  ; always 8 bytes
  ? data: hexstring
  ? raw: RawInfo
}
Figure 73: PathResponseFrame definition

8.13.20. ConnectionCloseFrame

If an error code has no known string, error can be "unknown", and error_code then holds the numerical value without variable-length integer encoding.

The trigger_frame_type field logs the Frame Type field, which only the transport variant of CONNECTION_CLOSE (type 0x1c) carries; see Section 19.19 of [QUIC-TRANSPORT]. A known frame type is logged as its frame type string, such as "stream". An unknown frame type is logged as the numerical value without variable-length integer encoding.

The reason phrase in a CONNECTION_CLOSE frame is a byte sequence, which RFC 9000 recommends be UTF-8; see Section 19.19 of [QUIC-TRANSPORT]. The reason field holds the reason phrase as text, and reason_bytes holds the raw bytes, for example when the value is not UTF-8 or when an endpoint does not want to decode it. Implementations SHOULD log at least one form and MAY log both.

ErrorSpace = "transport" /
             "application"

ConnectionCloseFrame = {
    frame_type: "connection_close"
    ? error_space: ErrorSpace
    ? error: $TransportError / CryptoError /
             $ApplicationError

    ; if error === "unknown"
    ? error_code: uint64

    ? reason: text
    ? reason_bytes: hexstring

    ; when error_space === "transport"
    ? trigger_frame_type: uint64 /
                          text
    ? raw: RawInfo
}
Figure 74: ConnectionCloseFrame definition

8.13.21. HandshakeDoneFrame

HandshakeDoneFrame = {
  frame_type: "handshake_done"
  ? raw: RawInfo
}
Figure 75: HandshakeDoneFrame definition

8.13.22. UnknownFrame

The frame_type_bytes field holds the numerical value of the frame type without variable-length integer encoding.

UnknownFrame = {
    frame_type: "unknown"
    frame_type_bytes: uint64
    ? raw: RawInfo
}
Figure 76: UnknownFrame definition

8.13.23. DatagramFrame

The QUIC DATAGRAM frame is defined in Section 4 of [QUIC-DATAGRAM].

If the DATAGRAM frame contains a length field, it MUST be logged in the qlog raw.length field. If it does not, the implementation MAY calculate the actual datagram byte length itself and log that in raw.length if necessary.

DatagramFrame = {
    frame_type: "datagram"
    ? raw: RawInfo
}
Figure 77: DatagramFrame definition

8.14. TransportError

The generic $TransportError is defined here as a CDDL "type socket" extension point. It can be extended to support additional transport errors.

$TransportError /= "no_error" /
                 "internal_error" /
                 "connection_refused" /
                 "flow_control_error" /
                 "stream_limit_error" /
                 "stream_state_error" /
                 "final_size_error" /
                 "frame_encoding_error" /
                 "transport_parameter_error" /
                 "connection_id_limit_error" /
                 "protocol_violation" /
                 "invalid_token" /
                 "application_error" /
                 "crypto_buffer_exceeded" /
                 "key_update_error" /
                 "aead_limit_reached" /
                 "no_viable_path" /
                 "unknown"
                 ; there is no value to reflect CRYPTO_ERROR
                 ; use the CryptoError type instead
Figure 78: TransportError definition

8.15. ApplicationError

Application errors are defined by the application protocol that runs on top of QUIC, such as HTTP/3, so this document cannot list them. $ApplicationError is instead defined as a CDDL "type socket" extension point, with a single "unknown" value.

$ApplicationError /= "unknown"
Figure 79: ApplicationError definition

Event schemas for application protocols MUST add their error strings by extending the $ApplicationError socket; see Section 8.2 of [QLOG-MAIN].

8.16. CryptoError

RFC 9001 converts a TLS alert into a QUIC error code by adding the alert's AlertDescription value to 0x0100; see Section 4.8 of [QUIC-TLS].

A fixed list of strings does not fit this range well, so a CryptoError value is crypto_error_ followed by the resulting QUIC error code in lowercase hexadecimal, from crypto_error_0x100 to crypto_error_0x1ff. For example, a handshake_failure alert, which is 0x0128 in QUIC, is logged as crypto_error_0x128.

; all strings from "crypto_error_0x100" to "crypto_error_0x1ff"
CryptoError = text .regexp "crypto_error_0x1[0-9a-f][0-9a-f]"
Figure 80: CryptoError definition

9. Handling Packet Coalescing

Multiple QUIC packets can be coalesced into a single UDP datagram, especially during the handshake; see Section 12.2 of [QUIC-TRANSPORT]. Neither QUIC nor UDP provides a way to tell which packets shared a datagram.

To let tools match QUIC packet events to UDP datagram events, the optional datagram_payload_checksum field carries a 32-bit CRC32c of the UDP payload, calculated as described in Section 11.3 of [RFC9868]. The UDP datagram events carry one checksum per datagram, in their datagram_payload_checksums field.

10. Security and Privacy Considerations

The security and privacy considerations discussed in Section 14 of [QLOG-MAIN] apply to this document as well.

11. IANA Considerations

This document registers a new entry in the "qlog event schema URIs" registry, which was created in Section 15 of [QLOG-MAIN]:

Event schema URI:

urn:ietf:params:qlog:events:quic

Namespace:

quic

Event Types:

server_listening, connection_started, connection_closed, connection_id_updated, spin_bit_updated, connection_state_updated, tuple_assigned, mtu_updated, version_information, alpn_information, parameters_set, parameters_restored, packet_sent, packet_received, packet_dropped, packet_buffered, packets_acked, udp_datagrams_sent, udp_datagrams_received, udp_datagram_dropped, stream_state_updated, frames_created, frames_parsed, stream_data_moved, datagram_data_moved, datagram_dropped, connection_data_blocked_updated, stream_data_blocked_updated, datagram_data_blocked_updated, migration_state_updated, timer_updated, key_updated, key_discarded, recovery_parameters_set, recovery_metrics_updated, congestion_state_updated, packet_lost, marked_for_retransmit, ecn_state_updated

Description:

Event definitions related to the QUIC transport protocol.

Reference:

This document

12. References

12.1. Normative References

[CDDL]
Birkholz, H., Vigano, C., and C. Bormann, "Concise Data Definition Language (CDDL): A Notational Convention to Express Concise Binary Object Representation (CBOR) and JSON Data Structures", RFC 8610, DOI 10.17487/RFC8610, , <https://www.rfc-editor.org/rfc/rfc8610>.
[GREASEBIT]
Thomson, M., "Greasing the QUIC Bit", RFC 9287, DOI 10.17487/RFC9287, , <https://www.rfc-editor.org/rfc/rfc9287>.
[QLOG-MAIN]
Marx, R., Niccolini, L., Seemann, M., and L. Pardue, "qlog: Structured Logging for Network Protocols", Work in Progress, Internet-Draft, draft-ietf-quic-qlog-main-schema-14, , <https://datatracker.ietf.org/doc/html/draft-ietf-quic-qlog-main-schema-14>.
[QUIC-DATAGRAM]
Pauly, T., Kinnear, E., and D. Schinazi, "An Unreliable Datagram Extension to QUIC", RFC 9221, DOI 10.17487/RFC9221, , <https://www.rfc-editor.org/rfc/rfc9221>.
[QUIC-RECOVERY]
Iyengar, J., Ed. and I. Swett, Ed., "QUIC Loss Detection and Congestion Control", RFC 9002, DOI 10.17487/RFC9002, , <https://www.rfc-editor.org/rfc/rfc9002>.
[QUIC-TLS]
Thomson, M., Ed. and S. Turner, Ed., "Using TLS to Secure QUIC", RFC 9001, DOI 10.17487/RFC9001, , <https://www.rfc-editor.org/rfc/rfc9001>.
[QUIC-TRANSPORT]
Iyengar, J., Ed. and M. Thomson, Ed., "QUIC: A UDP-Based Multiplexed and Secure Transport", RFC 9000, DOI 10.17487/RFC9000, , <https://www.rfc-editor.org/rfc/rfc9000>.
[RESET-STREAM-AT]
Seemann, M. and K. Oku, "QUIC Stream Resets with Partial Delivery", Work in Progress, Internet-Draft, draft-ietf-quic-reliable-stream-reset-11, , <https://datatracker.ietf.org/doc/html/draft-ietf-quic-reliable-stream-reset-11>.
[RFC2119]
Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, , <https://www.rfc-editor.org/rfc/rfc2119>.
[RFC8174]
Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, , <https://www.rfc-editor.org/rfc/rfc8174>.
[RFC9221]
Pauly, T., Kinnear, E., and D. Schinazi, "An Unreliable Datagram Extension to QUIC", RFC 9221, DOI 10.17487/RFC9221, , <https://www.rfc-editor.org/rfc/rfc9221>.
[RFC9868]
Touch, J. and C. Heard, Ed., "Transport Options for UDP", RFC 9868, DOI 10.17487/RFC9868, , <https://www.rfc-editor.org/rfc/rfc9868>.

12.2. Informative References

[RFC7301]
Friedl, S., Popov, A., Langley, A., and E. Stephan, "Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension", RFC 7301, DOI 10.17487/RFC7301, , <https://www.rfc-editor.org/rfc/rfc7301>.

Acknowledgements

Much of the initial work by Robin Marx was done at the Hasselt and KU Leuven Universities.

Thanks to Jana Iyengar, Brian Trammell, Dmitri Tikhonov, Stephen Petrides, Jari Arkko, Marcus Ihlar, Victor Vasiliev, Mirja Kuehlewind, Jeremy Laine, Kazu Yamamoto, Christian Huitema, Hugo Landau, Will Hawkins, Mathis Engelbart, Kazuho Oku, and Jonathan Lennox for their feedback and suggestions.

Change Log

This section is to be removed before publishing as an RFC.

Since draft-ietf-qlog-quic-events-13:

  • Added datagram_dropped (#550)
  • Wide range of editorial and consistency changes

Since draft-ietf-qlog-quic-events-12:

  • Split frames_processed into 3 events (#513)
  • Wide range of editorial changes

Since draft-ietf-qlog-quic-events-11:

  • Updated several fields to be uint64 per QUIC spec
  • Renamed error and error_code fields and logic (#473)
  • Clarified parameters_set usage (#493)
  • Replaced all length fields with raw.length (#495)
  • Change loss_timer_updated to timer_updated (#496)
  • Renamed path_assigned to tuple_assigned (#491)
  • Reworked stream_state_updated (#497)
  • Renamed owner to initiator (#498)
  • Split up flags in PacketHeader (#478)

Since draft-ietf-qlog-quic-events-09:

  • Several editorial changes
  • Reworked QUICConnectionStarted to use PathEndpointInfo (#453)
  • Consistent use of RawInfo and _bytes fields to log raw data (#450)

Since draft-ietf-qlog-quic-events-08:

  • Removed individual categories and put every event in the single quic event schema namespace. Major change (#439)
  • Renamed recovery:metrics_updated to quic:recovery_metrics_updated and recovery:parameters_set to quic:recovery_parameters_set (#439)
  • Added unknown_parameters field to parameters_set (#438)
  • Added extra parameters to parameters_restored (#441)

Since draft-ietf-qlog-quic-events-06:

  • Added PathAssigned and MigrationStateUpdated events (#336)
  • Added extension points to parameters_set and parameters_restored (#400)
  • Removed error_code_value from connection_closed (#386, #392)
  • Renamed generation to key_phase for key_updated and key_discarded (#390)
  • Removed retry_token from packet_sent and packet_received (#389)
  • Updated ALPN handling (#385)
  • Added key_unavailable trigger to packet_dropped (#381)
  • Updated several uint32 to uint64
  • ProtocolEventBody is now called ProtocolEventData (#352)
  • Editorial changes (#402, #404, #394, #393)

Since draft-ietf-qlog-quic-events-05:

  • SecurityKeyUpdated: the new key is no longer mandatory to log (#294)
  • Added ECN related events and metadata (#263)

Since draft-ietf-qlog-quic-events-04:

  • Updated guidance on logging events across connections (#279)
  • Renamed 'transport' category to 'quic' (#302)
  • Added support for multiple packet numbers in 'quic:frames_processed' (#307)
  • Added definitions for RFC9287 (QUIC GREASE Bit extension) (#311)
  • Added definitions for RFC9221 (QUIC Datagram Frame extension) (#310)
  • (Temporarily) removed definitions for connection migration events (#317)
  • Editorial and formatting changes (#298, #299, #304, #306, #327)

Since draft-ietf-qlog-quic-events-03:

  • Ensured consistent use of RawInfo to indicate raw wire bytes (#243)
  • Renamed UnknownFrame:raw_frame_type to :frame_type_value (#54)
  • Renamed ConnectionCloseFrame:raw_error_code to :error_code_value (#54)
  • Changed triggers for packet_dropped (#278)
  • Added entries to TransportError enum (#285)
  • Changed minimum_congestion_window to uint64 (#288)

Since draft-ietf-qlog-quic-events-02:

  • Renamed key_retired to key_discarded (#185)
  • Added fields and events for DPLPMTUD (#135)
  • Made packet_number optional in PacketHeader (#244)
  • Removed connection_retried event placeholder (#255)
  • Changed QuicFrame to a CDDL plug type (#257)
  • Moved data definitions out of the appendix into separate sections
  • Added overview Table of Contents

Since draft-ietf-qlog-quic-events-01:

  • Added Stateless Reset Token type (#122)

Since draft-ietf-qlog-quic-events-00:

  • Change the data definition language from TypeScript to CDDL (#143)

Since draft-marx-qlog-event-definitions-quic-h3-02:

  • These changes were done in preparation of the adoption of the drafts by the QUIC working group (#137)
  • Split QUIC and HTTP/3 events into two separate documents
  • Moved RawInfo, Importance, Generic events and Simulation events to the main schema document.
  • Changed to/from value options of the data_moved event

Since draft-marx-qlog-event-definitions-quic-h3-01:

Major changes:

  • Moved data_moved from http to transport. Also made the "from" and "to" fields flexible strings instead of an enum (#111,#65)
  • Moved packet_type fields to PacketHeader. Moved packet_size field out of PacketHeader to RawInfo:length (#40)
  • Made events that need to log packet_type and packet_number use a header field instead of logging these fields individually
  • Added support for logging retry, stateless reset and initial tokens (#94,#86,#117)
  • Moved separate general event categories into a single category "generic" (#47)
  • Added "transport:connection_closed" event (#43,#85,#78,#49)
  • Added version_information and alpn_information events (#85,#75,#28)
  • Added parameters_restored events to help clarify 0-RTT behaviour (#88)

Smaller changes:

  • Merged loss_timer events into one loss_timer_updated event
  • Field data types are now strongly defined (#10,#39,#36,#115)
  • Renamed qpack instruction_received and instruction_sent to instruction_created and instruction_parsed (#114)
  • Updated qpack:dynamic_table_updated.update_type. It now has the value "inserted" instead of "added" (#113)
  • Updated qpack:dynamic_table_updated. It now has an "owner" field to differentiate encoder vs decoder state (#112)
  • Removed push_allowed from http:parameters_set (#110)
  • Removed explicit trigger field indications from events, since this was moved to be a generic property of the "data" field (#80)
  • Updated transport:connection_id_updated to be more in line with other similar events. Also dropped importance from Core to Base (#45)
  • Added length property to PaddingFrame (#34)
  • Added packet_number field to transport:frames_processed (#74)
  • Added a way to generically log packet header flags (first 8 bits) to PacketHeader
  • Added additional guidance on which events to log in which situations (#53)
  • Added "simulation:scenario" event to help indicate simulation details
  • Added "packets_acked" event (#107)
  • Added "datagram_ids" to the datagram_X and packet_X events to allow tracking of coalesced QUIC packets (#91)
  • Extended connection_state_updated with more fine-grained states (#49)

Since draft-marx-qlog-event-definitions-quic-h3-00:

  • Event and category names are now all lowercase
  • Added many new events and their definitions
  • "type" fields have been made more specific (especially important for PacketType fields, which are now called packet_type instead of type)
  • Events are given an importance indicator (issue #22)
  • Event names are more consistent and use past tense (issue #21)
  • Triggers have been redefined as properties of the "data" field and updated for most events (issue #23)

Authors' Addresses

Robin Marx (editor)
Akamai
Luca Niccolini (editor)
Meta
Marten Seemann (editor)
Lucas Pardue (editor)
Apple