Draft: draft-ietf-quic-transport

Commentary: As a straw man , I selected a v1 limit of 48 bytes to reflect comments in London . Note that we can not grease the unused bits of the CIL fields if we 're going to make load balancers robust to version changes ( which is the point in having CIDs in the invariants , IIRC ) . 
 It would be nice if an invalid length could not be ~expressed~ encoded . Is 48 a completely arbitrary number ? 
 somewhat arbitrary . wanted it smaller than a 64B cacheline ; the most extreme use case we came up with was " more than 32B " . However , I 'd prefer that we not artificially limit it in the invariants , because my imagination is limited . 
 It 's great to have CID 's of length 1 .. 3 bytes , but perhaps some guidelines are needed as there is an increased collision chance and some attack mitigations might make assumptions on the CID being at least 32 bits . 
  a good point ; for purposes of Version Negotiation , v1 servers need to accommodate longer CIDs ( up to 255 bytes ) . I 'll make the change . 
 I missed that comment in the meeting : could you explain why ? 
 in v2 , we might allow 255B CIDs . So to send a proper VN packet in response to v2 we need to handle that entire CID . 
 This PR looks good to me . Thanks for making an encoding that we can deploy in parallel with older versions on the same server . 
 OK , the transport drafts now acknowledges that it may have to send VN with CIL 48 bytes . I believe all known issues are resolved . 
 This CL also needs to change the encoding of the Original Destination Connection ID Length ( ODCIL ) in Retry packets . It is currently encoded as the four least significant bits of the first byte and that wo n't fit anymore . I 'd suggest moving ODCIL to be a full byte between the Source Connection ID and the Original Destination Connection ID . I would then mark the four least significant bits of the first byte of Retry packets as unused similar to Version Negotiation packets . 
 Nice catch . It should be fixed now . 
 can you fix that up ? 
 The two principles previously espoused were : 1 ) MUST ensure every stateless reset is smaller than the packet received to prevent looping : 2 ) Stateless resets MUST be 69 bytes with in order to make them non - identifiable . This means clients that want stateless resets in response to keepalives(ie : PINGs ) must send 70 byte packets or risk getting nothing in response , therefore waiting for an RTO . This seems like an unnecessary risk / complexity , when we could decrease the minimum size of the stateless reset being processed by a client . The risk / cost to the client seems very low of processing stateless resets after a packet has already failed decryption . Additionally , it 's difficult to imagine a client ever use a 16 byte connection ID , and even more implausible the client will use a 48 byte connection ID , given most clients are expected to have one connection per port and even those with multiple likely wo n't have so many they need 8 byte CIDs . 
 In many cases even largish connection IDs will be around 20 bytes , meaning that minimal ( PING ) packets come to around 40 bytes . Adding 30 bytes of padding is a little annoying . the error in your description is here : We do n't stipulate any size constraints on processing packets , just generating them . The sending constraints exist to prevent indefinite looping of stateless reset . They also eliminate amplification as a side - effect . The reason we have this recommendation is to keep the stateless reset indistinguishable from " real " packets . Though it might be a little unusual , a 70 byte packet is n't implausible , no matter what the situation . But sending a 23 byte packet when every other packet included the same 40 byte connection ID will stand out badly . 
 Reading that full section , though we specify a length of the stateless reset to be sent in the wire format , we do n't use any normative language about size except : " Endpoints MUST discard packets that are too small to be valid QUIC packets . " so I guess if , as a server , I believe sending a stateless reset of a certain size is unlikely to be distinguishable from a " real " packet , I can do that ? If so , that 's not obvious , but it would seem there 's no normative text change necessary . 
 Yes , there are a lot of places where the sizes are mere suggestions rather than concrete requirements . That 's intentional . We could just say " oh well " , but the problem here is that the gap between min and  has increased dramatically as a result of . That makes the choice to pad to a size that will trigger a stateless reset a harder call . 
 The problem is that while you as a server can do that , the question here is what size packet the client needs to send to provoke that behavior from a generic server . And the server ca n't just base it on the CID size that it uses locally , because what really matters is size relative to other packets flowing in that direction , i.e. the client 's CID . Which will often be zero , but can be immense and we need to account for that . We currently say : There 's a crazy way out by embedding your own minimum CID size ( either unencrypted or non - privately ) in every packet so that a server can use that to determine whether it could safely send you a smaller SR . But adding an extra byte to all packets to save ~30 bytes per keepalive does n't seem likely to work out well . 
 I can see the need for issuing a SRT ASAP in order for fast client side connection recovery . This is one reason there has been strong pushback against probabilistic resets as I have proposed before . Another reason is probably esthetical and because it was n't that important when SRT 's could be made small enough . However , when you are facing a guessing game on how large to make packets and arbitrarily making payload larger than need be , and possible only some of the payload to eventually ensure a high chance of reset , I think this is really doing probabilistic resets the wrong way around . I suggest that as long as you can send a SRT that is smaller than inbound you should . If you can not do that you are only permitted to send a SRT with a probability no larger than size(inbound)/size(SRT ) . EDIT : the reason that SRT needs to smaller or less probable is two - fold : to prevent amplification attacks and to prevent endless loops . 
 I remember a few goals for stateless rejects(there are probably more ): 1 ) Can not be spoofed 2 ) Ideally an active attacker can not cause a server to generate one for an active connection 3 ) Indistinguishable from other traffic 4 ) No amplification attacks 5 ) No endless loops 6 ) Can be sent in response to any packet that ca n't be associated with an active connection . We compromised 6 in order to achieve 3 more often . I think we can tweak the existing design to not compromise 6 and still provide 3 . We can put a bit more burden on the client and add text to indicate that if the client wants a stateless reject that 's indistinguishable from other traffic , it needs to send a packet that 's at least 1 byte larger than the minimum plausible incoming packet . In most circumstances , I expect this to naturally occur because the server connection ID will be longer than the client 's . 
 I think you messed two key goals peer to fail fast on dead connections . unwanted inbound traffic from dead connections . 
 I think this would be the logical outcome , even though I am not sure I like the complexity . At the moment , minimum packet size is calculated like : 1 + len(dcid ) + 4 + len(aeadtag ) . Changing this to 1 + max(len(dcid ) , len(scid ) ) + 4 + len(aeadtag ) is not that difficult . 
 add one to your calculation . That would cause peers to generate packets that can be responded to with a stateless reset , but it would n't help the peer generating the stateless reset if it received a bogus packet . It would be ignorant of a critical value . That 's not strictly a problem , and it might be that this is better than any alternative , but I do wonder about what sort of perverse incentives we 're creating ( with or without the clever hack ) . 
 At the end of the day , I think the tradeoff is between the stateless reset sticking out and wasting padding bytes . Adding some text saying that endpoints that use longer connection IDs than their peers should pad their very short packets if they care about stateless resets not sticking out is the least awkward solution here ? I do n't think this is a really bad issue in practice . 
 To clarify , is it " longer connection IDs than their peers " or is it " peer 's connection ID is smaller than a stateless reset token " ? 
 It 's smaller than their peer 's . The stateless reset token conveniently fits in the space we would ordinarily dedicate to an authentication tag . 
 From an offline conversation with , when client connection IDs are in use , Stateless Resets are inherently distinguishable from regular traffic because they do not carry the previously - used client connection ID . One might say that they could be confused for a change in connection ID , which is true . But let 's assume we just changed from an 18 byte client connection ID to a 4 - byte connection ID ( which is specifically allowed by the spec ) , then that new packet is indistinguishable from a stateless reset . So I propose that instead of having a minimum required size for stateless resets of 21 and a recommended minimum of 39 , we can just say the minimum required size is 25 . The important value here is the minimum non - zero connection ID length , not the maximum connection ID length . This also means that the conversation about changing the maximum connection ID length is now orthogonal to stateless resets . 
 That ’s technically correct , but I‘m having doubts if this is s realistic scenario in practice . Realistically , a client would only pay the price of having a 18 byte overhead on every packet if it absolutely has to - i.e. if it ’s behind an architecture that performs connection ID based routing and requires that many bits to do so . If that ’s the case , changing to a 4 byte connection ID is not a realistic option . 
 I 'm not sure what you mean by " realistic scenario " . The only property we need from a stateless reset is that it could be real traffic . The fact that it 's unlikely traffic is not really relevant . The stateless reset is already going to stick out due to the fact that there are no subsequent packets after it since it closes the connection . 
 Although it is quite late in the day for this objection , I find that we already bumping up against the limits of the 18 byte connection ID . Inevitably , this touches on some issues we 're exploring in the quic - load - balancers draft , which is n't adopted , and many of you are no doubt happily ignoring . There are basically three uses of the 144 bits of CID entropy in QUIC - LB : 1 ) Server ID encoding -- as there are only so many servers one might have , 16 bits is usually enough for this . 2 ) " Private " server entropy -- the load balancer does n't care what the bits are , but they 're protected by crypto . The server created and encrypted the CID , so it can use this for its local purposes , but no one else can . The amount of this depends , but if you 're using a 256 - bit block cipher it 's something like 240 bits -- plenty 3 ) " Public " entropy -- not encrypted , these bits are readable by anyone who is familiar with the encoding scheme . With a 16 B encryption block , we have only 16 bits to play with here . QUIC - LB is already taking 2 of these last bits for config rotation , which allows server pools to incrementally deploy new CID keys or encoding schemes . So we 're down to 14 bits . The public entropy is useful for a growing pile of trusted hardware devices that are presumably not going to perform CID decryption . On f5 boxes , we have a hardware disaggregator for the many dataplane processors and threads that are operating . Today , we 'd use perhaps 8 of the bits , but in future architectures even 14 might not quite be enough . And now , having chatting with Manasi Deval from Intel after her Prague talk about hardware offload , I 'm fairly sure we 're going to need 4 to cheaply encode the connection ID length , at least for servers that want to use this service . If we ever wanted a solution that allowed multiple tiers of load balancing , which is not currently in scope for QUIC - LB , we do n't have nearly enough space . While I think that existing use cases can just barely make do with what we have , it is a bad sign if something in the invariants is already constraining use cases . Offline in London , we 've circulated a number of possible approaches to this , but the first - order discussion is a willingness to reopen the invariants . If so , there are a couple of solutions that wo n't break existing load balancer deployments . I 'm happy to write a PR if this is the consensus . 
 is a past issue about 1 , 2 , and 3 byte CIDs . 
 The two obvious encodings are : 
 Both consume the same size for any currently - sane CID , but making path elements consume varints seems cruel . 
 I prefer retaining the current design , with the exception that CIDL is mapped to 4,6,8, ... ,34 bytes instead of 4,5,6, ... ,18 bytes . 
 You mean . But I think that we need where m 0 and n < 256 . 
 Those are the same thing , but I did n't want to make people consume TLS encoding i this bug :) 
 Discussed in London ; support in the room for doing this . to make a PR . 
 One change , but looks good otherwise .
