number of TCP segments is not guaranteed to map predictably to the size and number of HTTP DATA or QUIC STREAM frames.  The TCP connection can be closed by either peer.  When the client ends the request stream (that is, the receive stream at the proxy enters the "Data Recvd" state), the proxy will set the FIN bit on its

This still seems like a pointless restriction to me, but it's what we agreed in Tokyo, so here it is.
NAME I think I share the concern (though I am not sure how much I care). HTTP/3 is expected to allow extension frames to be used without coordination; this PR forbids that to happen on a stream that uses the CONNECT method.
S 5.2. Can you send anything other than DATA on a CONNECT stream? You don't say that I can't send anything else, but you don't say I can't either.
It doesn't mean anything (because we haven't assigned semantics), but we should probably just forbid the use of other frames (of those defined in this document). This is probably a shortcoming of 7540 as well.
Wouldn’t it be better to just prohibit the HEADERS frame? We might want to introduce a frame that changes a property of a stream (e.g., max bitrate). I do not think there’s a strong reason to forbid such kind of extensions only on CONNECT stream.
I don't want to prohibit extension frames, but the frames defined in the current spec don't serve any purpose. That is why I said "of those defined in this document".
Well, there's technically nothing preventing the use of push.... ;-) More seriously, I don't feel like the prohibition adds much. This maps the definition of the CONNECT method into H3 framing -- DATA frames == TCP payload. In any other respect, it's an HTTP request and frames mean what they otherwise mean. Is there a particular frame that needs to be prohibited? HEADERS would indicate a trailer block; it might be worth upstreaming to the updated semantic draft that a CONNECT tunnel can't have trailers.
Discussed in Tokyo; after the CONNECT takes effect, all frames other than DATA are prohibited unless an extension specifically provides otherwise.
nits

number of TCP segments is not guaranteed to map predictably to the size and number of HTTP DATA or QUIC STREAM frames. <ins> Once the CONNECT method has completed, only DATA frames are permitted to be sent on the stream.  Extension frames MAY be used if specifically permitted by the definition of the extension.  Receipt of any other frame type MUST be treated as a connection error of type HTTP_UNEXPECTED_FRAME. </ins> The TCP connection can be closed by either peer.  When the client ends the request stream (that is, the receive stream at the proxy enters the "Data Recvd" state), the proxy will set the FIN bit on its


QUIC Stream ID for a client-initiated bidirectional stream encoded as a variable-length integer.  A client MUST treat receipt of a GOAWAY frame containing a Stream ID of any other type as a connection error <del> of type HTTP_WRONG_STREAM. </del>  Clients do not need to send GOAWAY to initiate a graceful shutdown; they simply stop making new requests.  A server MUST treat receipt of The GOAWAY frame applies to the connection, not a specific stream.  A client MUST treat a GOAWAY frame on a stream other than the control <del> stream as a connection error (errors) of type HTTP_UNEXPECTED_FRAME. </del>  See connection-shutdown for more information on the use of the GOAWAY frame.

I'm pretty sure the intent of URL was correct but the wrong error was changed in the draft. This pr makes the change that I think was intended in URL
Just a note. If this is the right answer and the PR lands, then will need to replace the new instance of HTTPMALFORMEDFRAME with something, e.g. HTTPINVALIDGOAWAY or something
Yeah, that crossed my mind. :-) YAEC!
In , NAME correctly observed that I messed up , but he closed his PR and I'm not able to reopen it without more git wizardry than I'm interested in practicing today. Easier just to fix it in my own branch.

QUIC Stream ID for a client-initiated bidirectional stream encoded as a variable-length integer.  A client MUST treat receipt of a GOAWAY frame containing a Stream ID of any other type as a connection error  <ins> of type HTTP_MALFORMED_FRAME. </ins> Clients do not need to send GOAWAY to initiate a graceful shutdown; they simply stop making new requests.  A server MUST treat receipt of The GOAWAY frame applies to the connection, not a specific stream.  A client MUST treat a GOAWAY frame on a stream other than the control  <ins> stream as a connection error (errors) of type HTTP_WRONG_STREAM. </ins> See connection-shutdown for more information on the use of the GOAWAY frame.


The HEADERS frame (type=0x1) is used to carry a header block, compressed using QPACK.  See QPACK for more details. <del> HEADERS frames can only be sent on request / push streams. </del>  7.2.3.

Receiving a HEADERS frame on a control stream should result in a connection error of type HTTPWRONGSTREAM. The correct error is documented for all other frame types except for HEADERS. This pr specifies the error for HEADERS frames as well.
This inconsistency was bugging me the other day. So thanks for beating me to fixing it :) NAME iff gets traction, we'd need a sweep of all these If a HEADERS frame is received on either control stream phrases to cover all streams (exclusively or inclusively).
NAME was closed. Is there anything else that's blocking this pr from being merged?
NAME I added NAME so he can review and merge this.

The HEADERS frame (type=0x1) is used to carry a header block, compressed using QPACK.  See QPACK for more details.  <ins> HEADERS frames can only be sent on request / push streams.  If a HEADERS frame is received on a control stream, the recipient MUST respond with a connection error (errors) of type HTTP_WRONG_STREAM. </ins> 7.2.3.


the client has advertised or a Push ID which has already been promised as a connection error of type HTTP_MALFORMED_FRAME. <del> If a PUSH_PROMISE frame is received on either control stream, the recipient MUST respond with a connection error (errors) of type </del>  HTTP_WRONG_STREAM.  See server-push for a description of the overall server push mechanism.

This pr changes the server's error on receipt of a PUSHPROMISE frame. While the current draft implicitly suggests push promises can only be sent by the server, it doesn't explicitly disallow clients from sending PUSHPROMISE frames. This pr explicitly disallows clients from sending PUSHPROMISE frames and requires the server to treat each receipt of a PUSHPROMISE frame as a connection error of type HTTPUNEXPECTEDFRAME.

the client has advertised or a Push ID which has already been promised as a connection error of type HTTP_MALFORMED_FRAME.  <ins> If a PUSH_PROMISE frame is received on the control stream, the client MUST respond with a connection error (errors) of type </ins> HTTP_WRONG_STREAM. <ins> A client MUST NOT send a PUSH_PROMISE frame.  A server MUST treat the receipt of a PUSH_PROMISE frame as a connection error of type HTTP_UNEXPECTED_FRAME. </ins> See server-push for a description of the overall server push mechanism.


SETTINGS frames always apply to a connection, never a single stream. A SETTINGS frame MUST be sent as the first frame of each control stream (see control-streams) by each peer, and MUST NOT be sent <del> subsequently or on any other stream.  If an endpoint receives a SETTINGS frame on a different stream, the endpoint MUST respond with a connection error of type HTTP_WRONG_STREAM.  If an endpoint receives a second SETTINGS frame, the endpoint MUST respond with a connection error of type HTTP_UNEXPECTED_FRAME. </del>  SETTINGS parameters are not negotiated; they describe characteristics of the sending peer, which can be used by the receiving peer.

by clearly splitting the two different error cases.
Moves existing text to be clearer, but doesn't change the stated requirements. Editorial, and fine by me.
It's unclear which error should be reported if an endpoint receives a second SETTINGS frame on any stream that's not a control stream. Perhaps this should be explicitly defined in the draft?
Hey NAME I initially thought this was an editorial problem so created as an attempt to clarify things. However, I think the question is more philosophical. To analyse it a bit, what you're asking is how to handle an error after the occurrence of a critical connection error. First, a SETTINGS frame on a wrong stream type, then another SETTINGS frame on a wrong stream. After the first critical error, all bets are off. I'd expect a lot implementations to just give up at that stage as the peer is likely to be misbehaving.
I agree its an edge case but I do think it'd be useful to exactly specify which of the two errors should be reported, especially when thinking of automated testing of HTTP/3 implementations.
I'm sympathetic to that but I fear this could be a slippery slope towards adding lots of text for issues that occur after a critical close event. PRIORITY frames fall into a similar bucket, along with many of the server push frames and their validation rules.
I think the real unclarity in the spec is that both can happen simultaneously. After receiving a proper SETTINGS frame on the control channel, receiving a SETTINGS frame on some other stream would be both a second SETTINGS frame and a SETTINGS frame on the wrong stream. clarifies this reasonable. Thanks, both of you!

SETTINGS frames always apply to a connection, never a single stream. A SETTINGS frame MUST be sent as the first frame of each control stream (see control-streams) by each peer, and MUST NOT be sent  <ins> subsequently.  If an endpoint receives a second SETTINGS frame on the control stream, the endpoint MUST respond with a connection error of type HTTP_UNEXPECTED_FRAME. SETTINGS frames MUST NOT be sent on any steam other than the control stream.  If an endpoint receives a SETTINGS frame on a different stream, the endpoint MUST respond with a connection error of type HTTP_WRONG_STREAM. </ins> SETTINGS parameters are not negotiated; they describe characteristics of the sending peer, which can be used by the receiving peer.


The "chunked" transfer encoding defined in Section 4.1 of RFC7230 MUST NOT be used.  Trailing header fields are carried in an additional HEADERS frame following the body.  Senders MUST send only one HEADERS frame in the trailers section; receivers MUST discard any subsequent HEADERS

reincarnated, because I messed up. Sorry, NAME
LGTM but don't forget push

