Applicability of the QUIC Transport Protocol
Ericsson
mirja.kuehlewind@ericsson.com
Google
Gustav-Gull-Platz 1
8004 Zurich
Switzerland
ietf@trammell.ch
Internet-Draft
This document discusses the applicability of the QUIC transport protocol,
focusing on caveats impacting application protocol development and deployment
over QUIC. Its intended audience is designers of application protocol mappings
to QUIC, and implementors of these application protocols.
QUIC is a new transport protocol currently
under development in the IETF quic working group, focusing on support of
semantics as needed for HTTP/2 such as
stream-multiplexing to avoid head-of-line blocking. Based on current
deployment practices, QUIC is encapsulated in UDP. The version of QUIC that is
currently under development will integrate TLS 1.3
to encrypt all payload data and most control
information.
This document provides guidance for application developers that want to use
the QUIC protocol without implementing it on their own. This includes general
guidance for application use of HTTP/2 over QUIC as well as the use of other
application layer protocols over QUIC. For specific guidance on how to
integrate HTTP/2 with QUIC, see .
In the following sections we discuss specific caveats to QUIC’s applicability,
and issues that application developers must consider when using QUIC as a
transport for their application.
The words “MUST”, “MUST NOT”, “SHOULD”, and “MAY” are used in this document.
It’s not shouting; when these words are capitalized, they have a special
meaning as defined in .
QUIC uses UDP as a substrate for userspace implementation and port numbers for
NAT and middlebox traversal. While there is no evidence of widespread,
systematic disadvantage of UDP traffic compared to TCP in the Internet
, somewhere between three and five
percent of networks simply block UDP traffic. All applications running on top
of QUIC must therefore either be prepared to accept connectivity failure on
such networks, or be engineered to fall back to some other transport protocol.
This fallback SHOULD provide TLS 1.3 or equivalent cryptographic protection,
if available, in order to keep fallback from being exploited as a downgrade
attack. In the case of HTTP, this fallback is TLS 1.3 over TCP.
These applications must operate, perhaps with impaired functionality, in the
absence of features provided by QUIC not present in the fallback protocol. For
fallback to TLS over TCP, the most obvious difference is that TCP does not
provide stream multiplexing and therefore stream multiplexing would need to be
implemented in the application layer if needed. Further, TCP without the TCP
Fast Open extension does not support 0-RTT session resumption. TCP Fast Open
can be requested by the connection initiator but might no be supported by the
far end or could be blocked on the network path. Note that there is some
evidence of middleboxes blocking SYN data even if TFO was successfully
negotiated (see ).
Any fallback mechanism is likely to impose a degradation of performance;
however, fallback MUST not silently violate the application’s expectation of
confidentiality or integrity of its payload data.
Moreover, while encryption (in this case TLS) is inseparably integrated with
QUIC, TLS negotiation over TCP can be blocked. In case it is RECOMMENDED to
abort the connection, allowing the application to present a suitable prompt to
the user that secure communication is unavailable.
QUIC provides for 0-RTT connection establishment. This presents opportunities
and challenges for applications using QUIC.
A transport protocol that provides 0-RTT connection establishment to recently
contacted servers is qualitatively different than one that does not from the
point of view of the application using it. Relative trade-offs between the cost
of closing and reopening a connection and trying to keep it open are
different; see .
Applications must be slightly rethought in order to make best use of 0-RTT
resumption. Most importantly, application operations must be divided into
idempotent and non-idempotent operations, as only idempotent operations may
appear in 0-RTT packets. This implies that the interface between the
application and transport layer exposes idempotence either explicitly or
implicitly.
Retransmission or (malicious) replay of data contained in 0-RTT resumption
packets could cause the server side to receive two copies of the same data.
This is further described in .
Data sent during 0-RTT resumption also cannot benefit from perfect forward
secrecy (PFS).
Data in the first flight sent by the client in a connection established with
0-RTT MUST be idempotent (as specified in section 2.1 in ).
Applications MUST be designed, and their data MUST be framed, such that multiple
reception of idempotent data is recognized as such by the receiverApplications
that cannot treat data that may appear in a 0-RTT connection establishment as
idempotent MUST NOT use 0-RTT establishment. For this reason the QUIC transport
SHOULD provide an interface for the application to indicate if 0-RTT support is
in general desired or a way to indicate whether data is idempotent, whether PFS
is a hard requirement for the application, and/or whether rejected 0-RTT dgitata
should be retransmitted or withdrawn.
Because QUIC is encapsulated in UDP, applications using QUIC must deal with
short idle timeouts. Deployed stateful middleboxes will generally establish
state for UDP flows on the first packet state, and keep state for much shorter
idle periods than for TCP. According to a 2010 study (), UDP
applications can assume that any NAT binding or other state entry will be
expired after just thirty seconds of inactivity.
A QUIC application has three strategies to deal with this issue:
Ignore it, if the application-layer protocol consists only of interactions
with no or very short idle periods.
Ensure there are no long idle periods.
Resume the session after a long idle period, using 0-RTT resumption when
appropriate.
The first strategy is the easiest, but it only applies to certain applications.
Either the server or the client in a QUIC application can send PING frames as
keep-alives, to prevent the connection and any on-path state from timing out.
Recommendations for the use of keep-alives are application specific, mainly
depending on the latency requirements and message frequency of the application.
In this case, the application mapping must specify whether the client or server
is responsible for keeping the application alive. Note that sending PING frames
more frequently than every 30 seconds over long idle periods may result in a too
much unproductive traffic and power usage for some situations.
Alternatively, the client (but not the server) can use session resumption
instead of sending keepalive traffic. In this case, a client that wants to send
data to a server over a connection idle longer than the server’s idle timeout
(available from the idle_timeout transport parameter) can simply reconnect. When
possible, this reconnection can use 0-RTT session resumption, reducing the
latency involved with restarting the connection. This of course only applies in
cases in which 0-RTT data is safe, when the client is the restarting peer, and
when the data to be sent is idempotent.
The tradeoffs between resumption and keepalive need to be evaluated on a
per-application basis. However, in general applications should use keepalives
only in circumstances where continued communication is highly likely;
, for instance, recommends using PING frames for keepalive only
when a request is outstanding.
QUIC’s stream multiplexing feature allows applications to run multiple streams
over a single connection, without head-of-line blocking between streams,
associated at a point in time with a single five-tuple. Stream data is carried
within Frames, where one (UDP) packet on the wire can carry one of multiple
stream frames.
Stream can be independently open and closed, gracefully or by error. If a
critical stream for the application is closed, the application can generate
respective error messages on the application layer to inform the other end or
the higher layer and eventually indicate QUIC to reset the connection. QUIC,
however, does not need to know which streams are critical, and does not
provide an interface to exceptional handling of any stream. There are special
streams in QUIC that are used for control on the QUIC connection, however,
these streams are not exposed to the application.
Mapping of application data to streams is application-specific and described for
HTTP/s in . In general data that can be processed independently,
and therefore would suffer from head of line blocking if forced to be received
in order, should be transmitted over different streams. If the application
requires certain data to be received in order, the same stream should
be used for that data. If there is a logical grouping of data chunks or
messages, streams can be reused, or a new stream can be opened for each
chunk/message. If one message is mapped to a single stream, resetting the stream
to expire an unacknowledged message can be used to emulate partial reliability
on a message basis. If a QUIC receiver has maximum allowed concurrent streams
open and the sender on the other end indicates that more streams are needed, it
doesn’t automatically lead to an increase of the maximum number of streams by
the receiver. Therefore it can be valuable to expose maximum number of allowed,
currently open and currently used streams to the application to make the mapping
of data to streams dependent on this information.
Further, streams have a maximum number of bytes that can be sent on one
stream. This number is high enough (2^64) that this will usually not be
reached with current applications. Applications that send chunks of data over
a very long period of time (such as days, months, or years), should rather
utilize the 0-RTT session resumption ability provided by QUIC, than trying to
maintain one connection open.
Streams are meaningful only to the application; since stream information is
carried inside QUIC’s encryption boundary, no information about the stream(s)
whose frames are carried by a given packet is visible to the network.
Therefore stream multiplexing is not intended to be used for differentiating
streams in terms of network treatment. Application traffic requiring different
network treatment SHOULD therefore be carried over different five-tuples (i.e.
multiple QUIC connections). Given QUIC’s ability to send application data in
the first RTT of a connection (if a previous connection to the same host has
been successfully established to provide the respective credentials), the cost
of establishing another connection is extremely low.
Stream prioritization is not exposed to either the network or the receiver.
Prioritization is managed by the sender, and the QUIC transport should
provide an interface for applications to prioritize streams . Further
applications can implement their own prioritization scheme on top of QUIC: an
application protocol that runs on top of QUIC can define explicit messages
for signaling priority, such as those defined for HTTP/2; it can define rules
that allow an endpoint to determine priority based on context; or it can
provide a higher level interface and leave the determination to the
application on top.
Priority handling of retransmissions can be implemented by the sender in the
transport layer. recommends to retransmit lost data before new data,
unless indicated differently by the application. Currently, QUIC only provides
fully reliable stream transmission, which means that prioritization of
retransmissions will be beneficial in most cases, by filling in gaps and freeing
up the flow control window. For partially reliable or unreliable streams,
priority scheduling of retransmissions over data of higher-priority streams
might not be desirable. For such streams, QUIC could either provide an
explicit interface to control prioritization, or derive the prioritization
decision from the reliability level of the stream.
Flow control provides a means of managing access to the limited buffers
endpoints have for incoming data. This mechanism limits the amount of data that
can be in buffers in endpoints or in transit on the network. However, there are
several ways in which limits can produce conditions that can cause a connection
to either perform suboptimally or deadlock.
Deadlocks in flow control are possible for any protocol that uses QUIC, though
whether they become a problem depends on how implementations consume data and
provide flow control credit. Understanding what causes deadlocking might help
implementations avoid deadlocks.
Large messages can produce deadlocking if the recipient does not process the
message incrementally. If the message is larger than flow control credit
available and the recipient does not release additional flow control credit
until the entire message is received and delivered, a deadlock can occur. This
is possible even where stream flow control limits are not reached because
connection flow control limits can be consumed by other streams.
A common flow control implementation technique is for a receiver to extend
credit to the sender as a the data consumer reads data. In this setting, a
length-prefixed message format makes it easier for the data consumer to leave
data unread in the receiver’s buffers and thereby withhold flow control credit.
If flow control limits prevent the remainder of a message from being sent, a
deadlock will result. A length prefix might also enable the detection of this
sort of deadlock. Where protocols have messages that might be processed as a
single unit, reserving flow control credit for the entire message atomically
ensures that this style of deadlock is less likely.
A data consumer can read all data as it becomes available to cause the receiver
to extend flow control credit to the sender and reduce the chances of a
deadlock. However, releasing flow control credit might mean that the data
consumer might need other means for holding a peer accountable for the state it
keeps for partially processed messages.
Deadlocking can also occur if data on different streams is interdependent.
Suppose that data on one stream arrives before the data on a second stream on
which it depends. A deadlock can occur if the first stream is left unread,
preventing the receiver from extending flow control credit for the second
stream. To reduce the likelihood of deadlock for interdependent data, the
sender should ensure that dependent data is not sent until the data
it depends on has been accounted for in both stream- and connection- level flow
control credit.
Some deadlocking scenarios might be resolved by cancelling affected streams with
STOP_SENDING or RST_STREAM. Cancelling some streams results in the connection
being terminated in some protocols.
QUIC provides an interface that provides multiple streams to the application;
however, the application usually cannot control how data transmitted over one
stream is mapped into frames or how those frames are bundled into packets.
By default, QUIC will try to maximally pack packets with one or more stream
data frames to minimize bandwidth consumption and computational costs (see
section 8 of ). If there is not enough data available to fill a packet,
QUIC may even wait for a short time, to optimize bandwidth efficiency instead of
latency. This delay can either be pre-configured or dynamically adjusted based
on the observed sending pattern of the application. If the application requires
low latency, with only small chunks of data to send, it may be valuable to
indicate to QUIC that all data should be send out immediately. Alternatively,
if the application expects to use a specific sending pattern, it can also
provide a suggested delay to QUIC for how long to wait before bundle frames
into a packet.
Similarly, an appliaction has usually no control about the length of a QUIC
packet on the wire. However, QUIC provides the ability to add a padding frame to
impact the packet size. This is mainly used by QUIC itself in the first packet
in order to ensure that the path is capable of transferring packets of at least
a certain size. Additionally, a QUIC implementation can expose an application
layer interface to specify a certain packet size. This can either be used by the
application to force certian packet sizes in specific use cases/networks, or
ensure that all packets are equally sized to conceal potential leakage of
application layer information when the data sent by the application are not
greedy. Note the initial packet must have a minimum size of 1200 bytes
according to the QUIC specification. A receiver of a smaller initial packet may
reject this packet in order to avoid amplification attacks.
As QUIC is a general purpose transport protocol, there are no requirements that
servers use a particular UDP port for QUIC in general. Instead, the same port
number is used as would be used for the same application over TCP. In the case
of HTTP the expectation is that port 443 is used, which has already been
registered for “http protocol over TLS/SSL”. However, also
specifies the use of Alt-Svc for HTTP/QUIC discovery which allows the server to
use and announce a different port number.
In general, port numbers serves two purposes: “first, they provide a
demultiplexing identifier to differentiate transport sessions between the same
pair of endpoints, and second, they may also identify the application protocol
and associated service to which processes connect” . Note that the
assumption that an application can be identified in the network based on the
port number is less true today, due to encapsulation, mechanisms for dynamic
port assignments as well as NATs.
However, whenever a non-standard port is used which does not enable easy
mapping to a registered service name, this can lead to blocking by
network elements such as firewalls that rely on the port number as a first order
of filtering.
QUIC supports connection migration. Even if lower-layer addresses (usually the
4-tuple of IP addresses and ports) changes, QUIC packets can still be associated
with an existing connection based on the Connection ID (see also section
) in the QUIC header, if present. This supports cases where address
information changes due to e.g. NAT rebinding or change of the local interface.
Currently QUIC only supports failover cases. Only one “path” can be used at a
time, and as soon as the new path is validated all traffic will be switched over
to the next path. Of course if an endpoint decided to not use the Connection ID
in short packets (Zero-length Conn ID) for a certain connection, migration is
not supported for that direction of the connection.
QUIC connections are closed either by expiration of an idle timeout or by an
explicit indication of the application that a connection should be closed
(immediate close). While data could still be received after the immediate close
has been initiated by one endpoint (for a limited time period), the expectation
is that an immediate close was negotiated at the application layer and
therefore no additional data is expected from both sides.
An immidate close will emit an CONNECTION_CLOSE frame. This frames has two sets
of types: one for QUIC internal problems that might lead to connection closure,
and one for closures initiated by the application. An application using QUIC can
define application-specific error codes, e.g. see section 8.1. In
the case of a grateful shut-down initiated by the application after application
layer negotiation, a NO_ERROR code is expected. Further, the CONNECTION_CLOSE
frame provides an optional reason field, that can be used to append
human-readable information to an error code. Note that QUIC RESET_STREAM and
STOP_SENDING frames provide similar capablities. Usually application error codes
are defined to be applicabile to all three frames.
Alternatively, a QUIC connection will be silently closed by each endpoint
separately after an idle timeout. The idle timeout is announce for each
endpoint during connection established and should be accessible by the
application. If an application desires to keep the connection open for longer
than the announced timeout, it can send keep-alives messages. See
{#resumption-v-keepalive} for further guidance.
QUIC exposes some information to the network in the unencrypted part of the
header, either before the encryption context is established, because the
information is intended to be used by the network. QUIC has a long header that
is used during connection establishment and for other control processes, and a
short header that may be used for data transmission in an established
connection. While the long header always exposes some information (such as the
version and Connection IDs), the short header exposes at most only a single
Connection ID.
Note that the Connection ID in the short header may be omitted. This is a
per-connection configuration option; if the Connection ID is not present, then
the peer omitting the connection ID will use the same local address for the
lifetime of the connection.
QUIC supports a server-generated Connection ID, transmitted to the client during
connection establishment (see Section 6.1 of ). Servers behind load
balancers may need to change the Connection ID during the handshake, encoding
the identity of the server or information about its load balancing pool, in
order to support stateless load balancing. Once the server generates a
Connection ID that encodes its identity, every CDN load balancer would be able
to forward the packets to that server without retaining connection state.
Server-generated connection IDs should seek to obscure any encoding, of routing
identities or any other information. Exposing the server mapping would allow
linkage of multiple IP addresses to the same host if the server also supports
migration. Furthermore, this opens an attack vector on specific servers or
pools.
The best way to obscure an encoding is to appear random to observers, which is
most rigorously achieved with encryption.
While sufficiently robust connection ID generation schemes will mitigate
linkability issues, they do not provide full protection. Analysis of
the lifetimes of six-tuples (source and destination addresses as well as the
migrated CID) may expose these links anyway.
In the limit where connection migration in a server pool is rare, it is trivial
for an observer to associate two connection IDs. Conversely, in the opposite
limit where every server handles multiple simultaneous migrations, even an
exposed server mapping may be insufficient information.
The most efficient mitigation for these attacks is operational, either by using
a load balancing architecture that loads more flows onto a single server-side
address, by coordinating the timing of migrations to attempt to increase the
number of simultaneous migrations at a given time, or through other means.
QUIC provides a Server Retry packet that can be sent by a server in response to
the Client Initial packet. The server may choose a new Connection ID in that
packet and the client will retry by sending another Client Initial packet with
the server-selected Connection ID. This mechanism can be used to redirect a
connection to a different server, e.g. due to performance reasons or when
servers in a server pool are upgraded gradually, and therefore may support
different versions of QUIC. In this case, it is assumed that all servers
belonging to a certain pool are served in cooperation with load balancers that
forward the traffic based on the Connection ID. A server can choose the
Connection ID in the Server Retry packet such that the load balancer will
redirect the next Client Initial packet to a different server in that pool.
Versioning in QUIC may change the protocol’s behavior completely, except
for the meaning of a few header fields that have been declared to be invariant
. A version of QUIC
with a higher version number will not necessarily provide a better service,
but might simply provide a different feature set. As such, an application needs
to be able to select which versions of QUIC it wants to use.
A new version could use an encryption scheme other than TLS 1.3 or higher.
specifies requirements for the cryptographic handshake as currently
realized by TLS 1.3 and described in a separate specification
. This split is performed to enable
light-weight versioning with different cryptographic handshakes.
QUIC provides integrity protection for its version negotiation process. This
process assumes that the set of versions that a server supports is fixed. This
complicates the process for deploying new QUIC versions or disabling old
versions when servers operate in clusters.
A server that rolls out a new version of QUIC can do so in three stages. Each
stage is completed across all server instances before moving to the next stage.
In the first stage of deployment, all server instances start accepting new
connections with the new version. The new version can be enabled progressively
across a deployment, which allows for selective testing. This is especially
useful when the new version is compatible with an old version, because the new
version is more likely to be used.
While enabling the new version, servers do not advertise the new version in any
Version Negotiation packets they send. This prevents clients that receive a
Version Negotiation packet from attempting to connect to server instances that
might not have the new version enabled.
During the initial deployment, some clients will have received Version
Negotiation packets that indicate that the server does not support the new
version. Other clients might have successfully connected with the new version
and so will believe that the server supports the new version. Therefore,
servers need to allow for this ambiguity when validating the negotiated version.
The second stage of deployment commences once all server instances are able
accept new connections with the new version. At this point, all servers can
start sending the new version in Version Negotiation packets.
During the second stage, the server still allows for the possibility that some
clients believe the new version to be available and some do not. This state
will persist only for as long as any Version Negotiation packets take to be
transmitted and responded to. So the third stage can follow after a relatively
short delay.
The third stage completes the process by enabling validation of the negotiation
version as though the new version were disabled.
The process for disabling an old version or rolling back the introduction of a
new version uses the same process in reverse. Servers disable validation of the
old version, stop sending the old version in Version Negotiation packets, then
the old version is no longer accepted.
This document has no actions for IANA.
See the security considerations in and ; the security
considerations for the underlying transport protocol are relevant for
applications using QUIC, as well.
Application developers should note that any fallback they use when QUIC cannot
be used due to network blocking of UDP SHOULD guarantee the same security
properties as QUIC; if this is not possible, the connection SHOULD fail to
allow the application to explicitly handle fallback to a less-secure
alternative. See .
Igor Lubashev contributed text to on server-selected Connection IDs.
This work is partially supported by the European Commission under Horizon 2020
grant agreement no. 688421 Measurement and Architecture for a Middleboxed
Internet (MAMI), and by the Swiss State Secretariat for Education, Research, and
Innovation under contract no. 15.0268. This support does not imply endorsement.
Key words for use in RFCs to Indicate Requirement Levels
In many standards track documents several words are used to signify the requirements in the specification. These words are often capitalized. This document defines these words as they should be interpreted in IETF documents. This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.
QUIC: A UDP-Based Multiplexed and Secure Transport
This document defines the core of the QUIC transport protocol. Accompanying documents describe QUIC's loss detection and congestion control and the use of TLS for key negotiation. Note to Readers Discussion of this draft takes place on the QUIC working group mailing list (quic@ietf.org), which is archived at <https://mailarchive.ietf.org/arch/search/?email_list=quic>. Working Group information can be found at <https://github.com/ quicwg>; source code and issues list for this draft can be found at <https://github.com/quicwg/base-drafts/labels/-transport>.
Using TLS to Secure QUIC
This document describes how Transport Layer Security (TLS) is used to secure QUIC. Note to Readers Discussion of this draft takes place on the QUIC working group mailing list (quic@ietf.org), which is archived at https://mailarchive.ietf.org/arch/search/?email_list=quic [1]. Working Group information can be found at https://github.com/quicwg [2]; source code and issues list for this draft can be found at https://github.com/quicwg/base-drafts/labels/-tls [3].
Using TLS to Secure QUIC
This document describes how Transport Layer Security (TLS) is used to secure QUIC. Note to Readers Discussion of this draft takes place on the QUIC working group mailing list (quic@ietf.org), which is archived at https://mailarchive.ietf.org/arch/search/?email_list=quic [1]. Working Group information can be found at https://github.com/quicwg [2]; source code and issues list for this draft can be found at https://github.com/quicwg/base-drafts/labels/-tls [3].
Internet Assigned Numbers Authority (IANA) Procedures for the Management of the Service Name and Transport Protocol Port Number Registry
This document defines the procedures that the Internet Assigned Numbers Authority (IANA) uses when handling assignment and other requests related to the Service Name and Transport Protocol Port Number registry. It also discusses the rationale and principles behind these procedures and how they facilitate the long-term sustainability of the registry.This document updates IANA's procedures by obsoleting the previous UDP and TCP port assignment procedures defined in Sections 8 and 9.1 of the IANA Allocation Guidelines, and it updates the IANA service name and port assignment procedures for UDP-Lite, the Datagram Congestion Control Protocol (DCCP), and the Stream Control Transmission Protocol (SCTP). It also updates the DNS SRV specification to clarify what a service name is and how it is registered. This memo documents an Internet Best Current Practice.
Version-Independent Properties of QUIC
This document defines the properties of the QUIC transport protocol that are expected to remain unchanged over time as new versions of the protocol are developed. Note to Readers Discussion of this draft takes place on the QUIC working group mailing list (quic@ietf.org), which is archived at https://mailarchive.ietf.org/arch/search/?email_list=quic [1]. Working Group information can be found at https://github.com/quicwg [2]; source code and issues list for this draft can be found at https://github.com/quicwg/base-drafts/labels/-invariants [3].
Internet Path Transparency Measurements using RIPE Atlas (RIPE72 MAT presentation)
Using UDP for Internet Transport Evolution (arXiv preprint 1612.07816)
QUIC Deployment Experience at Google (IETF96 QUIC BoF presentation)
Network Support for TCP Fast Open (NANOG 67 presentation)
An experimental study of home gateway characteristics (Proc. ACM IMC 2010)
Retrying HTTP Requests
HTTP allows requests to be automatically retried under certain circumstances. This draft explores how this is implemented, requirements for similar functionality from other parts of the stack, and potential future improvements.
Hypertext Transfer Protocol Version 3 (HTTP/3)
The QUIC transport protocol has several features that are desirable in a transport for HTTP, such as stream multiplexing, per-stream flow control, and low-latency connection establishment. This document describes a mapping of HTTP semantics over QUIC. This document also identifies HTTP/2 features that are subsumed by QUIC, and describes how HTTP/2 extensions can be ported to HTTP/3.
Retrying HTTP Requests
HTTP allows requests to be automatically retried under certain circumstances. This draft explores how this is implemented, requirements for similar functionality from other parts of the stack, and potential future improvements.