Paragraph (version A):
The smallest unit of QUIC that can be encapsulated in a UDP
datagram.  Multiple QUIC packets can be encapsulated in a single
UDP datagram.
Paragraph (version A):
Streams in QUIC provide a lightweight, ordered byte-stream
abstraction to an application.  An alternative view of QUIC streams
is as an elastic "message" abstraction.
Paragraph (version A):
Streams are an ordered byte-stream abstraction with no other
structure that is visible to QUIC.  STREAM frame boundaries are not
expected to be preserved when data is transmitted, when data is
retransmitted after packet loss, or when data is delivered to the
application at a receiver.
Paragraph (version B):
Streams are an ordered byte-stream abstraction with no other
structure visible to QUIC.  STREAM frame boundaries are not expected
to be preserved when data is transmitted, retransmitted after packet
loss, or delivered to the application at a receiver.
Paragraph (version A):
QUIC does not provide frames for exchanging prioritization
information.  Instead it relies on receiving priority information
from the application that uses QUIC.
Paragraph (version B):
Sending the first STREAM or STREAM_DATA_BLOCKED frame causes a
sending part of a stream to enter the "Send" state.  An
implementation might choose to defer allocating a stream ID to a
stream until it sends the first STREAM frame and enters this state,
which can allow for better stream prioritization.
Paragraph (version A):
Before creating a stream, all streams of the same type with lower-
numbered stream IDs MUST be created.  This ensures that the creation
order for streams is consistent on both endpoints.
Paragraph (version B):
Before a stream is created, all streams of the same type with lower-
numbered stream IDs MUST be created.  This ensures that the creation
order for streams is consistent on both endpoints.
Paragraph (version B):
Once all data for the stream has been received, the receiving part
enters the "Data Recvd" state.  This might happen as a result of
receiving the same STREAM frame that causes the transition to "Size
Known".  After all data has been received, any STREAM or
STREAM_DATA_BLOCKED frames for the stream can be discarded.
Paragraph (version B):
Once the application receives the signal indicating that the stream
was reset, the receiving part of the stream transitions to the "Reset
Read" state, which is a terminal state.
Paragraph (version A):
STREAM frames received after sending STOP_SENDING are still counted
toward connection and stream flow control, even though these frames
will be discarded upon receipt.
Paragraph (version A):
A receiver can advertise a larger offset by sending MAX_STREAM_DATA
or MAX_DATA frames at any time during the connection.  A receiver
cannot renege on an advertisement however.  That is, once a receiver
advertises an offset, it MAY advertise a smaller offset, but this has
no effect.
Paragraph (version A):
If a sender runs out of flow control credit, it will be unable to
send new data and is considered blocked.  A sender SHOULD send
STREAM_DATA_BLOCKED or DATA_BLOCKED frames to indicate it has data to
write but is blocked by flow control limits.  These frames are
expected to be sent infrequently in common cases, but they are
considered useful for debugging and monitoring purposes.
Paragraph (version A):
A sender sends a single STREAM_DATA_BLOCKED or DATA_BLOCKED frame
only once when it reaches a data limit.  A sender SHOULD NOT send
multiple STREAM_DATA_BLOCKED or DATA_BLOCKED frames for the same data
limit, unless the original frame is determined to be lost.  Another
STREAM_DATA_BLOCKED or DATA_BLOCKED frame can be sent after the data
limit is increased.
Paragraph (version A):
On receipt of a RESET_STREAM frame, an endpoint will tear down state
for the matching stream and ignore further data arriving on that
stream.  If a RESET_STREAM frame is reordered with stream data for
the same stream, the receiver's estimate of the number of bytes
received on that stream can be lower than the sender's estimate of
the number sent.  As a result, the two endpoints could disagree on
the number of bytes that count towards connection flow control.
Paragraph (version A):
Endpoints MUST NOT exceed the limit set by their peer.  An endpoint
that receives a STREAM frame with a stream ID exceeding the limit it
has sent MUST treat this as a stream error of type STREAM_LIMIT_ERROR
(error-handling).
Paragraph (version A):
A receiver cannot renege on an advertisement.  That is, once a
receiver advertises a stream limit using the MAX_STREAMS frame,
advertising a smaller limit has no effect.  A receiver MUST ignore
any MAX_STREAMS frame that does not increase the stream limit.
Paragraph (version B):
Endpoints MUST NOT exceed the limit set by their peer.  An endpoint
that receives a frame with a stream ID exceeding the limit it has
sent MUST treat this as a connection error of type STREAM_LIMIT_ERROR
(error-handling).
Paragraph (version A):
Due to packet reordering or loss, clients might receive packets for a
connection that are encrypted with a key it has not yet computed.
Clients MAY drop these packets, or MAY buffer them in anticipation of
later packets that allow it to compute the key.
Paragraph (version B):
Due to packet reordering or loss, a client might receive packets for
a connection that are encrypted with a key it has not yet computed.
The client MAY drop these packets, or MAY buffer them in anticipation
of later packets that allow it to compute the key.
Paragraph (version A):
If a server receives a packet that has an unsupported version, but
the packet is sufficiently large to initiate a new connection for any
version supported by the server, it SHOULD send a Version Negotiation
packet as described in send-vn.  Servers MAY rate control these
packets to avoid storms of Version Negotiation packets.
Paragraph (version A):
Servers MUST drop other packets that contain unsupported versions.
Paragraph (version A):
If the packet is a 0-RTT packet, the server MAY buffer a limited
number of these packets in anticipation of a late-arriving Initial
Packet.  Clients are forbidden from sending Handshake packets prior
to receiving a server response, so servers SHOULD ignore any such
packets.
Paragraph (version B):
If the packet is a 0-RTT packet, the server MAY buffer a limited
number of these packets in anticipation of a late-arriving Initial
packet.  Clients are not able to send Handshake packets prior to
receiving a server response, so servers SHOULD ignore any such
packets.
Paragraph (version A):
When an Initial packet is sent by a client which has not previously
received a Retry packet from the server, it populates the Destination
Connection ID field with an unpredictable value.  This MUST be at
least 8 bytes in length.  Until a packet is received from the server,
the client MUST use the same value unless it abandons the connection
attempt and starts a new one.  The initial Destination Connection ID
is used to determine packet protection keys for Initial packets.
Paragraph (version A):
The Destination Connection ID field in the server's Initial packet
contains a connection ID that is chosen by the recipient of the
packet (i.e., the client); the Source Connection ID includes the
connection ID that the sender of the packet wishes to use (see
connection-id).  The server MUST use consistent Source Connection IDs
during the handshake.
Paragraph (version B):
Once the handshake completes, if an endpoint is unable to buffer all
data in a CRYPTO frame, it MAY discard that CRYPTO frame and all
CRYPTO frames received in the future, or it MAY close the connection
with a CRYPTO_BUFFER_EXCEEDED error code.  Packets containing
discarded CRYPTO frames MUST be acknowledged because the packet has
been received and processed by the transport even though the CRYPTO
frame was discarded.
Paragraph (version A):
An immediate close can be used after an application protocol has
arranged to close a connection.  This might be after the application
protocols negotiates a graceful shutdown.  The application protocol
exchanges whatever messages that are needed to cause both endpoints
to agree to close the connection, after which the application
requests that the connection be closed.  The application protocol can
use an CONNECTION_CLOSE frame with an appropriate error code to
signal closure.
Paragraph (version A):
If the connection has been successfully established, endpoints MUST
send any CONNECTION_CLOSE frames in a 1-RTT packet.  Prior to
connection establishment a peer might not have 1-RTT keys, so
endpoints SHOULD send CONNECTION_CLOSE frames in a Handshake packet.
If the endpoint does not have Handshake keys, or it is not certain
that the peer has Handshake keys, it MAY send CONNECTION_CLOSE frames
in an Initial packet.  If multiple packets are sent, they can be
coalesced (see packet-coalesce) to facilitate retransmission.
Paragraph (version B):
An endpoint MAY send a stateless reset in response to a packet with a
long header.  Sending a stateless reset is not effective prior to the
stateless reset token being available to a peer.  In this QUIC
version, packets with a long header are only used during connection
establishment.  Because the stateless reset token is not available
until connection establishment is complete or near completion,
ignoring an unknown packet with a long header might be as effective
as sending a stateless reset.
Paragraph (version A):
Application protocols can signal application-specific protocol errors
using the application-specific variant of the CONNECTION_CLOSE frame.
Errors that are specific to the transport, including all those
described in this document, are carried the QUIC-specific variant of
the CONNECTION_CLOSE frame.
Paragraph (version A):
The byte following the version contains the lengths of the two
connection ID fields that follow it.  These lengths are encoded as
two 4-bit unsigned integers.  The Destination Connection ID Length
(DCIL) field occupies the 4 high bits of the byte and the Source
Connection ID Length (SCIL) field occupies the 4 low bits of the
byte.  An encoded length of 0 indicates that the connection ID is
also 0 bytes in length.  Non-zero encoded lengths are increased by
3 to get the full length of the connection ID, producing a length
between 4 and 18 bytes inclusive.  For example, an byte with the
value 0x50 describes an 8-byte Destination Connection ID and a
zero-length Source Connection ID.
Paragraph (version A):
The Destination Connection ID field follows the connection ID
lengths and is either 0 bytes in length or between 4 and 18 bytes.
negotiating-connection-ids describes the use of this field in more
detail.
Paragraph (version A):
The Source Connection ID field follows the Destination Connection
ID and is either 0 bytes in length or between 4 and 18 bytes.
negotiating-connection-ids describes the use of this field in more
detail.
Paragraph (version B):
As future versions of QUIC may support Connection IDs larger than the
version 1 limit, Version Negotiation packets could carry Connection
IDs that are longer than 20 bytes.
Paragraph (version A):
A client SHOULD instead generate a fresh cryptographic handshake
message and start packet numbers from 0.  This ensures that new 0-RTT
packets will not use the same keys, avoiding any risk of key and
nonce reuse; this also prevents 0-RTT packets from previous handshake
attempts from being accepted as part of the connection.
Paragraph (version A):
The four least-significant bits of the first byte of a Retry
packet are not protected as they are for other packets with the
long header, because Retry packets don't contain a protected
payload.  These bits instead encode the length of the Original
Destination Connection ID field.  The length uses the same
encoding as the DCIL and SCIL fields.
Paragraph (version A):
The Original Destination Connection ID contains the value of the
Destination Connection ID from the Initial packet that this Retry
is in response to.  The length of this field is given in ODCIL.
Paragraph (version A):
The next Initial packet from the client uses the connection ID and
token values from the Retry packet (see negotiating-connection-ids).
Aside from this, the Initial packet sent by the client is subject to
the same restrictions as the first Initial packet.  A client can
either reuse the cryptographic handshake message or construct a new
one at its discretion.
Paragraph (version A):
STREAM frames implicitly create a stream and carry stream data.  The
STREAM frame takes the form 0b00001XXX (or the set of values from
0x08 to 0x0f).  The value of the three low-order bits of the frame
type determine the fields that are present in the frame.
Paragraph (version A):
An 8-bit unsigned integer containing the length of the connection
ID.  Values less than 4 and greater than 18 are invalid and MUST
be treated as a connection error of type PROTOCOL_VIOLATION.