The "chunked" transfer encoding defined in Section 4.1 of RFC7230 MUST NOT be used. <ins> If a DATA frame is received before a HEADERS frame on a either a request or push stream, the recipient MUST respond with a connection error of type HTTP_UNEXPECTED_FRAME (errors). </ins> Trailing header fields are carried in an additional HEADERS frame following the body.  Senders MUST send only one HEADERS frame in the trailers section; receivers MUST discard any subsequent HEADERS


this connection, while requests on the indicated stream ID and greater were rejected.  This enables client and server to agree on which requests were accepted prior to the connection shutdown.  This <del> identifier MAY be lower than the stream limit identified by a QUIC MAX_STREAM_ID frame, and MAY be zero if no requests were processed. Servers SHOULD NOT increase the QUIC MAX_STREAM_ID limit after sending a GOAWAY frame. </del>  Clients MUST NOT send new requests on the connection after receiving GOAWAY; a new connection MAY be established to send additional might already have retried unprocessed requests on another connection.  A server that is attempting to gracefully shut down a connection SHOULD send an initial GOAWAY frame with the last Stream <del> ID set to the current value of QUIC's MAX_STREAM_ID and SHOULD NOT increase the MAX_STREAM_ID thereafter.  This signals to the client that a shutdown is imminent and that initiating further requests is prohibited.  After allowing time for any in-flight requests (at least one round-trip time), the server MAY send another GOAWAY frame with an updated last Stream ID.  This ensures that a connection can be cleanly shut down without losing requests. </del>  Once all accepted requests have been processed, the server can permit the connection to become idle, or MAY initiate an immediate closure

TBH I'm not sure the whole "set GOAWAY last stream id to MAXSTREAMID / maximum value allowed by MAX_STREAMS" is really necessary, but I kept it for now.

this connection, while requests on the indicated stream ID and greater were rejected.  This enables client and server to agree on which requests were accepted prior to the connection shutdown.  This  <ins> identifier MAY be zero if no requests were processed.  Servers SHOULD NOT increase the QUIC MAX_STREAMS limit after sending a GOAWAY frame. </ins> Clients MUST NOT send new requests on the connection after receiving GOAWAY; a new connection MAY be established to send additional might already have retried unprocessed requests on another connection.  A server that is attempting to gracefully shut down a connection SHOULD send an initial GOAWAY frame with the last Stream  <ins> ID set to the maximum value allowed by QUIC's MAX_STREAMS and SHOULD NOT increase the MAX_STREAMS limit thereafter.  This signals to the client that a shutdown is imminent and that initiating further requests is prohibited.  After allowing time for any in-flight requests (at least one round-trip time), the server MAY send another GOAWAY frame with an updated last Stream ID.  This ensures that a connection can be cleanly shut down without losing requests. </ins> Once all accepted requests have been processed, the server can permit the connection to become idle, or MAY initiate an immediate closure


It is recommended that "initial_max_bidi_streams" be no smaller than 100, so as to not unnecessarily limit parallelism. <del> These streams carry frames related to the request/response (see request-response).  When a stream terminates cleanly, if the last frame on the stream was truncated, this MUST be treated as a connection error (see HTTP_MALFORMED_FRAME in http-error-codes). Streams which terminate abruptly may be reset at any point in the frame. </del> HTTP/3 does not use server-initiated bidirectional streams; clients MUST omit or specify a value of zero for the QUIC transport parameter "initial_max_bidi_streams". the end of the identified fields MUST be treated as a connection error of type HTTP_MALFORMED_FRAME.  7.2. 7.2.1.

