This document specifies two means of performing version negotiation: 1) "incompatible", which requires a round trip and is applicable to all versions, and 2) "compatible", which allows saving the round trip but only applies when the versions are compatible (see
Section 2.2).
The client initiates a QUIC connection by choosing an Original Version and sending a first flight of QUIC packets with a long header to the server [
QUIC-INVARIANTS]. The client's first flight includes Version Information (see
Section 3), which will be used to optionally enable compatible version negotiation (see
Section 2.3) and to prevent version downgrade attacks (see
Section 4).
Upon receiving this first flight, the server verifies whether it knows how to parse first flights from the Chosen Version (which is also the Original Version in this case). If it does not, then it starts incompatible version negotiation (see
Section 2.1), which causes the client to initiate a new connection with a different version. For instance, if the client initiates a connection with version A that the server can't parse, the server starts incompatible version negotiation; then, when the client initiates a new connection with version B, we say that the first connection's client Chosen Version is A, the second connection's client Chosen Version is B, and the Original Version for the entire sequence is A.
If the server can parse the first flight, it can establish the connection using the client's Chosen Version, or it
MAY select any other compatible version, as described in
Section 2.3.
Note that it is possible for a server to have the ability to parse the first flight of a given version without fully supporting it, in the sense that it implements enough of the version's specification to parse first flight packets but not enough to fully establish a connection using that version.
The server starts incompatible version negotiation by sending a Version Negotiation packet. This packet
SHALL include each entry from the server's set of Offered Versions (see
Section 5) in a Supported Version field. The server
MAY add reserved versions (as defined in
Section 6.3 of [
QUIC]) in Supported Version fields.
Clients will ignore a Version Negotiation packet if it contains the Original Version attempted by the client, as required by
Section 4. The client also ignores a Version Negotiation packet that contains incorrect connection ID fields, as required by
Section 6 of [
QUIC-INVARIANTS].
Upon receiving the Version Negotiation packet, the client
SHALL search for a version it supports in the list provided by the server. If it doesn't find one, it
SHALL abort the connection attempt. Otherwise, it
SHALL select a mutually supported version and send a new first flight with that version -- this version is now the Negotiated Version.
The new first flight will allow the endpoints to establish a connection using the Negotiated Version. The handshake of the Negotiated Version will exchange Version Information (see
Section 3) that is required to ensure that version negotiation was genuine, i.e., that no attacker injected packets in order to influence the version negotiation process (see
Section 4).
Only servers can start incompatible version negotiation. Clients
MUST NOT send Version Negotiation packets and servers
MUST ignore all received Version Negotiation packets.
If A and B are two distinct versions of QUIC, A is said to be "compatible" with B if it is possible to take a first flight of packets from version A and convert it into a first flight of packets from version B. As an example, if versions A and B are absolutely equal in their wire image and behavior during the handshake but differ after the handshake, then A is compatible with B and B is compatible with A. Note that the conversion of the first flight can be lossy; some data, such as QUIC version 1 0-RTT packets, could be ignored during conversion and retransmitted later.
Version compatibility is not symmetric. It is possible for version A to be compatible with version B and for version B not to be compatible with version A. This could happen, for example, if version B is a strict superset of version A, i.e., if version A includes the concept of streams and STREAM frames and version B includes the concept of streams and the hypothetical concept of tubes along with STREAM and TUBE frames, then A would be compatible with B, but B would not be compatible with A.
Note that version compatibility does not mean that every single possible instance of a first flight will succeed in conversion to the other version. A first flight using version A is said to be "compatible" with version B if two conditions are met: (1) version A is compatible with version B and (2) the conversion of this first flight to version B is well defined. For example, if version B is equal to version A in all aspects except it introduced a new frame in its first flight that version A cannot parse or even ignore, then version B could still be compatible with version A, as conversions would succeed for connections where that frame is not used. In this example, first flights using version B that carry this new frame would not be compatible with version A.
When a new version of QUIC is defined, it is assumed to not be compatible with any other version unless otherwise specified. Similarly, no other version is compatible with the new version unless otherwise specified. Implementations
MUST NOT assume compatibility between versions unless explicitly specified.
Note that both endpoints might disagree on whether two versions are compatible or not. For example, two versions could have been defined concurrently and then specified as compatible in a third document much later -- in that scenario, one endpoint might be aware of the compatibility document, while the other may not.
When the server can parse the client's first flight using the client's Chosen Version, it can extract the client's Version Information structure (see
Section 3). This contains the list of versions that the client knows its first flight is compatible with.
In order to perform compatible version negotiation, the server
MUST select one of these versions that it (1) supports and (2) knows the client's Chosen Version is compatible with. This selected version is now the Negotiated Version. After selecting it, the server attempts to convert the client's first flight into that version and replies to the client as if it had received the converted first flight.
If those formats are identical, as in cases where the Negotiated Version is the same as the client's Chosen Version, then this will be the identity transformation. If the first flight is correctly formatted, then this conversion process cannot fail by definition of the first flight being compatible; if the server is unable to convert the first flight, it
MUST abort the handshake.
If a document specifies that a QUIC version is compatible with another, that document
MUST specify the mechanism by which clients are made aware of the Negotiated Version. An example of such a mechanism is to have the client determine the server's Negotiated Version by examining the QUIC long header Version field. Note that, in this example mechanism, it is possible for the server to initially send packets with the client's Chosen Version before switching to the Negotiated Version (this can happen when the client's Version Information structure spans multiple packets; in that case, the server might acknowledge the first packet in the client's Chosen Version and later switch to a different Negotiated Version). Mutually compatible versions
SHOULD use the same mechanism.
Note that, after the first flight is converted to the Negotiated Version, the handshake completes in the Negotiated Version. If the Negotiated Version has requirements that apply during the handshake, those requirements apply to the entire handshake, including the converted first flight. In particular, if the Negotiated Version mandates that endpoints perform validations on Handshake packets, endpoints
MUST also perform such validations on the converted first flight. For instance, if the Negotiated Version requires that the 5-tuple remain stable for the entire handshake (as QUIC version 1 does), then both endpoints need to validate the 5-tuple of all packets received during the handshake, including the converted first flight.
Note also that the client can disable compatible version negotiation by only including the Chosen Version in the Available Versions field of the Version Information (see
Section 3).
If the server does not find a compatible version (including the client's Chosen Version), it will perform incompatible version negotiation instead (see
Section 2.1).
Note that it is possible to have incompatible version negotiation followed by compatible version negotiation. For instance, if version A is compatible with version B and version C is compatible with version D, the following scenario could occur:
Client Server
Chosen = A, Available Versions = (A, B) ------------->
<------------------------ Version Negotiation = (D, C)
Chosen = C, Available Versions = (C, D) ------------->
<------------- Chosen = D, Available Versions = (D, C)
In this example, the client selected C from the server's Version Negotiation packet, but the server preferred D and then selected it from the client's offer.
QUIC connections are shared state between a client and a server [
QUIC-INVARIANTS]. The compatible version negotiation mechanism defined in this document (see
Section 2.3) is performed as part of a single QUIC connection; that is, the packets with the client's Chosen Version are part of the same connection as the packets with the Negotiated Version.
In comparison, the incompatible version negotiation mechanism, which leverages QUIC Version Negotiation packets (see
Section 2.1), conceptually operates across two QUIC connections, i.e., the connection attempt prior to receiving the Version Negotiation packet is distinct from the connection with the incompatible version that follows.
Note that this separation across two connections is conceptual, i.e., it applies to normative requirements on QUIC connections, but it does not require implementations to internally use two distinct connection objects.
When the client picks its Original Version, it
SHOULD try to avoid incompatible version negotiation to save a round trip. Therefore, the client
SHOULD pick an Original Version to maximize the combined probability that both:
-
the server knows how to parse first flights from the Original Version and
-
the Original Version is compatible with the client's preferred version.
Without additional information, this could mean selecting the oldest version that the client supports while advertising newer compatible versions in the client's first flight.