Until now, only bidirectional streams were specified to treat a truncated last frame on a cleanly terminated stream as an error. With this pr, the same logic applies to push streams as well. I just slightly reworded the text from the bidirectional streams section and put it into the push stream section. Not sure if this is the best solution but I'm also not sure where to put the text if the text for both the bidirectional streams and push streams had to be combined.
already contains this text: That's intended to cover any stream type with frames. Perhaps we should consolidate the language from both sections into that, possibly leaving behind cross-references.
I hadn't seen that paragraph yet. I'd propose to drop the text in the Bidirectional Streams section. The reasoning is that when searching for what to do when a stream is closed with a truncated frame, I found (or rather was pointed to) the text in the Bidirectional Streams section. After finding a paragraph like that, I didn't expect to find any other text in the rfc related to truncated frames (which led to this pr). Without the text in the Bidirectional Streams section, I might have found the text in the Frame Layout section first which would have immediately answered my question. This would also consolidate errors related to frames in the HTTP Framing Layer section which seems like the first place to go looking for frame related errors. Should I make another pr (if this sounds like a good idea) or update this one with the proposed change?
Fine to just update this one. Thanks!
This is the best I could come up with. I had to relax the text from Frame Layout to allow truncated frames on abruptly closed streams. Maybe abruptly closed stream should be clarified better (I'm not sure if this is done elsewhere already?).
Reworded so as to not change the original text as much. The pr now just moves the truncation section from the streams section to the frames section.

It is recommended that "initial_max_bidi_streams" be no smaller than 100, so as to not unnecessarily limit parallelism.  HTTP/3 does not use server-initiated bidirectional streams; clients MUST omit or specify a value of zero for the QUIC transport parameter "initial_max_bidi_streams". the end of the identified fields MUST be treated as a connection error of type HTTP_MALFORMED_FRAME. <ins> When a stream terminates cleanly, if the last frame on the stream was truncated, this MUST be treated as a connection error (errors) of type HTTP_MALFORMED_FRAME.  Streams which terminate abruptly may be reset at any point in a frame. </ins> 7.2. 7.2.1.


An endpoint SHOULD ensure that its peer has a sufficient number of available and unused connection IDs.  Endpoints store received <del> connection IDs for future use.  An endpoint uses the active_connection_id_limit transport parameter to advertise the number of connection IDs it can store for future use.  An endpoint SHOULD NOT provide more connection IDs than the peer's limit.  If an endpoint has provided its peer with the maximum number of connection IDs, it SHOULD only provide a new connection ID when the peer retires one.  An endpoint MAY limit the frequency or the total number of connection IDs issued for each connection to avoid the risk of running out of connection IDs (see reset-token). </del>  An endpoint that initiates migration and requires non-zero-length connection IDs SHOULD ensure that the pool of connection IDs

Reword the text to be a bit more clear about what's expected from a CID issuer.
I believe this is editorial, since it does not change any normative text.
This inverts the form of the recommendation, but doesn't really change the design.
We previously had text: In adding text for the TP (activeconnectionid_limit), we kept the same idea, but lost a little bit of the text: We still need the SHOULD replace used CIDs in the event that we haven't provided the peer with the maximum number of CIDs.
Putting together a PR for this

An endpoint SHOULD ensure that its peer has a sufficient number of available and unused connection IDs.  Endpoints store received  <ins> connection IDs for future use and advertise the number of connection IDs they are willing to store with the active_connection_id_limit transport parameter.  An endpoint SHOULD NOT provide more connection IDs than the peer's limit. An endpoint SHOULD supply a new connection ID when it receives a packet with a previously unused connection ID or when the peer retires one, unless providing the new connection ID would exceed the peer's limit.  An endpoint MAY limit the frequency or the total number of connection IDs issued for each connection to avoid the risk of running out of connection IDs; see reset-token. </ins> An endpoint that initiates migration and requires non-zero-length connection IDs SHOULD ensure that the pool of connection IDs


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 <del> packets to avoid storms of Version Negotiation packets. </del>  The first packet for an unsupported version can use different semantics and encodings for any version-specific field.  In unknown version, but instead send a Version Negotiation packet, provided that the packet is sufficiently long. <del> Servers MUST drop other packets that contain unsupported versions. </del> Packets with a supported version, or no version field, are matched to a connection using the connection ID or - for packets with zero- length connection IDs - the address tuple.  If the packet doesn't sets the Destination Connection ID it sends to match the value of the Source Connection ID that they receive. <del> 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. </del>  The client populates the Source Connection ID field with a value of <del> its choosing and sets the SCIL field to indicate the length. The first flight of 0-RTT packets use the same Destination and Source </del>  Connection ID values as the client's first Initial. <del> 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. On first receiving an Initial or Retry packet from the server, the </del>  client uses the Source Connection ID supplied by the server as the Destination Connection ID for subsequent packets, including any subsequent 0-RTT packets.  That means that a client might change the

Qualifies a MUST with an 'otherwise', since otherwise it would appear to directly contradict previous normative text.

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  <ins> packets to avoid storms of Version Negotiation packets.  Otherwise, servers MUST drop packets that specify unsupported versions. </ins> The first packet for an unsupported version can use different semantics and encodings for any version-specific field.  In unknown version, but instead send a Version Negotiation packet, provided that the packet is sufficiently long.  Packets with a supported version, or no version field, are matched to a connection using the connection ID or - for packets with zero- length connection IDs - the address tuple.  If the packet doesn't sets the Destination Connection ID it sends to match the value of the Source Connection ID that they receive.  <ins> When an Initial packet is sent by a client that has not previously received an Initial or 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. </ins> The client populates the Source Connection ID field with a value of  <ins> its choosing and sets the SCIL field to indicate the length.  The first flight of 0-RTT packets use the same Destination and Source </ins> Connection ID values as the client's first Initial.  <ins> Upon first receiving an Initial or Retry packet from the server, the </ins> client uses the Source Connection ID supplied by the server as the Destination Connection ID for subsequent packets, including any subsequent 0-RTT packets.  That means that a client might change the


A denial of service attack is possible if the same connection ID is used by instances that share a static key, or if an attacker can cause a packet to be routed to an instance that has no state but the <del> same static key (see reset-oracle).  A connection ID from a connection that is reset by revealing the Stateless Reset Token MUST NOT be reused for new connections at nodes that share a static key. </del>  Note that Stateless Reset packets do not have any cryptographic protection.

This came up as the result of an incomplete implementation, but there are - maybe - good reasons to have the same stateless reset token being used for multiple connection IDs on the same connection. There are drawbacks however. Reuse of a stateless reset token means that you have to remember (and consistently route) connection IDs, even when they are retired. This text makes a note of the possibility and explains the drawbacks.
As part of the discussions on / , we've found that reusing stateless reset tokens (SRTs) can allow attackers that delay packets to close connections. Even though the text in does a good job explaining why you shouldn't reuse SRTs, I don't see any reason why one would want to reuse SRTs across connection IDs in the same connection. Therefore I would prefer we ban reusing them, and make it a PROTOCOL_VIOLATION to do so. Apologies for not raising this on , I had not thought about the problem at that time.
I do not think you missed anything. Instead, you spotted the issue at the exact moment; it's my understanding that is introducing this issue. There is no problem in the current design, because receiving a packet with an outdated CID is not expected to cause a stateless reset. Regarding why you'd want to reuse a SRT, the simple reason is because you do not need to create different SRTs for every Connection ID. A QUIC stack that generates a CID by a construction like 1 can use as a construction for generating SRT. For QUIC stacks that aren't interested in retiring CIDs (which seems to be the majority for me), this construction makes sense. I think my preference would be to retain the design; it's just about adding a warning when you want to retire a CID. 1: path_index is a sequence number of path for each connection
I don't think introduces this issue. It does make it worse but the issue was already present. It's reasonable for a server implementation to drop a CID from their state when the client retires it. There are two options to guarantee safety: make sure each CID has a distinct SRT make sure once a CID is sent in NEWCONNECTIONID it always routes back to this connection I think (1) is simpler and giving implementations two options here not only makes the spec more complicated, but can also lead to accidental vulnerabilities. I think it would be safer for the implementations you mention to use to generate their SRT. That also makes it simpler to generate the SRT from the cleartext CID. In general, it's been this spec philosophy to add constraints that can be verified by the peer whenever possible. Forcing distinct CIDs ensures verifiable security whereas requiring that CIDs get routed appropriately for the duration of the connection is not enforceable and therefore easier to get wrong.
Essentially, the current text says that an issuer can't actually forget a retired CID until all CIDs with the same SRT have been retired. At that point and not before, you can forget all of them at once. doesn't change this at all -- doesn't even really make it worse. That PR provides a mechanism for asking a client to retire a certain set of CIDs; if the set only partially overlaps with the set of CIDs covered by an SRT, that mechanism is not terribly useful because you can't actually forget all the CIDs you asked the peer to retire. It could be very useful to ask the peer to retire the last CID in one of these groups because you've got 200 of them you'd really like to clear out. But that doesn't change the core fact: An issuer can't actually forget a retired CID until all CIDs with the same SRT have been retired. If each SRT maps uniquely to a CID, this is a simple requirement to satisfy. If you choose to do something else, that's your problem.
I think what NAME says is correct. OTOH, I now think that forbidding reuse of SRT might help the receiver of the stateless resets. Without the prohibition, when retiring a CID, an endpoint needs to consult other SRTs that have been issued for the same connection to see if it can unregister the SRT corresponding to the CID being retired, or use a ref-counted hashmap for maintaining the mapping from SRTs to connections. We might argue that this requirement is easy to miss, and hard to test. Having the prohibition removes this requirement and therefore can be considered as a simplification. Contrary to that, we might argue that allowing the reuse of SRT has marginal benefit. It is trivial to construct different SRT for each CID, as pointed out by NAME
NAME analysis is entirely correct. I don't think that we should put in rules to prevent self-harm through idiocy. And this is definitely a case of that. An endpoint that forgets a CID when the associated SRT is active only hurts themselves. The text is sufficient as it stands. A prohibition won't prevent implementations from doing dangerous things. As it stands, you need to take extraordinary steps to put yourself in position to mess this up. The construction NAME describes is a totally sensible one, but it is very hard to forget connection IDs with that scheme. More relevant to this, the SRT construction process described in the spec won't have this problem. What exactly do you think this prohibition will achieve?
If there is a prohibition, an endpoint can remove the SRT of the CID being retired, without consulting the SRTs of other CIDs belonging to the same connection. While incorrect in terms of current spec, such design works flawlessly with endpoints that issue different SRT for each CID. It seems that most implementations would issue different SRT for each CID. Then, there's chance that we might see stacks implementing this incorrect but simpler approach. Having the prohibition eliminates the chance of seeing these "incorrectness."
I agree that we should remove the text that allows the reuse of SRTs from the spec (In fact, last night I was planning to open exactly this issue, and woke up this morning to see that NAME already did so while I was asleep). I don’t see any reason why you’d want to reuse a SRT in the first place - computing one HMAC shouldn’t be more expensive than applying the packe protection to send a single packet. With regards to how you store connection IDS, this design is only simpler if you do the wrong thing and don‘t keep track of expired connection IDs that still have active SRTs. Doing it the right way will most likely result in a more complex design than associating each CID with its own SRT. In short: it’s easy to mess up, and there’s little benefit, so let’s remove it.
I'm not totally against a unique SRT per CID, but I am concerned about the static memory use of otherwise passive connections. If a CID and SRT can be 255 bytes each (depending on where discussions go) that is half a kilobyte per CID, and there might be 8 of those, possibly more. That is 4K per connection even if you retire all active buffers because there isn't currently any traffic.
NAME SRTs are 16 bytes long
NAME I'm not sure about that - isn't there text about hiding SRT among ordinary traffic. If so it cannot be shorter than the CID + overhead, well the packet that is. But I'm probably mixing up packet size with storage requirements.
The SRT is only 16 bytes, but the packet it's sent in is supposed to look like a typical QUIC short header packet, and therefore needs to be of a minimum size.
So essentially, we're saying that we maybe didn't really have consensus on the answer to being "yes." But since there was a consensus call that said we did, now the question is whether we have consensus to reverse that decision. Let me suggest that be taken to the list.
Discussed in Montreal, next step is for NAME to do a PR and follow normal process
Conclusion: prohibit the use of duplicate tokens. Allow, but not require the use of PROTOCOL_VIOLATION if a duplicate is detected.
covered this.
We hit this during interop, where one implementation would re-issue the same SRT(Stateless Reset Token) it included in the TPs in each new NEWCONNECTIONID frame. The question was raised whether that was allowed or not, and NAME decided that "design text was needed."
LGTM. Quicly was originally implemented this way, though we stopped doing that in the CID generation scheme that we use as the default. When an endpoint constructs a CID as an encrypted tuple of (connid1, pathid), there's no need to regenerate a token for every new path ID being generated. 1: An ID that identifies a QUIC connection. Not the CID that appears on the wire.

A denial of service attack is possible if the same connection ID is used by instances that share a static key, or if an attacker can cause a packet to be routed to an instance that has no state but the  <ins> same static key; see reset-oracle.  A connection ID from a connection that is reset by revealing the Stateless Reset Token MUST NOT be reused for new connections at nodes that share a static key. The same Stateless Reset Token MAY be used for multiple connection IDs on the same connection.  However, reuse of a Stateless Reset Token might expose an endpoint to denial of service if associated connection IDs are forgotten while the associated token is still active at a peer.  An endpoint MUST ensure that packets with Destination Connection ID field values that correspond to a reused Stateless Reset Token are attributed to the same connection as long as the Stateless Reset Token is still usable, even when the connection ID has been retired.  Otherwise, an attacker might be able to send a packet with a retired connection ID and cause the endpoint to produce a Stateless Reset that it can use to disrupt the connection, just as with the attacks in reset-oracle. </ins> Note that Stateless Reset packets do not have any cryptographic protection.


A Retry packet uses a long packet header with a type value of 0x3. It carries an address validation token created by the server.  It is <del> used by a server that wishes to perform a stateless retry (see validate-handshake). </del>  A Retry packet (shown in retry-format) does not contain any protected fields.  In addition to the long header, it contains these additional

ekr says there used to be stateless and stateful retry, but now there is just stateless. This is the only place the phrase is used, so instead just say "retry".

A Retry packet uses a long packet header with a type value of 0x3. It carries an address validation token created by the server.  It is  <ins> used by a server that wishes to perform a retry (see validate- handshake). </ins> A Retry packet (shown in retry-format) does not contain any protected fields.  In addition to the long header, it contains these additional


unaware middleboxes, but not against on-path attackers. QUIC can use any of the ciphersuites defined in TLS13 with the <del> exception of TLS_AES_128_CCM_8_SHA256.  The AEAD for that ciphersuite, AEAD_AES_128_CCM_8 CCM, does not produce a large enough authentication tag for use with the header protection designs provided (see header-protect).  All other ciphersuites defined in TLS13 have a 16-byte authentication tag and produce an output 16 bytes larger than their input. </del>  The key and IV for the packet are computed as described in protection-keys.  The nonce, N, is formed by combining the packet To ensure that sufficient data is available for sampling, packets are padded so that the combined lengths of the encoded packet number and protected payload is at least 4 bytes longer than the sample required <del> for header protection.  For the AEAD functions defined in TLS13, which have 16-byte expansions and 16-byte header protection samples, this results in needing at least 3 bytes of frames in the unprotected payload if the packet number is encoded on a single byte, or 2 bytes of frames for a 2-byte packet number encoding. </del>  The sampled ciphertext for a packet with a short header can be determined by the following pseudocode:

For input to discussion on the related issues. This says that we don't do CCM_8 and makes it clear that you don't reject an attempt to use a ciphersuite you don't understand. This is an editorial resolution to the design issue. , .
LGTM
I think I must be entirely missing the point, but I don't understand this: Can you please explain how this works? For the sake of argument, TLSAES128CCM8_SHA256 is not supported, but the peer chooses to use it, and the TLS engine behind it all decides to support it via some configuration that the endpoint is directly aware of. How does the endpoint now deal with header protection. Is it assumed that this is a TLS black box so it just magically works if the TLS engine has accepted the cipher via some useful API call. If it is a TLS blackbox API call, how would that work with HW offloading?
Now I understand it. Being offered by TLS is not the same as being used by any of the endpoints. Sorry for the noise.
Suppose that we had a future TLS cipher suite such as AESOCBSHA256. Would that work with QUIC without a new QUIC draft? The AEAD part is already clearly defined and given that AES-OCB uses AES, the same HP mechanism as we currently use with GCM can be used here. From my perspective, the desirable property is that if we add new TLS cipher suites with analogous cipher cores (block cipher and/or nonce-based stream cipher) to the existing spec, then no new QUIC document should be required. An alternative design would be to just require a "QUIC applicability" paragraph in new TLS cipher suites analogous to the DTLS-OK IANA flag. (see also ).
How about header protection which depends on being defined for the negotiated AEAD?
That's what paragraph 2 above is about.
As discussed in , we did not originally have a good way to make AEADAES128CCM8 work in QUIC, because it needs a 16 byte sample for header protection but only produces an 8 byte auth tag. However, since then we have merged , and it's therefore easy to pad short packets by 8 extra bytes when using CCM_8. There might be QUIC use-cases that could benefit from the shorter tags, and it would be nice to have QUIC support all the original TLS 1.3 cipher suites.
+1 CCM_8 is big in environments like broadcast production
This just adds an odd error-prone special case for padding, and the key benefit of being short is lost on short packets due to padding.
I think we can accommodate this in QUIC v1. The change has zero impact on implementations not supporting CCM8. Code can be left as-is. Specification-wise the required change would be to alter the definition of minimal payload size from 4 to ).
I disagree. It's only the very short packets that do not have the benefit. Longer packets do not require additional padding, and will benefit from the use of the shorter tag.
I don't think that we need this. The reasons offered thus far amount to "because we can". I'd like to see some analysis suggesting that shorter authentication tags are OK in the general case. Or text explaining constraints on deployments that choose to use a shorter tag. I understand the benefits of CCM over GCM; I understand that this includes an ability to have a shorter tag, unlike GCM which fails badly. I understand the desire to save bits. But it isn't really that many bits, and the shorter tag changes the odds of an invalid packet being accepted fairly significantly.
NAME can you elaborate on what makes QUIC different from TLS that suggests AES128CCM_8 is useful in TLS but not in QUIC?
It's worth remembering that QUIC packet sizes are (typically) smaller than TLS record size which means that the consequences of saving bytes are larger in QUIC than in TLS.
Well, we generally discourage the use of CCM_8 in TLS as well. We mostly only added it for IoT purposes.
NAME QUIC is also applicable to IoT, CCM_8 should be supported but discouraged here as well.
Is there a strong argument to diverge from existing TLS practice here?
CCM8 is better suitet for a separate QUIC version for IoT where header protection and padding is also dropped and where the traffic volume is low. A cheaper handshake would also be needed. Someone said CCM8 is commonly used with video AFAIR.
NAME I don't think encouraging protocol fragmentation is a good idea. The reason TLS 1.3 has CCM_8 is to allow IoT use-cases to use TLS 1.3 instead of making them develop new protocols. The same argument applies to QUIC.
Q: Should we allow it for QUIC, but not allow it for HTTP/3?
No, the application layer shouldn't deal with transport security. If the security properties changes, it should be a new QUIC version.
I agree with NAME on this one. HTTP/2 doesn't disallow CCM_8 when run over TLS 1.3.
But if I argue that CCM8 changes security properties, you might disagree since that would move CCM8 to a new QUIC version.
NAME this doesn't seem like a very persuasive analogy, given that HTTP/2 has a long list of rules about disabling ciphers that were present in TLS 1.2: URL The reason that it doesn't have that for TLS 1.3 is that (1) TLS 1.3 post-dates HTTP/2 and (2) the suites were chosen according to the same principles Second, as a practical matter, most browsers do not support CCM_8, and I suspect that will continue to be true for most stacks, partly because performance is not awesome (as it requires computing CBC). If, as NAME implies, we want to have a cipher with a shorter authentication tag (I'm undecided on this topic myself at present) for general use then we should consult CFRG for one, rather than just use the one that happened to be the one that IoT devices supported.
I would personally have preferred to include AES128CCM8 in the QUIC spec to have parity with TLS 1.3, but I can live with the text from because that allows someone to write a one-page spec on how to use AES128CCM8 in QUIC without risking interoperability issues. I'm OK with closing this issue with no further action at this point.
LGTM

unaware middleboxes, but not against on-path attackers. QUIC can use any of the ciphersuites defined in TLS13 with the  <ins> exception of TLS_AES_128_CCM_8_SHA256.  A ciphersuite MUST NOT be negotiated unless a header protection scheme is defined for the ciphersuite.  This document defines a header protection scheme for all ciphersuites defined in TLS13 aside from TLS_AES_128_CCM_8_SHA256.  These ciphersuites have a 16-byte authentication tag and produce an output 16 bytes larger than their input. An endpoint MUST NOT reject a ClientHello that offers a ciphersuite that it does not support, or it would be impossible to deploy a new ciphersuite.  This also applies to TLS_AES_128_CCM_8_SHA256. </ins> The key and IV for the packet are computed as described in protection-keys.  The nonce, N, is formed by combining the packet To ensure that sufficient data is available for sampling, packets are padded so that the combined lengths of the encoded packet number and protected payload is at least 4 bytes longer than the sample required  <ins> for header protection.  The ciphersuites defined in TLS13 - other than TLS_AES_128_CCM_8_SHA256, for which a header protection scheme is not defined in this document - have 16-byte expansions and 16-byte header protection samples.  This results in needing at least 3 bytes of frames in the unprotected payload if the packet number is encoded on a single byte, or 2 bytes of frames for a 2-byte packet number encoding. </ins> The sampled ciphertext for a packet with a short header can be determined by the following pseudocode:


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 <del> 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. </del>  A client MAY attempt 0-RTT after receiving a Retry packet by sending 0-RTT packets to the connection ID provided by the server.  A client <del> that sends additional 0-RTT packets without constructing a new cryptographic handshake message MUST NOT reset the packet number to 0 after a Retry packet; see packet-0rtt. </del>  A server acknowledges the use of a Retry packet for a connection using the original_connection_id transport parameter (see transport-

As agreed in London and Tokyo.
We already have an editorial issue in this section; it needs breaking into subsections rather than combining long paragraphs into even longer ones. Thanks for catching the packet number thing.
I've been trying to reason about Retry and in particular the following text: This seems like it really is saying that a Retry is a new connection, and in particular that the server cannot memorize the CRYPTO from the previous Initial (which it might otherwise choose to do). I'm still trying to figure out if this is the right answer, but it seems confusing to allow the client to have both behaviors, especially as we encourage the "construct a new one". Also, if you construct a new one, could you potentially reuse the KeyShare values (might be an issue with PQ). I'm not quite sure what I think about this, but I wanted to post an issue to see if other people think it's weird and if so, maybe we should take a more prescriptive stance.
I think that it's OK to resend the same cryptographic handshake message. The semantics of Retry mean that the first wasn't used. The only thing that might be a concern is if an attacker can use this to cause two parallel connections to be established from the same ClientHello. I don't think that's possible, and the protocol is structured in a way that avoids that. (Your point about reuse and PQ does make me think that the stuff NAME was contemplating as a defense against packet injection is potentially dangerous in that context. I'm glad that there are now other approaches being contemplated for that.)
It is the same connection. Otherwise, the originalconnectionid transport parameter has no clear meaning.
What NAME says makes sense. We may want to tease out the text a bit to articulate this point, but I think it's part of the same connection establishment and therefore part of the same connection.
Tokyo conclusion: It's the same connection. We will prohibit changing the cryptographic handshake message based on receiving Retry. You can think of this as re-sending a lost Initial.
Not quite, since a retry has a fixed target DCID (stateful) whereas a lost initial has a random OCID that can reach a random server (stateless)
IIUC, we are going to require the Hello to be the same and to not reset the PN when receiving a Retry. Assuming that is true, do we need to keep the statement that says that the server CID "MUST not be equal to the Destination Connection ID field of the packet sent by the client"? I think we can remove it now (for simplicity). See also:
NAME that requirement also exists to ensure that the server doesn't fake out the ODCID transport parameter. I think we're should keep that bit.
NAME Ah thank you for pointing out that. Agreed.
London discussion: NAME to include the last editorial bits in the spec
LGTM modulo the point below.

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  <ins> the same restrictions as the first Initial packet.  A client MUST use the same cryptographic handshake message it includes in this packet. A server MAY treat a packet that contains a different cryptographic handshake message as a connection error or discard it. </ins> A client MAY attempt 0-RTT after receiving a Retry packet by sending 0-RTT packets to the connection ID provided by the server.  A client  <ins> MUST NOT change the cryptographic handshake message it sends in response to receiving a Retry. A client MUST NOT reset the packet number for any packet number space after processing a Retry packet; packet-0rtt contains more information on this. </ins> A server acknowledges the use of a Retry packet for a connection using the original_connection_id transport parameter (see transport-


0-RTT, Handshake, 1-RTT) makes it more likely the receiver will be able to process all the packets in a single pass.  A packet with a short header does not include a length, so it can only be the last <del> packet included in a UDP datagram. </del>  Senders MUST NOT coalesce QUIC packets for different connections into a single UDP datagram.  Receivers SHOULD ignore any subsequent

This is a SHOULD per agreement in London because a) we can't decide why you might not, but b) we can't mandate any particular action on the part of a receiver.
The spec says It makes sense to continue processing if the keys are not available. However, if the keys are available, and decryption fails, this means that the peer sent an invalid packet (or that we're dealing with an on-path attacker, which we can't do anything against anyway). It seems that it would be equally valid to stop processing the datagram in that case. Since it's possible to coalesce about 70 QUIC packets into a single 1200 byte datagram, an attacker could cause a peer to attempt 70 AEAD operations with a single datagram.
Doesn't decryption failure happen also when the packet gets corrupt while being transmitted? Consider a coalesced packet sent by client that contains Initial (for ACKing ServerHello) and a Handshake packet. I'd assume that we'd prefer processing Handshake packet even if Initial was corrupt. Does that matter, considering the fact that an attacker can force AEAD operation for every 21 bytes by using a short header packet (1 type + 4 byte space + 16 byte AEAD tag)?
I assumed those to be very rare, so treating the whole datagram as corrupted might be a valid response. I don't have any numbers about that though. Maybe we can ask NAME if he can share some measurements? The assumption here is that sending a lot of small packet is more expensive than sending one large packet of the same payload size. First, there's UDP and IP overhead that we're not counting here, and second (and probably more importantly), you only need a single syscall. Looking at the numbers, I think I messed up my calculation of how many coalesced packets fit into a single datagram, since I failed to account for the AEAD tag. This brings the number QUIC packets per datagram down to about 35.
That could be true, though I am still not sure if we need to talk specifically about the attack. For example, an attacker can also send a packet that successfully decrypts, that contains many tiny CRYPTO frames (4-bytes each). I'd assume that processing of that would be more costly than AEAD-decrypting every 35 bytes. Having said that, the state of the endpoints are guaranteed to progress even if the following QUIC packets of a coalesced datagram are discarded (due to corruption, etc.). Therefore, I won't disagree to relaxing the requirement from MUST to SHOULD.
I was not suggesting to add language about this attack. My suggestion was to stop processing a datagram once unprotecting one of the QUIC packets fails, although you have the keys available for that encryption level. I don't understand this. Why is processing CRYPTO frames expensive? In my implementation, assembling stream (and crypto stream) data is not an expensive operation.
Because for every 4-byte CRYPTO frame (containing 1 octet of data), you need to decode 2 varints (length and offset fields), check if it's newly received data, and then write it to the buffer. If the data is new, there would be a call to TLS stack that causes the TLS stack to see if the entire handshake message is available, which would turn out not to be true. TLS stack will then buffer the data. After that, the transport layer will remove the newly received data from the transport-level buffer. My assumption is that doing this amount of processing is more heavyweight than running two AES operations and one GCM operation per every 35 bytes. Based on that, I do not think we need a statement that specifies how to handle these tiny CRYPTO frames, or similar types of attacks (e.g., a packet payload containing many ACK frames). The same goes for not specifying how to handle tiny QUIC packets.
From my experience, decryption failure is extraordinarily rare. Sufficiently so it's not worth worrying about or optimizing for cases when one QUIC packet decrypts and another does not.
We talked in Kista about using just such coalesced packets for PMTU probing. I.e. coalescing a short long-header packet that will not decrypt but contains Server's Connection ID with a short-header packet that has the actual payload. If the UDP datagram does get delivered, the long-header packet will be discarded and the short-header packet -- ACKed. If this UDP datagram runs into an MTU problem, the response ICMP will contain Server Connection ID, and it will be routed to the correct backend.
Discussed in Tokyo; NAME will make a PR to prohibit senders from coalescing packets of different encryption levels and to permit (but not require) receivers to enforce this constraint.
Fine, as long as a violation will not DoS the connection, since a middle box can add random data to a UDP datagram.
That is a good point. I think that the idea would be that a connection error would only be permissible if there were multiple packets of the same encryption level that could be successfully authenticated. Endpoints would otherwise be permitted to drop any extra packets. Note that you can just drop extra packets without checking if they decrypt.
What's going to prevent a man-on-a-side from recording packets with different encryption levels and then sending a coalesced packet? Both will decrypt. You do not want to kill a connection that way.
NAME I believe you meant prohibit senders from coalescing packets of the same(not different) encryption level, correct?
Yes, but NAME point made me reconsider. I think that the rule would need to be: a) senders MUST NOT coalesce packets of the same encryption level (there is no need to do so, so this is basically a no-op requirement); b) receivers SHOULD/MAY/MUST? discard packets if a packet of the same encryption level was received in the same datagram. Unfortunately we can't do any more than this because an attacker has complete control over coalescing. I realize now that this dovetails nicely with the requirement we already added here to require exactly one Retry or Version Negotiation in response to a packet.
I think b being MAY is what we agreed at the Interim. While I understand that some are concerned about this being used as an attack vector, there are people who do not care about this (including me), because there are more attractive ways to enforce the server to do more calculation. MAY is a good compromise.
I'm not (that) concerned about calculation. I'm concerned about disconnects. Either due to changes by an attacker or due to a random error in the packet.
I am a bit worried when I hear suggestions of disabling coalescing with different encryption level. Initial(CH) + 0RTT is fairly common, and useful. So is Initial(SH) + handshake, Initial(ACK) + Handshake(ACK), or Handshake(Finished) + 1RTT. Yes, I can see attacks with "Christmas tree" packets containing too many coalesced segments. But mitigation is obvious -- implementations are free to drop packets for whatever reason. In the rare cases of false positive, when the funny coalesced packet is not in fact an attack, the peer will just resend after at time out.
What if it resends to the same "Christmas tree", for example for probing to get source CID?
I don't think this issue warrants changing the protocol specification. You may want to document the potential attack in the security section, and suggest mitigations. But IMHO this is one of the many ways in which a client can be nasty with a server, and vice versa. There are many other examples, such as acking packets one by one, sending multiple 1 byte stream frames, randomly alternating between multiple streams, etc. The generic pattern is to find some operation that is expensive for a particular server implementation, and then abuse it. You can't possibly document all the ways of doing that.
My point was that it is hard to define what is a valid abuse filter without risking not being able to connect with client which sends well intended but ill conceived "Christmass tree" packets. While you can always get into trouble, I think there are some difficulties here.
NAME IIRC, one of the concerns regarding this approach was that we might see a deadlock if the receiver has dropped the keys necessary for processing the the first packet within a datagram, and decides to drop the entire datagram due to not being able to process the first packet. Then, the sender might simply retransmit the coalesced datagram that starts from the same packet type as the previous datagram, and that datagram again gets dropped by the receiver, and ... Therefore, it has been my view that it is beneficial to specify the minimum requirements that guarantees the connection to make progress. IIRC, my preference was to define a minimum number of packets within a single datagram that a receiver should process. But I am fine with what we have in the text right now, because it does not make the implementations complicated; a sensible sender would never send a datagram containing more than one packet at the same encryption level, detecting them on the receiver-side is an implementation choice.
NAME lands, we should have very few cases where "the receiver has dropped the keys necessary for processing the first packet within a datagram". I imagine we could see some corner cases, but they most likely will be transient states, not leading to deadlocks.
NAME I agree that it's likely that we would not have deadlocks. But I think it's beneficial to specify how a server should not drop coalesced packets, because there would be performance issues if there is a disagreement about what is possible between the endpoints (one of the concerns being PMTU probes using coalesced packets (URL)).
Would a note suffice? Something like "an endpoint that coalesces more than a few packets risks having the peer not process all packets." I don't think we need stronger or RFC 2119 language here, just a note for sensible implementation is adequate I think.
NAME I personally think that that might be sufficient, though it is my understanding is that we already have a rough consensus on how to proceed; see URL Maybe I am incorrect in understanding the status, but based on this understanding, what I am wondering is if there's any new information that makes the WG reconsider.
Thanks NAME for reminding me that we discussed this in Tokyo and arrived at an agreement (it's not consensus per my read, it was agreement among the participants in the "room"). I'll gently poke NAME to write the PR.
I am a bit skeptical of the idea of using coalesced packets for PMTU discovery. If I receive a handshake packet after the handshake is finished, I will most probably ignore it. I don't have the keys anymore, I cannot remove header protection or packet protection. Packet will be treated as a decryption error, and whatever is coalesced with it will most likely be ignored. I understand the goal of coalescing is to find a CID in the ICMP payload, but that seems somewhat brittle. Parsing the ICMP based on the 5 tuple would be much more robust, and does not require playing games with packet coalescing. I may be a bit biased because I would rather just completely ignore all ICMP packets, and rely only on end-to-end ACKs. It removes the need to find a portable way to obtain ICMP messages through the socket API, and it also prevents attacks by ICMP spoofing.
NAME I would point out that the draft says don't. Quoting from : if decryption fails (because the keys are not available or any other reason), the receiver MAY either discard or buffer the packet for later processing and MUST attempt to process the remaining packets. I agree that endpoints should rely on the 5 tuple whenever possible. However, if a stateless load balancer is being used, the CID becomes the only identifier for routing packets. That's why we have this mechanism (and the text quoted above).
I really wish QUIC did not use coalescing for any kind of semantics and only to avoid roundtrips.
Coalescing for other-than-PMTU-discovery is to reduce the number of small datagrams. That's useful for reducing network bandwidth and CPU cost, especially for short connections. So any kind of analysis for possible attacks should weigh "Christmas Tree" coalesced packets vs. receiving each of the coalesced packets separately. Coalescing makes sending such packets cheaper for the sender but it also makes it equally cheaper for the server to receive. My view is that packet coalescing is "neutral" in its effect on attack resilience of the protocol. (One can actually say it is slightly beneficial for network links, since they reduce the bandwidth required to send the same packets separately.)
Yeah, meant reduce UDP packet count, not roundtrip.
Discussed in London; NAME will (still) make a PR to prohibit senders from coalescing packets of different encryption levels and to permit (but not require) receivers to enforce this constraint.
Clarified in London: Sender of a packet cannot coalesce multiple packets of the same type. If you receive multiple packets of the same type in the same datagram, you're not obligated to process all of them.
LGTM

0-RTT, Handshake, 1-RTT) makes it more likely the receiver will be able to process all the packets in a single pass.  A packet with a short header does not include a length, so it can only be the last  <ins> packet included in a UDP datagram.  An endpoint SHOULD NOT coalesce multiple packets at the same encryption level. </ins> Senders MUST NOT coalesce QUIC packets for different connections into a single UDP datagram.  Receivers SHOULD ignore any subsequent


ECN codepoints to affect the sender's rate.  If duplicate packets are discarded by a receiver, an off-path attacker will need to race the duplicate packet against the original to be successful in this <del> attack.  Therefore, QUIC receivers ignore ECN codepoints set in duplicate packets (see ecn). </del>  21.8.

NAME made a good suggestion in , which I have taken and tweaked further. It is not sufficient to discard ECN if one QUIC packet is discared, you need to discard ALL QUIC packets in the IP packet.
Sounds about right to me.
S 21.7 This could use more elaboration. As written, it seems like if you win the race (which you might like half the time) then you will succeed in the attack. Is that true?
Yes, the winner of a race can reduce the congestion window or maybe prevent it from being reduced.
As I see this: QUIC receivers ignore the ECN codepoint field received in IP packets that carry duplicate QUIC packets (see {{ecn}}).
So NAME is this resolved or do you expect more than what Martin wrote?
I think that I can make that call now. If there is a problem, we can open an issue.

ECN codepoints to affect the sender's rate.  If duplicate packets are discarded by a receiver, an off-path attacker will need to race the duplicate packet against the original to be successful in this  <ins> attack.  Therefore, QUIC endpoints ignore the ECN codepoint field on an IP packet unless at least one QUIC packet in that IP packet is successfully processed; see ecn. </ins> 21.8.


the administrator, implementations MUST disable the spin bit for a given connection with a certain likelihood.  The random selection process SHOULD be designed such that on average the spin bit is <del> disabled for at least one eighth of connections.  The selection </del>  process performed at the beginning of the connection SHOULD be applied for all paths used by the connection. <del> In case multiple connections share the same five-tuple, that is, have the same source and destination IP address and UDP ports, endpoints should try to co-ordinate across all connections to ensure a clear signal to any on-path measurement points. </del>  When the spin bit is disabled, endpoints MAY set the spin bit to any value, and MUST ignore any incoming value.  It is RECOMMENDED that

When multiple connections share the same network path or multiple connection IDs are used for the same path, using the spin bit is complicated by the difficulty in correlating forward and reverse flows. There is currently text that uses RFC 6919 language in the draft recommending coordination of the spin to avoid this problem. However, as , this coordination seems difficult and therefore unlikely. It also creates a strong signal that indicates that all the connections on the path are terminated at the same endpoint on both ends. Maybe we don't want to be generating that signal. Also, because this coordination can't be guaranteed, the on-path observer is forced to build code for correlating/segregating measurements by connection ID in the case where there is no coordination. Maybe it's best just to remove this recommendation entirely.
+1 to removing the recommendation, or changing the statement to just a caution (e.g., if you are to coalesce connections that spin, then you'd better be aware of ...). FWIW I am not sure if I agree with this observation. IIUC, the connections sharing a 5-tuple will be terminated by the same endpoint on both ends; it is impossible for a middlebox to coalesce multiple QUIC connections. It is impossible because CIDs can change mid-connection, and a middlebox cannot determine to where the packets containing the new CID must be sent.
There definitely needs to be a degree of coordination between the middlebox and the endpoints, but that doesn't mean that you can't use something like the quic-lb stuff on either end (or both ends).
Yes, I think it is better to remove that recommendation, but perhaps replace it with a note to observers.
Text says: How would that work when 1/8th of the connections are not spinning? Wouldn't that either be sending a misleading signal, or violate the 1/8th rule?

the administrator, implementations MUST disable the spin bit for a given connection with a certain likelihood.  The random selection process SHOULD be designed such that on average the spin bit is  <ins> disabled for at least one eighth of network paths.  The selection </ins> process performed at the beginning of the connection SHOULD be applied for all paths used by the connection.  <ins> In case multiple connections share the same network path, as determined by having the same source and destination IP address and UDP ports, endpoints should try to co-ordinate across all connections to ensure a clear signal to any on-path measurement points. </ins> When the spin bit is disabled, endpoints MAY set the spin bit to any value, and MUST ignore any incoming value.  It is RECOMMENDED that


protocol. An endpoint can verify support for Explicit Congestion Notification <del> (ECN) in the first packets it sends, as described in ecn- verification. </del>  The CRYPTO frame can be sent in different packet number spaces.  The sequence numbers used by CRYPTO frames to ensure ordered delivery of ECN marking and the peer is able to access the ECN codepoint in the IP header.  A network path does not support ECN if ECN marked packets get dropped or ECN markings are rewritten on the path.  An endpoint <del> verifies the path, both during connection establishment and when migrating to a new path (see migration). </del>  13.3.1. 13.3.2. <del> Each endpoint independently verifies and enables use of ECN by setting the IP header ECN codepoint to ECN Capable Transport (ECT) for the path from it to the other peer.  Even if not setting ECN codepoints on packets it transmits, the endpoint SHOULD provide feedback about ECN markings received (if accessible). To verify both that a path supports ECN and the peer can provide ECN feedback, an endpoint sets the ECT(0) codepoint in the IP header of all outgoing packets RFC8311. If an ECT codepoint set in the IP header is not corrupted by a network device, then a received packet contains either the codepoint sent by the peer or the Congestion Experienced (CE) codepoint set by a network device that is experiencing congestion. If a QUIC packet sent with an ECT codepoint is newly acknowledged by the peer in an ACK frame without ECN feedback, the endpoint stops setting ECT codepoints in subsequent IP packets, with the expectation that either the network path or the peer no longer supports ECN. Network devices that corrupt or apply non-standard ECN markings might result in reduced throughput or other undesirable side-effects.  To reduce this risk, an endpoint uses the following steps to verify the counts it receives in an ACK frame. The total increase in ECT(0), ECT(1), and CE counts MUST be no smaller than the total number of QUIC packets sent with an ECT codepoint that are newly acknowledged in this ACK frame.  This step detects any network remarking from ECT(0), ECT(1), or CE codepoints to Not-ECT. </del>  Any increase in either ECT(0) or ECT(1) counts, plus any increase in the CE count, MUST be no smaller than the number of packets acknowledged in this ACK frame.  This step detects any erroneous network remarking from ECT(0) to ECT(1) (or vice versa).  An endpoint could miss acknowledgements for a packet when ACK frames are lost.  It is therefore possible for the total increase in ECT(0), ECT(1), and CE counts to be greater than the number of packets <del> acknowledged in an ACK frame.  When this happens, and if verification </del>  succeeds, the local reference counts MUST be increased to match the counts in the ACK frame. <del> Processing counts out of order can result in verification failure. An endpoint SHOULD NOT perform this verification if the ACK frame is received in a packet with packet number lower than a previously received ACK frame.  Verifying based on ACK frames that arrive out of order can result in disabling ECN unnecessarily. </del>  <del> Upon successful verification, an endpoint continues to set ECT </del>  codepoints in subsequent packets with the expectation that the path <del> is ECN-capable. If verification fails, then the endpoint ceases setting ECT codepoints in subsequent IP packets with the expectation that either the network path or the peer does not support ECN. If an endpoint sets ECT codepoints on outgoing IP packets and encounters a retransmission timeout due to the absence of acknowledgments from the peer (see QUIC-RECOVERY), or if an endpoint has reason to believe that an element on the network path might be corrupting ECN codepoints, the endpoint MAY cease setting ECT codepoints in subsequent packets.  Doing so allows the connection to be resilient to network elements that corrupt ECN codepoints in the IP header or drop packets with ECT or CE codepoints in the IP header. </del>  14.

Cleans up ECN verification text. .
I've taken in all feedback and reworked this section a fair bit. It's structured differently now, PTAL.
The wall of commits is a little disconcerting there.
I think this is ready. Unless someone disagrees, I'll mark the corresponding issue as proposal-ready tomorrow.
I don't think that we'll find new objections. I marked it myself.
Section 13.3.2 "ECN Verification" Although I understand the intent here, retransmission timeouts, as a term, don't exist anymore in QUIC except during handshakes. Disabling ECN after any PTO firing seems a bit extreme, so I'd like to see a better recommendation here, which covers both handshakes and short headers.
Fixing the naming(retransmission timeout to probe timeout) is trivial, but I'm not sure what to recommend here for when to disable ECN. Also, probe timeouts don't occur until the handshake is complete, is that a problem? One idea that's somewhat in keeping with RFC3168 would be to disable ECN on crypto handshake timeouts and probe timeouts, and if the un-marked packets get through and the marked do not, disable ECN. But even that is subject to random loss and I believe is likely to spuriously trigger. We could disable ECN temporarily on only the second timeout, to reduce the chance of spurious disabling? RFC8311(URL) says we can do something different from 3168 if we document it, but doesn't specify what. Is there some useful RFC I can cite that I haven't found, or are we on our own in designing a mechanism here?
NAME I reclassified this as design, because it's not just the name update. If we decide to just fix the name, then we can change it back. Correct me if I'm wrong.
How about probing ECN on even packet numbers for the first N packets before making a decision, where N is tunable.
I believe what the text was actually meant to say was that if you set the ECT code point and you loose ALL packets with the code point set, you MAY disable it.
What NAME says makes sense. We don't need to document a full algorithm here, but the proposed text works for me.
So the intention here is that if you hit an ECN blackhole that you disable ECT marking the sent packet in a timely fashion to minimize the impact on connection establishment. And I don't think a single sender behavior is necessary.
NAME mentioned that this should be triggered on persistent congestion.
Agreed in London for NAME to write a PR.
Just to clarify: RFC8311 permits other uses that are specified by an RFC: i.e., currently this is RFC8511, and the expected RFC to be published by the L4S work items in TSVWG (expecting to be Lc'ed later in 2019).
There seems two things: • What to do during handshake (maybe is described in transport) • Disabling after handshake which I think is in the loss/recovery draft, rather than transport? - I thought I heard Jana argue that this reaction depends on the loss/marking observations on the path - which I would agree with.
NAME is that ready for a consensus call?
There are a lot of open comments on the PR, so I think not yet?
NAME Not yet, the PR needs some work.

protocol. An endpoint can verify support for Explicit Congestion Notification  <ins> (ECN) in the first packets it sends, as described in ecn-validation. </ins> The CRYPTO frame can be sent in different packet number spaces.  The sequence numbers used by CRYPTO frames to ensure ordered delivery of ECN marking and the peer is able to access the ECN codepoint in the IP header.  A network path does not support ECN if ECN marked packets get dropped or ECN markings are rewritten on the path.  An endpoint  <ins> validates the use of ECN on the path, both during connection establishment and when migrating to a new path (migration). </ins> 13.3.1. 13.3.2.  <ins> It is possible for faulty network devices to corrupt or erroneously drop packets with ECN markings.  To provide robust connectivity in the presence of such devices, each endpoint independently validates ECN counts and disables ECN if errors are detected. Endpoints validate ECN for packets sent on each network path independently.  An endpoint thus validates ECN on new connection establishment, when switching to a new server preferred address, and on active connection migration to a new path. Even if an endpoint does not use ECN markings on packets it transmits, the endpoint MUST provide feedback about ECN markings received from the peer if they are accessible.  Failing to report ECN counts will cause the peer to disable ECN marking. 13.3.2.1. To start ECN validation, an endpoint SHOULD do the following when sending packets on a new path to a peer: Set the ECT(0) codepoint in the IP header of early outgoing packets sent on a new path to the peer RFC8311. If all packets that were sent with the ECT(0) codepoint are eventually deemed lost QUIC-RECOVERY, validation is deemed to have failed. To reduce the chances of misinterpreting congestive loss as packets dropped by a faulty network element, an endpoint could set the ECT(0) codepoint in the first ten outgoing packets on a path, or for a period of three RTTs, whichever occurs first. Implementations MAY experiment with and use other strategies for use of ECN.  Other methods of probing paths for ECN support are possible, as are different marking strategies.  Implementations can also use the ECT(1) codepoint, as specified in RFC8311. 13.3.2.2. An endpoint that sets ECT(0) or ECT(1) codepoints on packets it transmits MUST use the following steps on receiving an ACK frame to validate ECN. If this ACK frame newly acknowledges a packet that the endpoint sent with either ECT(0) or ECT(1) codepoints set, and if no ECN feedback is present in the ACK frame, validation fails.  This step protects against both a network element that zeroes out ECN bits and a peer that is unable to access ECN markings, since the peer could respond without ECN feedback in these cases. For validation to succeed, the total increase in ECT(0), ECT(1), and CE counts MUST be no smaller than the total number of QUIC packets sent with an ECT codepoint that are newly acknowledged in this ACK frame.  This step detects any network remarking from ECT(0), ECT(1), or CE codepoints to Not-ECT. </ins> Any increase in either ECT(0) or ECT(1) counts, plus any increase in the CE count, MUST be no smaller than the number of packets acknowledged in this ACK frame.  This step detects any erroneous network remarking from ECT(0) to ECT(1) (or vice versa). <ins> Processing ECN counts out of order can result in validation failure. An endpoint SHOULD NOT perform this validation if this ACK frame does not advance the largest packet number acknowledged in this connection. </ins> An endpoint could miss acknowledgements for a packet when ACK frames are lost.  It is therefore possible for the total increase in ECT(0), ECT(1), and CE counts to be greater than the number of packets  <ins> acknowledged in an ACK frame.  When this happens, and if validation </ins> succeeds, the local reference counts MUST be increased to match the counts in the ACK frame.  <ins> 13.3.2.3. </ins>  <ins> If validation fails, then the endpoint stops sending ECN markings in subsequent IP packets with the expectation that either the network path or the peer does not support ECN. Upon successful validation, an endpoint can continue to set ECT </ins> codepoints in subsequent packets with the expectation that the path  <ins> is ECN-capable.  Network routing and path elements can change mid- connection however; an endpoint MUST disable ECN if validation fails at any point in the connection. Even if validation fails, an endpoint MAY revalidate ECN on the same path at any later time in the connection. </ins> 14.


remote peer reaches the limit early and becomes blocked.  In particular, the value chosen for "initial_max_uni_streams" should consider that remote peers may wish to exercise reserved stream <del> behavior (stream-grease).  To reduce the likelihood of blocking, both clients and servers SHOULD send a value of three or greater for the QUIC transport parameter "initial_max_uni_streams", and a value of 1,024 or greater for the QUIC transport parameter </del>  "initial_max_stream_data_uni".  If the stream header indicates a stream type which is not supported by the recipient, the remainder of the stream cannot be consumed as the semantics are unknown.  Recipients of unknown stream types MAY

As per URL HTTP MUST allow enough uni streams credits for mandatory streams (control, QPACK encoder/decoder, other mandatory extensions) but is not required to grant additional credits if the peer wastes them. This is the companion PR to updating the requirements in the HTTP draft, and together they I created a separate PR since it touches -http instead of -qpack, so it doesn't get blocked by the QPACK refactoring.
It's acceptable but not ideal. Didnt we just remove a mandatory TP in some unrelated change?
Also rebased to fix conflict.
That is an exceptionally long sentence. But I think the text is correct.
Now that is being merged, we have the possibility to avoid creating unnecessary encoder / decoder streams. Avoiding creation is beneficial for memory-constrained devices, because they can configure the QUIC transport stack to handle at most one unidirectional stream (in each direction) to support HTTP/3 if the QPACK state forbids 1) peer's creation of the encoder stream when endpoint's is zero, and 2) peer's creation of decoder stream is delayed until the endpoint's creation of the encoder stream. With the proposed changes, a memory constrained HTTP/3 stack that sends of zero and never opens an encoder stream can be certain that the peer never opens an encoder stream or a decoder stream. Note that it has minimal impact to existing implementations; regarding creation of the encoder stream, states that the stream cannot be used until SETTINGS is being received. It is a clarification (or a requirement) that "use" includes the creation of the stream. The situation is the same for the decoder stream. Pull request filed as .
Discussed in Tokyo; do not require creating the streams, but creating them is not forbidden even if the setting prevents their use.
And make sure that constrained devices can trivially detect and reset non-forbidden forbidden streams instead of assigning state.
I made to try and put into writing what was discussed in Tokyo since there hasn't been any actiity in a while. Incidentally we run into the problem of allowing endpoints to create all QPACK control streams while interop'ing between quant and quiche HTTP/3.
Discussed and clarified in London. HTTP MUST set initialmaxuni_streams to at least 3 HTTP does not have to keep granting more credit if the peer burns the stream credits on grease/push/extensions. Peers SHOULD create control, qpack encoder and decoder as their first 3 streams if they plan to use them, since there's no guarantee of ever getting more credit.
Created in addition to the existing PR to also update the -http requirements as per the discussion in London.
There's one nit which implies a design question; once that's settled one way or another, I'm happy with this.

remote peer reaches the limit early and becomes blocked.  In particular, the value chosen for "initial_max_uni_streams" should consider that remote peers may wish to exercise reserved stream  <ins> behavior (stream-grease).  To avoid blocking, both clients and servers MUST allow the peer to create at least one unidirectional stream for the HTTP control stream plus the number of unidirectional streams required by mandatory extensions (such as QPACK) by setting an appropriate value for the QUIC transport parameter "initial_max_uni_streams" (three being the minimum value required for the base HTTP/3 protocol and QPACK), and SHOULD use a value of 1,024 or greater for the QUIC transport parameter </ins> "initial_max_stream_data_uni". <ins> Note that an endpoint is not required to grant additional credits to create more unidirectional streams if its peer consumes all the initial credits before creating the critical unidirectional streams. Endpoints SHOULD create the HTTP control stream as well as the unidirectional streams required by mandatory extensions (such as the QPACK encoder and decoder streams) first, and then create additional streams as allowed by their peer. </ins> If the stream header indicates a stream type which is not supported by the recipient, the remainder of the stream cannot be consumed as the semantics are unknown.  Recipients of unknown stream types MAY


more detail in Section 3.2 of RFC7230, though the wire rendering in HTTP/3 differs.  As in HTTP/2, header field names MUST be converted to lowercase prior to their encoding.  A request or response <del> containing uppercase header field names MUST be treated as malformed. </del>  As in HTTP/2, HTTP/3 uses special pseudo-header fields beginning with the ':' character (ASCII 0x3a) to convey the target URI, the method not possible, unless this is otherwise permitted (e.g., idempotent actions like GET, PUT, or DELETE).  4.2. The pseudo-method CONNECT (RFC7231, Section 4.3.6) is primarily used A CONNECT request in HTTP/3 functions in the same manner as in HTTP/2.  The request MUST be formatted as described in HTTP2, Section 8.3.  A CONNECT request that does not conform to these <del> restrictions is malformed.  The request stream MUST NOT be closed at the end of the request. </del>  A proxy that supports CONNECT establishes a TCP connection (RFC0793) to the server identified in the ":authority" pseudo-header field.

by importing the RFC7540 malformed request/response text. This section is general enough, it doesn't appear to have much in the way of difference between HTTP/2 and HTTP/3. Are there things we should trim out, or conditions that we need to spell out in more detail?
This issues arises from some off-list discussion on how to build up a header list, and who is responsible for validating the result. The conclusion is that the application is responsible for doing so. In HTTP/2, RFC 7540 section 8.1.2.6 explains how an endpoint should handle malformed requests or responses. There is no explicit link to this in H3, only implicitly via references to topics in 8.1.2.*. However, I think some of that guidance is not applicable: the one that sticks out is this: if the value of a content-length header field does not equal the sum of the DATA frame payload lengths that form the body.
Some further info. In H2, malformed request/response causes a stream error of type PROTOCOLERROR. The H3 draft says: No single mapping. See new HTTPMALFORMEDFRAME error codes defined in Section 8.1. But errors in the category of malformed request/response may not caused by a problem with the frames themselves. Fortunately, we have H3 HTTPGENERALPROTOCOLERROR, which seems like the correct thing to use in this case. But nowhere in the draft is this ever referred to, so its easy to miss.
FRAME related errors are defined at the end of S8.1. However that is going to change due to
Nice catch! I think I'd prefer using HTTPGENERALPROTOCOLERROR or an error code just for this purpose rather than HTTPMALFORMEDFRAME, because this is a framing error in the HTTP semantic layer, rather than an error in a HTTP/3 frame. The issue with using HTTPMALFORMEDFRAME becomes most apparent when the request body is shorter than the sum of DATA frames, and if there is a trailer. Then, the error will be detected when seeing the HEADERS frame. It would be strange to report the issue as HTTPMALFORMEDFRAME(HEADERS) (because it's not an issue with the HEADERS frame), or as HTTPMALFORMED_FRAME(DATA) (because none of the DATA frames that the client sent was malformed.
^^ this comment is about an endpoint abruptly closing a stream rather than an endpoint detecting the peer abruptly closing a stream. Taking back the comment, as I think it deserves a separate issue.
Another error that we saw a lot in the early days of H2 deployment was headers fields using mixed-case capitalization. This cannot be attributed to a malformed H3 frame. I really hope this class of error has been shaken out of systems by now but you never know.
NAME Agreed. I think using HTTPGENERALPROTOCOL_ERROR as a catch-all error code when the peer does something stupid makes sense. After all, that's what we did in H2 and IMO it works well.
I think this falls under the umbrella of making sure RFC7540 content is referenced / restated appropriately in an H3 world.
Maybe it's worth noting that HTTPGENERALPROTOCOLERROR is the error that a proxy is expected to send when upstream resets a HTTP message (e.g., h1 connection being closed in middle of a T-E chunk, receiving RSTSTREAM on a h2 stream). We could have an error code dedicated to that, but H2 has not had one and therefore I do not think there's a strong necessity to have a dedicated codepoint.
Discussed in London; restate them with care.

more detail in Section 3.2 of RFC7230, though the wire rendering in HTTP/3 differs.  As in HTTP/2, header field names MUST be converted to lowercase prior to their encoding.  A request or response  <ins> containing uppercase header field names MUST be treated as malformed (malformed). </ins> As in HTTP/2, HTTP/3 uses special pseudo-header fields beginning with the ':' character (ASCII 0x3a) to convey the target URI, the method not possible, unless this is otherwise permitted (e.g., idempotent actions like GET, PUT, or DELETE). <ins> 4.1.3. A malformed request or response is one that is an otherwise valid sequence of frames but is invalid due to the presence of extraneous frames, prohibited header fields, the absence of mandatory header fields, or the inclusion of uppercase header field names. A request or response that includes a payload body can include a "content-length" header field.  A request or response is also malformed if the value of a content-length header field does not equal the sum of the DATA frame payload lengths that form the body. A response that is defined to have no payload, as described in Section 3.3.2 of RFC7230 can have a non-zero content-length header field, even though no content is included in DATA frames. Intermediaries that process HTTP requests or responses (i.e., any intermediary not acting as a tunnel) MUST NOT forward a malformed request or response.  Malformed requests or responses that are detected MUST be treated as a stream error (errors) of type HTTP_GENERAL_PROTOCOL_ERROR. For malformed requests, a server MAY send an HTTP response prior to closing or resetting the stream.  Clients MUST NOT accept a malformed response.  Note that these requirements are intended to protect against several types of common attacks against HTTP; they are deliberately strict because being permissive can expose implementations to these vulnerabilities. </ins> 4.2. The pseudo-method CONNECT (RFC7231, Section 4.3.6) is primarily used A CONNECT request in HTTP/3 functions in the same manner as in HTTP/2.  The request MUST be formatted as described in HTTP2, Section 8.3.  A CONNECT request that does not conform to these  <ins> restrictions is malformed (see malformed).  The request stream MUST NOT be closed at the end of the request. </ins> A proxy that supports CONNECT establishes a TCP connection (RFC0793) to the server identified in the ":authority" pseudo-header field.


QPACK defines two settings which are included in the HTTP/3 SETTINGS frame. <del> An integer with a maximum value of 2^30 - 1.  The default value is zero bytes.  See _table-dynamic for usage.  This is the equivalent of the SETTINGS_HEADER_TABLE_SIZE from HTTP/2. </del>  <del> An integer with a maximum value of 2^16 - 1.  The default value is zero.  See overview-hol-avoidance. </del>  6.

In addition, remove verbiage stating that the settings values are integers, as the are integers by the definition of the SETTINGS frame.
Both specify the maximum value the decoder supports. The encoder is free to pick and use a smaller value. There is no reason to add artificial limits to the way an decoder is implemented.
This could be viewed as an alternative resolution to .
Also, historical note: These have maxima because settings used to be length-prefixed blobs. By defining them to be integers of a maximum size, the length could be constrained. Now that we use varints, there is a maximum structural size already.

QPACK defines two settings which are included in the HTTP/3 SETTINGS frame.  <ins> The default value is zero.  See _table-dynamic for usage.  This is the equivalent of the SETTINGS_HEADER_TABLE_SIZE from HTTP/2. </ins>  <ins> The default value is zero.  See overview-hol-avoidance. </ins> 6.