Destination Connection ID field that follows it.  This length is encoded as an 8-bit unsigned integer.  In QUIC version 1, this value MUST NOT exceed 20.  Endpoints that receive a version 1 long <del> header with a value larger than 20 MUST drop the packet.  Servers SHOULD be able to read longer connection IDs from other QUIC versions in order to properly form a version negotiation packet. </del>  The Destination Connection ID field follows the Destination Connection ID Length field, which indicates the length of this This length is encoded as a 8-bit unsigned integer.  In QUIC version 1, this value MUST NOT exceed 20 bytes.  Endpoints that receive a version 1 long header with a value larger than 20 MUST <del> drop the packet.  Servers SHOULD be able to read longer connection IDs from other QUIC versions in order to properly form a version negotiation packet. </del>  The Source Connection ID field follows the Source Connection ID Length field, which indicates the length of this field. 21.5. An adversarial sender might intentionally send fragments of stream <del> data in order to cause disproportionate receive buffer memory </del>  commitment and/or creation of a large and inefficient data structure. An adversarial receiver might intentionally not acknowledge packets <del> containing stream data in order to force the sender to store the </del>  unacknowledged stream data for retransmission. The attack on receivers is mitigated if flow control windows

There are other uses of "in order" that the editors may like to address to avoid potential misunderstanding: /An adversarial receiver might intentionally not acknowledge packets containing stream data in order to force the sender to store the unacknowledged stream data for retransmission./ would be clearer as /An adversarial receiver might intentionally not acknowledge packets containing stream data so that it forces the sender to store the unacknowledged stream data for retransmission./
There are other uses of "in order" that the editors may like to address to avoid potential misunderstanding: This also is potentially misread: /An adversarial sender might intentionally send fragments of stream data in order to cause disproportionate receive buffer memory commitment and/or creation of a large and inefficient data structure./ and could be /An adversarial sender might intentionally send fragments of stream data that cause disproportionate receive buffer memory commitment and/or creation of a large and inefficient data structure./
There are other uses of "in order" that the editors may like to address to avoid potential misunderstanding: /Servers SHOULD be able to read longer connection IDs from other QUIC versions in order to properly form a version negotiation packet./ ... seem like /Servers SHOULD be able to read longer connection IDs from other QUIC versions to properly form a version negotiation packet./
"in order to" seems like a well-known construction, so I'm not concerned about its existence. Your rewording loses the implication of "SHOULD... because you'll need to...." Though I see your point that this sentence could be parsed as "reading in order" being the thing servers SHOULD be able to do. Perhaps "In order to properly form X, servers SHOULD Y"?
Also, this seems to be the same sentence as ?
That works for me - starting with the construction seems clear. Gorry
The new PR appears to have all the in orders in good order then!
There are other uses of "in order" that the editors may like to address to avoid potential misunderstanding: /Servers SHOULD be able to read longer connection IDs from other QUIC versions in order to properly form a version negotiation packet./ ... this might be misread as an ordering requirement, but I think is intend to be: /Servers SHOULD be able to read longer connection IDs from other QUIC versions to properly form a version negotiation packet./
FYI, it's more readable to quote with , e.g., this might be misread as an ordering requirement, but I think is intend to be:
All looks clear to me.

Destination Connection ID field that follows it.  This length is encoded as an 8-bit unsigned integer.  In QUIC version 1, this value MUST NOT exceed 20.  Endpoints that receive a version 1 long  <ins> header with a value larger than 20 MUST drop the packet.  In order to properly form a Version Negotiation packet, servers SHOULD be able to read longer connection IDs from other QUIC versions. </ins> The Destination Connection ID field follows the Destination Connection ID Length field, which indicates the length of this This length is encoded as a 8-bit unsigned integer.  In QUIC version 1, this value MUST NOT exceed 20 bytes.  Endpoints that receive a version 1 long header with a value larger than 20 MUST  <ins> drop the packet.  In order to properly form a Version Negotiation packet, servers SHOULD be able to read longer connection IDs from other QUIC versions. </ins> The Source Connection ID field follows the Source Connection ID Length field, which indicates the length of this field. 21.5. An adversarial sender might intentionally send fragments of stream  <ins> data in an attempt to cause disproportionate receive buffer memory </ins> commitment and/or creation of a large and inefficient data structure. An adversarial receiver might intentionally not acknowledge packets  <ins> containing stream data in an attempt to force the sender to store the </ins> unacknowledged stream data for retransmission. The attack on receivers is mitigated if flow control windows


server from repeated client probes, the PTO backoff is not reset at a client that is not yet certain that the server has finished validating the client's address.  That is, a client does not reset <del> the PTO backoff factor on receiving acknowledgements until it receives a HANDSHAKE_DONE frame or an acknowledgement for one of its Handshake or 1-RTT packets. </del>  This exponential reduction in the sender's rate is important because consecutive PTOs might be caused by loss of packets or unblock the server until it is certain that the server has finished its address validation (see Section 8 of QUIC-TRANSPORT).  That is, the client MUST set the probe timer if the client has not received an <del> acknowledgement for one of its Handshake or 1-RTT packets and has not received a HANDSHAKE_DONE frame, even if there are no packets in flight.  When the PTO fires, the client MUST send a Handshake packet if it has Handshake keys, otherwise it MUST send an Initial packet in a UDP datagram of at least 1200 bytes. </del>  6.2.3.

This avoids redefinition of the conditions for handshake confirmation. It's fewer words, even with the extra references.
The draft should use "handshake confirmed" and point to the TLS draft instead of describing the conditions individually in various places.

server from repeated client probes, the PTO backoff is not reset at a client that is not yet certain that the server has finished validating the client's address.  That is, a client does not reset  <ins> the PTO backoff factor on receiving acknowledgements until the handshake is confirmed; see Section 4.1.2 of QUIC-TLS. </ins> This exponential reduction in the sender's rate is important because consecutive PTOs might be caused by loss of packets or unblock the server until it is certain that the server has finished its address validation (see Section 8 of QUIC-TRANSPORT).  That is, the client MUST set the probe timer if the client has not received an  <ins> acknowledgement for one of its Handshake packets and the handshake is not confirmed (see Section 4.1.2 of QUIC-TLS), even if there are no packets in flight.  When the PTO fires, the client MUST send a Handshake packet if it has Handshake keys, otherwise it MUST send an Initial packet in a UDP datagram of at least 1200 bytes. </ins> 6.2.3.


17.2.5.3. <del> 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 MUST use the same cryptographic handshake message it included in this packet. A server MAY treat a packet that contains a different cryptographic handshake message as a connection error or discard it. </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

See the issue for the sorry history of this mistake.
This issue has a long and storied history. We were originally of the view that only one packet needed to include a token (which you can see in the discussion of ). However, the resolution of that issue in was very clear: every Initial packet needs to include a token. That introduced a regression as it grouped the token with the connection ID, and we caught that in . However, the fix to that in reverted the token changes along with the connection ID changes. Since then, this error has gone unnoticed. Most people will have seen the stronger normative requirement : To my knowledge, every implementation includes the token in every Initial packet, changing only when a Retry is received. We should reconcile the text.
FWIW, that's what the interop runner tests for in the Retry test case.
NAME : Yes, that's what sent us off on this chase :-)

17.2.5.3.  <ins> Subsequent Initial packets from the client include the connection ID and token values from the Retry packet.  The client copies the Source Connection ID field from the Retry packet to the Destination Connection ID field and uses this value until an Initial packet with an updated value is received; see negotiating-connection-ids.  The value of the Token field is copied to all subsequent Initial packets; see validate-retry. Other than updating the Destination Connection ID and Token fields, the Initial packet sent by the client is subject to the same restrictions as the first Initial packet.  A client MUST use the same cryptographic handshake message it included 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


frame.  A client sends MAX_PUSH_ID frames to control the number of pushes that a server can promise.  A server SHOULD use Push IDs sequentially, beginning from zero.  A client MUST treat receipt of a <del> push stream with a Push ID that is greater than the maximum Push ID as a connection error of type H3_ID_ERROR. </del>  The Push ID is used in one or more PUSH_PROMISE frames (frame-push- promise) that carry the header section of the request message.  These

Probably editorial since this is what was previously intended, but still touching a MUST.
states: The case when the client has not sent a MAXPUSHID frame is not covered. (In this case, the client should treat the receipt of any frame with a Push ID as an error.)
There is some other text that kinda marks the error case: One interpretation is that since any value of ID is greater than , then the error condition holds. But I agree with Dmitri that we could probably make this clearer with some editorializing.
That's a big stretch. Does it hold in languages that have a , such as JavaScript? E.g., can one write ?
Since it starts as unset, and MAXPUSHID can only increase the value then in this world, 0 must be greater than unset
What is "unset?" <DmitriMode>If 0 is a valid value, then "unset" means , which is not smaller than 0. That is, it is not true that , which is what the text says.</DmitriMode>
Sure, but then it makes it very hard to send a MAXPUSHID that doesn't decrease the value. I think we agree that it would help the doc to fix your original nitpick.
Unset is negative one? The frame sets a boolean? This seems easy enough to implement in practice, but you're correct that the doc is effectively using a nullable type. I see a couple possible doc fixes. The most minimal change is to simply state that it is also an error to receive anything that uses a Push ID if no MAXPUSHID frame has been sent. That's compatible and consistent with the intent of the current design. Alternatively, we could define MAXPUSHID to tell the recipient how many Push IDs they can use; 0 is the initial value, and sending 0 leaves push disabled. That's consistent with QUIC's MAXSTREAMS et al. but would be a breaking change.
I vote for the simplest approach:
+1 let's keep it simple
Editorial?
Yes, this is an editorial issue.

frame.  A client sends MAX_PUSH_ID frames to control the number of pushes that a server can promise.  A server SHOULD use Push IDs sequentially, beginning from zero.  A client MUST treat receipt of a  <ins> push stream as a connection error of type H3_ID_ERROR when no MAX_PUSH_ID frame has been sent or when the stream references a Push ID that is greater than the maximum Push ID. </ins> The Push ID is used in one or more PUSH_PROMISE frames (frame-push- promise) that carry the header section of the request message.  These


multiple QUIC versions SHOULD pad the first UDP datagram they send to the largest of the minimum datagram sizes from all versions they support.  This ensures that the server responds if there is a <del> mutually supported version. </del>  6.1.

It was not clear to me that the current text was insufficient, but hopefully this is more sufficient.
I think enough opinions suggesting that we don't need this might lead me to close the PR over Gorry's objections, but I am sensitive to the fact that most people here are perhaps a little too familiar with the protocol already.
Cross references are cheap.
(Broken out of .) Are you saying a client that does not PAD does not negotiate? If so: What is the logic behind that design decision? I don’t read that as it assures what it say. To me, the padding assures the client can send this side of packet to the remote endpoint. Does this agree with what is written in section 8.1 on padding? and Section 14?
Yes, a client that does not pad to at least the minimum packet size for some version the server supports will not receive a Version Negotiation packet in response. The corresponding server text is in 5.2.2: This is both to prevent an attack vector from spoofed packets, and to reduce the risk that a non-QUIC packet will trigger Version Negotiation in response. There is a slight risk that the client might only support version of QUIC with smaller minima and the connection attempt will time out instead of discovering via VN that there are no mutually supported versions. However, if there is any overlap, compliance with this SHOULD enables the client to trigger the server's VN response. Yes, this aligns with the guidance in Section 8.1: and Section 14: However, those are in regards to a packet from this QUIC version, while the text you're asking about regards packets when the peer's QUIC version is unknown as of yet.
Not seeing any action here. As Mike says, the text describes sender requirements and explains why.
NAME
I agree with the logic in this thread, but this seems far too subtle/clever and others will be confused at this point. However, I read this as the remote checks that the minimum size is sufficient. That means not padding this breaks the protocol. Why doesn't that require a MUST for any version (sec 14). If it doesn't then this doesn't yet explain under what cases the present "SHOULD" can be violated.
Not padding breaks the protocol, so there's a MUST in Section 14. That MUST does apply to all versions that the client supports; the client obviously can't know the details of versions it doesn't support, including any the server supports, so that's all it can reference. There's no minimum first-packet size in the invariants. The server SHOULD send Version Negotiation in response to a packet of unknown version but an appropriate size; if it doesn't, the client won't know what versions it supports, but it might choose not to do so every time. (Hence the "MAY limit" in the next sentence.) Is the thing that's missing for you a linkage between "SHOULD send" and "MAY limit"?
All of this makes sense, but it was the sentence in section 6 which I found unclear, which says: "Clients that support multiple QUIC versions SHOULD pad the first packet they send to the largest of the minimum packet sizes across all versions they support." That made me ask what happens when they support a version and didn't pad to the min size required by that version?
Then they are violating the requirements of that version and won't get a response.
So let's say a hypothetical client supports versions with the following minimum packet sizes to open a connection: A: 1250 bytes B: 1200 bytes C: 1720 bytes The client is trying to open a connection with version B, so they MUST send at least 1200 bytes. However, if the server only supports A and C, this won't provoke a Version Negotiation packet and the connection attempt will time out. That's not incorrect, because the server doesn't support the attempted version, but it's suboptimal because there is overlap in the supported versions they could discover and use. The client SHOULD send at least 1720 bytes, because that guarantees the packet is large enough to trigger a Version Negotiation packet in the event there's any overlap in their supported version sets. It's only a SHOULD, not a MUST, because there might be legitimate reasons a client would opt not to send a 1720-byte packet, such as expecting a 1500-byte MTU on the network.
NAME
So if this confused me (a little), I am assuming it could confuse others, so one way to make this clear is add a sentence (albeit a little long) after the SHOULD in section 6 that says what happens and explicitly includes a forward ref to Section 14.1 i.e. AFTER THIS CURRENT TEXT: “The size of the first packet sent by a client will determine whether a server sends a Version Negotiation packet. Clients that support multiple QUIC versions SHOULD pad the first packet they send to the largest of the minimum packet sizes across all versions they support. This ensures that the server responds if there is a mutually supported version.” ADD SOMETHING LIKE: "If a client sends an Initial packet (see section 14.1) less than the smallest allowed packet size for the version of QUIC supported at the server (e.g., when this size is not permitted by the local interface), the server would not reply with a Version Negotiation packet and the connection attempt will time out.
?
I think you know what to expect, to me the text just needs to explain what you expect can happen.
NAME does resolve this issue for you?
This text seems to answer my question - Since this in section 6, I'd have also preferred adding a forward reference to the later section 14.1 to get the details.Not sure if we really need this...

multiple QUIC versions SHOULD pad the first UDP datagram they send to the largest of the minimum datagram sizes from all versions they support.  This ensures that the server responds if there is a  <ins> mutually supported version.  A server might not send a Version Negotiation packet if the datagram it receives is smaller than the minimum size specified in a different version; see initial-size. </ins> 6.1.


it uses for receiving packets.  These keys will be needed to process packets the peer sends after updating. <del> An endpoint SHOULD retain old keys so that packets sent by its peer prior to receiving the key update can be processed.  Discarding old keys too early can cause delayed packets to be discarded.  Discarding packets will be interpreted as packet loss by the peer and could adversely affect performance. </del>  6.2. Phase bit alone can be used to select keys. An endpoint MAY allow a period of approximately the Probe Timeout <del> (PTO; see QUIC-RECOVERY) after a key update before it creates the next set of packet protection keys.  These updated keys MAY replace the previous keys at that time.  With the caveat that PTO is a subjective measure - that is, a peer could have a different view of the RTT - this time is expected to be long enough that any reordered packets would be declared lost by a peer even if they were acknowledged and short enough to allow for subsequent key updates. </del>  Endpoints need to allow for the possibility that a peer might not be able to decrypt packets that initiate a key update during the period sufficient time could lead to packets being discarded. An endpoint SHOULD retain old read keys for no more than three times <del> the PTO.  After this period, old read keys and their corresponding secrets SHOULD be discarded. </del>  6.6.

I've been debugging a connection failure that seems to be caused by a key update. Here's what's happening: At some point during the connection, an endpoint decides to update its keys. After the key update, it doesn't send any (ack-eliciting) packets. If it sent non-ack-eliciting packets during this time, these packets are lost. After a while (e.g. 3 PTOs) it drops the old keys. The peer never received a packet in the new key phase, so it continues sending using the old key phase. Because the keys are dropped, these packets will all end up being undecryptable. Note that this is not an uncommon situation: An endpoint downloading a large object might initiate the key update, but not have anything to send but acknowledgements. says that receive keys MAY be discarded after 1 PTO: ... and SHOULD be discarded after 3 PTO: It seems like this advice is not safe. The deadlock could be avoided if old keys are kept until an ACK has been received for a packet encrypted with the new keys.
Just wondering, but isn't section 6.5 about receiving key updates? I might argue that the the moment of "key update" referred to by that section is the moment the endpoint receives a packet using a new key phase. If we interpret that way, then this is a non-issue, I think? FWIW, quicly does something along these lines.
I read this text as being about receiving packets during a key update, no matter who initiated the update. Even if you interpret as only applying when the peer initiated the update, that still leaves us lacking any text when it’s safe to discard old keys for the endpoint that initiated the update. As the deadlock shows, there’s no duration that can be considered safe.
Right. As stated, my argument is that the receive key of generation N is not supposed to de dropped until the endpoint receives a packet protected using a receive key of generation N+1. Once a packet protected using a receive key of generation N+1 is received, the endpoint is recommended to drop key of generation N within the 3 PTO window. That is what the text quoted above says: An endpoint SHOULD retain old read keys for no more than three times the PTO. After this period, old read keys and their corresponding secrets SHOULD be discarded.
I'm a bit uncomfortable tying the trigger to the peer's own key update, even though they're required to update in step. Would it make more sense to say you don't discard until you've received an acknowledgement for a packet sent with the new keys? That already has to be tracked for the maximum frequency of key updates, so it would make sense to hang this mechanism on it as well. If you know the peer has received a packet from you in the new phase, they'll be updating soon enough, but you know they have read keys which is the real issue.This seems like what we intended to me.

it uses for receiving packets.  These keys will be needed to process packets the peer sends after updating.  <ins> An endpoint MUST retain old keys until it has successfully unprotected a packet sent using the new keys.  An endpoint SHOULD retain old keys for some time after unprotecting a packet sent using the new keys.  Discarding old keys too early can cause delayed packets to be discarded.  Discarding packets will be interpreted as packet loss by the peer and could adversely affect performance. </ins> 6.2. Phase bit alone can be used to select keys. An endpoint MAY allow a period of approximately the Probe Timeout  <ins> (PTO; see QUIC-RECOVERY) after receiving a packet that uses the new key generation before it creates the next set of packet protection keys.  These updated keys MAY replace the previous keys at that time. With the caveat that PTO is a subjective measure - that is, a peer could have a different view of the RTT - this time is expected to be long enough that any reordered packets would be declared lost by a peer even if they were acknowledged and short enough to allow for subsequent key updates. </ins> Endpoints need to allow for the possibility that a peer might not be able to decrypt packets that initiate a key update during the period sufficient time could lead to packets being discarded. An endpoint SHOULD retain old read keys for no more than three times  <ins> the PTO after having received a packet protected using the new keys. After this period, old read keys and their corresponding secrets SHOULD be discarded. </ins> 6.6.


Some networks may exhibit higher degrees of packet reordering, causing a sender to detect spurious losses.  Additionally, packet reordering could be more common with QUIC than TCP, because network <del> elements that could observe and reorder out-of-order TCP packets cannot do that for QUIC, because packet numbers are encrypted. Algorithms that increase the reordering threshold after spuriously detecting losses, such as RACK RACK, have proven to be useful in TCP and are expected to be at least as useful in QUIC. </del>  6.1.2. Initial packet number space doubles the length of the timeout in the Handshake packet number space. <del> The time length of a connection that is experiencing consecutive PTOs is limited by the endpoint's idle timeout. </del>  The probe timer MUST NOT be set if the time threshold (time- threshold) loss detection timer is set.  The time threshold loss not confirmed (see Section 4.1.2 of QUIC-TLS), even if there are no packets in flight.  When the PTO fires, the client MUST send a Handshake packet if it has Handshake keys, otherwise it MUST send an <del> Initial packet in a UDP datagram of at least 1200 bytes. </del>  6.2.3. the PTO expiry, subject to the address validation limits in Section 8.1 of QUIC-TRANSPORT. <del> Endpoints can also use coalesced packets to ensure that each datagram elicits at least one acknowledgement.  For example, a client can coalesce an Initial packet containing PING and PADDING frames with a 0-RTT data packet and a server can coalesce an Initial packet containing a PING frame with one or more packets in its first flight. </del>  6.2.4. QUIC begins every connection in slow start with the congestion window set to an initial value.  Endpoints SHOULD use an initial congestion window of 10 times the maximum datagram size (max_datagram_size), <del> limited to the larger of 14720 or twice the maximum datagram size. This follows the analysis and recommendations in RFC6928, increasing the byte limit to account for the smaller 8 byte overhead of UDP compared to the 20 byte overhead for TCP. </del>  If the maximum datagram size changes during the connection, the initial congestion window SHOULD be recalculated with the new size.

This PR is also a good spot to comment on my accidental commit() to master. I can make any changes to that in this PR.
This was filed a while ago (before WGLC ended), didn't want to drop it.
Editorial, design?
There is a design issue here regarding the initial congestion window that I would like to see split out into a new issue (which can be marked design). The rest appear to be things we can address with editorial changes.
NAME I moved the IW text into , though I'm not sure I think it's worth revisiting.
Thanks :) I agree that it isn't worth re-opening, but we did sign on to at least discuss whether it is worth discussing.
Do we have a plan for this one?
So, is there a plan? (Is there even anything left to do?)
Some of these are addressed already, others are in an accidental commit(), and for some there's no need to address them in the text. The rest are in
Some suggestions only.One nit, but LGTM.

Some networks may exhibit higher degrees of packet reordering, causing a sender to detect spurious losses.  Additionally, packet reordering could be more common with QUIC than TCP, because network  <ins> elements that could observe and reorder TCP packets cannot do that for QUIC, because QUIC packet numbers are encrypted.  Algorithms that increase the reordering threshold after spuriously detecting losses, such as RACK RACK, have proven to be useful in TCP and are expected to be at least as useful in QUIC. </ins> 6.1.2. Initial packet number space doubles the length of the timeout in the Handshake packet number space.  <ins> The total length of time over which consecutive PTOs expire is limited by the idle timeout. </ins> The probe timer MUST NOT be set if the time threshold (time- threshold) loss detection timer is set.  The time threshold loss not confirmed (see Section 4.1.2 of QUIC-TLS), even if there are no packets in flight.  When the PTO fires, the client MUST send a Handshake packet if it has Handshake keys, otherwise it MUST send an  <ins> Initial packet in a UDP datagram with a payload of at least 1200 bytes. </ins> 6.2.3. the PTO expiry, subject to the address validation limits in Section 8.1 of QUIC-TRANSPORT.  <ins> Endpoints can also use coalesced packets (see Section 12.2 of QUIC- TRANSPORT) to ensure that each datagram elicits at least one acknowledgement.  For example, a client can coalesce an Initial packet containing PING and PADDING frames with a 0-RTT data packet and a server can coalesce an Initial packet containing a PING frame with one or more packets in its first flight. </ins> 6.2.4. QUIC begins every connection in slow start with the congestion window set to an initial value.  Endpoints SHOULD use an initial congestion window of 10 times the maximum datagram size (max_datagram_size),  <ins> limited to the larger of 14720 bytes or twice the maximum datagram size.  This follows the analysis and recommendations in RFC6928, increasing the byte limit to account for the smaller 8 byte overhead of UDP compared to the 20 byte overhead for TCP. </ins> If the maximum datagram size changes during the connection, the initial congestion window SHOULD be recalculated with the new size.


10. <del> This document does not create any new IANA registries, but it registers the values in the following registries: </del>  <del> TLS ExtensionType Values Registry TLS-REGISTRIES - IANA is to register the quic_transport_parameters extension found in quic_parameters.  The Recommended column is to be marked Yes.  The TLS 1.3 Column is to include CH and EE. QUIC Transport Error Codes Registry QUIC-TRANSPORT - IANA is to register the KEY_UPDATE_ERROR (0xe), as described in key-update- error. </del>  An endpoint has received more data in CRYPTO frames than it can buffer.  An endpoint has reached the confidentiality or integrity limit for the AEAD algorithm used by the given connection.

I guess technically we could have let both of these stand and used the same error code for both conditions, but that seems unnecessary. Moving the AEAD limit seems easier.
The transport draft-30 introduced AEADLIMITREACHED(0xe) error code. Meanwhile, the TLS draft also has KEYUPDATEERROR error code (0xe), and they collide with each other. I believe they are transport error code, not application error code.
It would probably be a good idea to list the KEYUPDATEERROR in the error code table in the transport document.
Ugh, that's unfortunate. I think that we need to renumber the newer one (and yeah, put them all in the same document so it doesn't happen again).
I believe we should fix the fact that we have two errors with the same code, however I think it would be best to just use 0xE for all these scenarios. At this point in the process, we're working on deploying h3-29 and having a breaking wire change like this would be very unfortunate. Additionally, I cannot think of any action an implementation would take that would be different between KEYUPDATEERROR and AEADLIMITREACHED. Any chance we could just rename AEADLIMITREACHED to KEYUPDATEERROR and call it a day?
I think it's useful to distinguish what happened for logging and diagnostic purposes. I also don't understand why this creates a significant implementation inconvenience. On the sending side, presumably you're just sending the wrong code for a while and it's a bug, and on the receiving side, given that you have to handle unknown error codes anyway (because they can be IANA Registered), what's the problem if you receive an unknown error code in this likely quite uncommon case?
I agree with NAME There is a separate question of whether -31 will be a new version or not, but let's not get ahead of ourselves.
I am trying to write a test for the generation of the key update error code. This is a bit hard, since by default the implementation will rotate keys after sending more than packets, and thus the condition is not seen. One could test that the peer has sent more than packets without rotating the keys, but then there is some fuzziness to what the limit is. For example, for AES GCM, should that be "received 2^25 packets" or "2^25 + 1"? The error is also a bit silly. It requires tracking how many packets were decrypted with a given key, and then disconnecting with a specific error if the limit is exceeded. But the node that tracks the limit could also unilaterally trigger a key rotation if the receive count is getting close enough. Of course, the non-standard peer could refuse to update its own key, and never rotate the key phase. Is that the condition for KEYUPDATEERROR?
NAME this issue is merely changing the error code for an error that already exists. I read your comment as a question about testing this, but you could be asking about when this error code ought to be sent. My reading is that the sender is required to close the connection when it sends too many packets, and the receiver is required to close the connection when too many packets fail authentication, and I think the text is clear on precisely when both of these ought to happen. A receiver is not required to, but could do what you're suggesting, and honestly, the precise value of "2^25 + 1" or "2^25" wouldn't matter IMO, since the receiver can only know a lower bound on the number of packets sent with the same key (since packet loss, etc). But if the receiver were to close with this error, doing it at the point that there is no ability for the sender to update the keys makes sense. The simplest thing to do would be to check if "2^25 + 1" packets have been received with the same key.
NAME the sender is NOT required to close the connection when it sends too many packets. That's not what the TLS limits say. The requirement is to use key rotation, move to the next epoch, and start using a new encryption key. Reading the fine print, I believe the KEYUPDATEERROR error will happen if the node starts a key rotation but the peer keeps using the old key for too long, for example if the peer continues using the old key but acknowledges packets sent with the new key. The AEADLIMITREACHED error will happen if the peer transmits too many packets for the epoch. This is different from the integrity limit, which does require closing the connection if too many decryption failures are observed.
NAME -- yes, I didn't read carefully; you're right. So, as I suggested earlier IMO the best you can do at the receiver is check for 2^25 + 1, even though it is a lower bound. It does require tracking the number of packets as you say though.
I can't wait to see the first error report in which some poor dude tries to transmit more than 50GB over a QUIC connection and hits a bug in the implementation of key update...
NAME There's another condition where KEYUPDATEERROR might occur: If you acknowledge a packet in key phase N+1 using key phase N. It's just a MAY in the specification though (I don't know why, this should be trivial to implement): NAME That's why we recently added a key update test to the interop runner. Unfortunately, it looks like a number of implementations (even those that already boasted about rolling out QUIC into production) treat key updates as some kind of optional feature, which it isn't. This is bad, as this now prevents others from rolling out QUIC implementations that initiate a key update early in the connection to exercise the mechanism.
NAME it would be non-trivial for neqo to implement that MAY. The information about what keys are used is quite well separated from the code that manages acknowledgments.
NAME That's the same in quic-go. There's another way to determine this condition though: As you need to keep track of the largest acknowledged PN anyway, you can check when you receive an ACK for a packet sent in the current key phase that you unprotected at least one packet in the current key phase. If that's not the case, the ACK for the current key phase must have been sent in the previous key phase, and you can throw an error.
NAME you and I were thinking of the same error condition, which I described as "for example if the peer continues using the old key but acknowledges packets sent with the new key". And yes, I was also going to test that by implementing the "last acknowledged" check.
NAME I just implemented this check in quic-go, and it was literally less than 10 LOC. Going to roll that out in my interop runner image soon, so we'll see if people are responding to key updates correctly.
It's looking to me like I'm alone in preferring to merge the two codes, so I just wanted to say that I don't feel too strongly here. If there's WG consensus to merge then I'm OK with that.
Ugh. Thanks for resolving this.

10.  <ins> This document registers the quic_transport_parameters extension found in quic_parameters in the TLS ExtensionType Values Registry TLS- REGISTRIES. </ins>  <ins> The Recommended column is to be marked Yes. The TLS 1.3 Column is to include CH and EE. </ins> An endpoint has received more data in CRYPTO frames than it can buffer. <ins> An endpoint detected errors in performing key updates; see Section 6 of QUIC-TLS. </ins> An endpoint has reached the confidentiality or integrity limit for the AEAD algorithm used by the given connection.


if clients send an empty Token field when the server address has changed from when the NEW_TOKEN frame was received. <del> Therefore, clients SHOULD NOT send a token received in a NEW_TOKEN frame from one server address in an Initial packet that is sent to a different server address.  As strict equality might reduce the utility of this mechanism, clients MAY employ heuristics that result in different server addresses being treated as equivalent, such as treating addresses with a shared prefix of sufficient length as being functionally equivalent (for instance, /24 in IPv4 or /56 in IPv6). In addition, clients SHOULD treat a preferred address that is successfully validated as equivalent to the address on which the connection was made; see preferred-address. </del>  Sending a Retry packet (packet-retry) offers a server the option to change the Token field.  After sending a Retry, the server can also

This is another judgment call, but as this wasn't a MUST in the first place, we weren't really preventing an attack. This just removes the recommendation to remove NEW_TOKEN tokens from Initial packets to new server addresses. It leaves the generic guidance, which is far more nuanced. I've added some commentary about the effect of withholding tokens on performance, as it seems like that is worth highlighting here. All in all, this leans more toward saying that request forgery is not the responsibility of QUIC deployments.
I'm merging this. If anyone thinks this ought to be dealt with differently, please say so on this PR or on the issue, and we can revisit.
Apologies for merging this PR. NAME NAME : we should still run through the process as a design issue.
PR adds a new normative restriction on when tokens received in NEWTOKEN can be used: "Therefore, clients SHOULD NOT send a token received in a NEWTOKEN frame from one server address in an Initial packet that is sent to a different server address. As strict equality might reduce the utility of this mechanism, clients MAY employ heuristics that result in different server addresses being treated as equivalent, such as treating addresses with a shared prefix of sufficient length as being functionally equivalent (for instance, /24 in IPv4 or /56 in IPv6). In addition, clients SHOULD treat a preferred address that is successfully validated as equivalent to the address on which the connection was made; see {{preferred-address}}." I believe this will greatly reduce the practical value of 0-RTT, since most responses cannot fit into 2 packets(assuming 1 to complete the handshake) and unless Anycast is used for load balancing, it's extremely common for the IPs not to match and quite common for them not to share a /24 or /56. From local testing, when I resolved URL twice in a row a second apart, I got 172.217.6.196 and 172.217.12.164. I can follow up with some numbers on how frequently this occurs today in existing deployments if that's useful? Also, from an editorial perspective, I also don't think this normative statement should only be in security considerations, since I believe it's more likely to be missed there.
On the email thread, I suggested changing the restriction to something like "MUST NOT use tokens from one RFC address space in a different one.", but NAME said that people setup vulnerable servers on public internet addresses. The key to SSRF is that one peer has a privilege that the other peer does not, but if it's a public address, couldn't either peer send traffic to the address without any clever use of another QUIC peer? Address spoofing isn't trivial, but it's certainly possible in some cases, so anyone only relying on peer IP address would already be vulnerable, so I'm not grasping how QUIC is making this worse for public IPs.
No guaranteed. I used to work at an office that had a firewall and a VPN. There were a bunch of unprotected server behind that firewall. Most, if not all, of those servers used addresses from public address space. This is likely not common practice any more (addresses are expensive), but it is also not completely unexpected. To give an example, one thing you might consider is that you have a subnet (x::/56 say) where servers all operate public addresses and operate services that are accessible on the public Internet. However, that network also has a bypass for authentication checks if the source address of a request is in the x::/56 subnet to enable cross-traffic between services without overheads. This might be consider acceptable if you also assume that ingress filtering is in use for the network. I need to stress again how much I think that this sort of thing is monumentally inadvisable. On the other hand, if we're looking for hypotheticals (in the absence of actual cases), then these are at least plausible cases to consider. That said, I definitely agree that this restriction is not great and it probably is too much. I don't know how to generate a heuristic that can capture a change of the magnitude you identify. Google is lucky to have addresses that are contiguous at ~/20; I imagine that other servers have to work with a larger spread. (BTW, I get 142.250.66.228) After considering this, if we are not going to implement more rigorous protections for other aspects of this (migration spoofing), then it seems reasonable to allow this reuse and accept the risk. That means relying on the generic protections more for this case also, which already talk about moving from public space to link-local/site-local/private spaces. We might even just rely solely on those protections. (The editorial note is well-taken; we can fix that.)
Reopening so that we can get this into our processes.
Closing this now that WGLC has finished. was already merged.
One nit.Thanks NAME greatly appreciated.

if clients send an empty Token field when the server address has changed from when the NEW_TOKEN frame was received.  <ins> Clients could avoid using NEW_TOKEN if the server address changes. However, not including a Token field could adversely affect performance.  Servers could rely on NEW_TOKEN to enable sending of data in excess of the three times limit on sending data; see validate-handshake.  In particular, this affects cases where clients use 0-RTT to request data from servers. </ins> Sending a Retry packet (packet-retry) offers a server the option to change the Token field.  After sending a Retry, the server can also


To avoid excessively small idle timeout periods, endpoints MUST increase the idle timeout period to be at least three times the <del> current Probe Timeout (PTO).  This allows for multiple PTOs to expire prior to idle timeout, ensuring the idle timeout does not expire as a result of a single packet loss. </del>  10.1.1.

I think this was the intent of the sentence, but I kept not being able to parse it so.

To avoid excessively small idle timeout periods, endpoints MUST increase the idle timeout period to be at least three times the  <ins> current Probe Timeout (PTO).  This allows for multiple PTOs to expire, and therefore multiple probes to be sent in the event of loss, prior to idle timeout. </ins> 10.1.1.


8.2. <del> Path validation is used during connection migration (see migration) by the migrating endpoint to verify reachability of a peer from a new local address.  In path validation, endpoints test reachability between a specific local address and a specific peer address, where an address is the two-tuple of IP address and port. </del>  Path validation tests that packets sent on a path to a peer are received by that peer.  Path validation is used to ensure that address. Path validation does not validate that a peer can send in the return <del> direction.  The peer performs independent validation of the return path. </del>  Path validation can be used at any time by either endpoint.  For instance, an endpoint might check that a peer is still in possession packet on that path.  Effective NAT traversal needs additional synchronization mechanisms that are not provided here. <del> An endpoint MAY include PATH_CHALLENGE and PATH_RESPONSE frames that are used for path validation with other frames.  In particular, an endpoint can pad a packet carrying a PATH_CHALLENGE for Path Maximum Transfer Unit (PMTU) discovery (see pmtud), or an endpoint can include a PATH_RESPONSE with its own PATH_CHALLENGE. </del>  <del> When probing a new path, an endpoint might want to ensure that its peer has an unused connection ID available for responses.  The endpoint can send NEW_CONNECTION_ID and PATH_CHALLENGE frames in the same packet.  This ensures that an unused connection ID will be available to the peer when sending a response. </del>  <del> 8.3. </del>  To initiate path validation, an endpoint sends a PATH_CHALLENGE frame containing an unpredictable payload on the path to be validated. An endpoint MAY send multiple PATH_CHALLENGE frames to guard against packet loss.  However, an endpoint SHOULD NOT send multiple <del> PATH_CHALLENGE frames in a single packet.  An endpoint SHOULD NOT send a PATH_CHALLENGE more frequently than it would an Initial packet, ensuring that connection migration is no more load on a new path than establishing a new connection. </del>  The endpoint MUST use unpredictable data in every PATH_CHALLENGE frame so that it can associate the peer's response with the corresponding PATH_CHALLENGE. <del> 8.4. </del>  On receiving a PATH_CHALLENGE frame, an endpoint MUST respond by echoing the data contained in the PATH_CHALLENGE frame in a information.  The peer is expected to send more PATH_CHALLENGE frames as necessary to evoke additional PATH_RESPONSE frames. <del> 8.5. </del>  Path validation succeeds when a PATH_RESPONSE frame is received that contains the data that was sent in a previous PATH_CHALLENGE frame. frame is not adequate validation, since the acknowledgment can be spoofed by a malicious peer. <del> 8.6. </del>  Path validation only fails when the endpoint attempting to validate the path abandons its attempt to validate the path. Failure to validate a path does not cause the connection to end unless there are no valid alternative paths available. <del> An endpoint uses a new connection ID for probes sent from a new local address; see migration-linkability for further discussion.  An endpoint that uses a new local address needs to ensure that at least one new connection ID is available at the peer.  That can be achieved by including a NEW_CONNECTION_ID frame in the probe. Receiving a PATH_CHALLENGE frame from a peer indicates that the peer is probing for reachability on a path.  An endpoint sends a PATH_RESPONSE frame in response, as per migrate-validate. </del> PATH_CHALLENGE, PATH_RESPONSE, NEW_CONNECTION_ID, and PADDING frames are "probing frames", and all other frames are "non-probing frames". A packet containing only probing frames is a "probing packet", and a address.  Thus an endpoint can migrate to a new local address without first validating the peer's address.  When migrating, the new path might not support the endpoint's current sending rate.  Therefore, the endpoint resets its congestion controller and RTT estimate, as described in migration-cc. The new path might not have the same ECN capability.  Therefore, the endpoint verifies ECN capability as described in ecn. <del> To establish reachability on the new path, an endpoint initiates path validation (migrate-validate) on the new path.  An endpoint MAY defer path validation until after a peer sends the next non-probing frame to its new address. Path validation is necessary to verify reachability of a peer on a new network path.  Acknowledgments cannot be used for path validation as they contain insufficient entropy and might be spoofed.  No method is provided to establish return reachability, as endpoints independently determine reachability on each direction of a path. </del> 9.3. Receiving a packet from a new peer address containing a non-probing frame indicates that the peer has migrated to that address. <del> In response to such a packet, an endpoint MUST start sending subsequent packets to the new peer address and MUST initiate path validation (migrate-validate) to verify the peer's ownership of the unvalidated address. </del>  An endpoint MAY send data to an unvalidated peer address, but it MUST protect against potential attacks as described in address-spoofing loss detection and congestion state can reduce the performance impact of the attack. <del> An endpoint only changes the address that it sends packets to in response to the highest-numbered non-probing packet.  This ensures that an endpoint does not send packets to an old peer address in the case that it receives reordered packets. </del> After changing the address to which it sends non-probing packets, an endpoint could abandon any path validation for other addresses.

I tried to be conservative about changes, but there were a few paragraphs I thought should be moved. Otherwise, the changes are minimal. URL is the commit that adds a paragraph to clarify the main confusion on
Something to look into...
NAME I was previously finding the text confusing and potentially contradictory. When I read it all at once, I believe I understand the intent, but hopefully this change makes each portion clearer and less dependent upon adjacent sections. There's probably a bit more worth doing, but at some point I'm willing to call it good enough.
We decided to only specify client-initiated connection migration in QUIC v1. The section about Connection Migration mostly talks about endpoint and peer though, which makes it really hard to parse. I haven't implement migration yet in quic-go, and reading this section confused me at multiple places. I believe that specifying the role of the endpoint would make things a lot clearer, and could offer to come up with a PR.
I don't think this is worth doing, but would look at a PR.
To clarify, this would only apply to connection migration, not the path validation section that immediately precedes it.
NAME The idea was to keep the migration mechanisms generic, since they can be applied to either endpoint. However, this isn't adequate if we support migration at both ends, since you'd have to deal with other situations, such as simultaneous migration. v1 simply limits the scope of where these mechanisms ought to be applied. There's value in that, but I understand your reservations. Like NAME I'm not keen to follow the premise of this issue, but I'd be open to a PR. I'd also be open to clarifying the parts you found confusing.
Looking at that section again, I don't believe I want to do anything about this issue.
This choice was deliberate. Maybe it's nicer, but now is not really a good time.
Update: I thought simultaneous path validation wasn't necessary, but it looks like I misunderstood. Changing this to an editorial issue so I can re-read this text and hopefully make it clearer. I believe this is already implied, but I couldn't find it stated anywhere. I think it'd be ideal to set expectations that servers are not expected to validate multiple paths at the same time. Validating multiple paths seems like a plausible feature for multipath, but adds undue complexity for connection migration.
Does that imply that a client can only probe a single path at a time? What's the time it has to wait after apparent unsuccessful probing of one path until it can probe the next?
NAME I'm not sure if I agree. It has been my assumption that a server is expected to respond to clients probing multiple paths concurrently. Note that in , we added the following text:
Hmm, I didn't think this was already implied. You're not expected (or allowed) to send non-probing packets on multiple paths, but you can certainly get probes in from multiple different remote endpoints. This is somewhat necessary if we're to be resilient to the various packet racing attacks -- if an attacker can cause you to be upset about a packet coming in from a different destination then they can really mess you up.
I'm a bit confused about why a client needs to probe multiple paths simultaneously, given we have connection migration, not multipath. To me, it just adds complexity. I guess I can just give out a single CID to mitigate the issue, but do we really expect servers to correctly implement probing on multiple paths? What if they all succeed? A client can still only use one. NAME can you clarify the packet racing issue a bit more or point me to text that already does? I'd like to draw a state transition diagram for path validation, but if we can have multiple paths being validated at once, I'm not sure that's practical.
NAME So the scenario is that a client is using one path and has 2 or more new ones available? I've heard future hardware could make that possible, though I've never seen that case myself. The short of this is now that we're trying to move to IETF QUIC style connection migration and path validation, and I'm finding it fairly confusing.
I think there's a pretty big distinction between probing a path to establish the potential for connectivity and migrating the connection to that path. For example, I would not be at all surprised if a client were to probe all available paths and choose the "best" one to migrate to when the need arises (often, although not always, there is some warning that conditions are worsening on the current path which provides the opportunity to evaluate alternatives). That said, I think a large part of the reason to allow probing on multiple paths is to avoid requiring synchronization of a shared understanding as to the state of current probes. We don't want to add a bunch of machinery to establish failure of a probe on a different path. So even if we don't think that a client would ever intentionally probe multiple paths, if a client probes one path and hears no response, it seems completely reasonable to think that it would switch and probe a different path if it had one available. If it turns out that there was just some delay for that initial path, say it had a 1.5s RTT, now the server is going to think the client probed two paths at the same time, when in reality the client thinks it's just talking to one. It also doesn't seem particularly harmful to allow this? As NAME noted, there's some good text in there about limiting state required on the server, and of course path validation can happen at any time, so it seems completely reasonable to "forget" that any given path was validated if a client probes and then doesn't end up using the path. (Note that you'd likely want to retire the CID used on that path, so as to replace it with a new one when you did that forgetting, which also serves as a nice heuristic for the other endpoint to say "oh, hmm, they don't think they're talking to me over there", but we can dig into that later.) The packet racing thing is a reference to and the previous section. This led us to require endpoints to validate both the current and the new path when it changes, which conveniently provokes some traffic to reconfirm the active path and makes it more difficult to disrupt a connection that's gone idle. While technically this is only discussing one "new" path and then the old path being validated at the same time, it seems as though it would be trivial for such an attacker to convince an endpoint that it was seeing "probes" (since we've discussed that it's not that difficult to identify a probing packet as an observer) from multiple paths and therefore be quite upset if such behavior were restricted. I'm not saying this happens all the time, but it seems pretty reasonable to have a Wi-Fi interface and then a dual SIM device with multiple cellular interfaces. Overall, it seems as though (even if this isn't crazy common) it's not unreasonable to expect that this behavior might happen, and it doesn't seem like there's a big benefit to restricting it, nor harm in allowing it, beyond the existing text reminding endpoints to be careful with the amount of state they're willing to allocate to any individual client (which is a thing outside of any probing/migration concept anyways).
+1 to what NAME says. The resolution of (which ended up as PR ) was that a server has to remember state of N paths where N is the number of CIDs active at one time. As NAME says, a client cannot tell when a server thinks that it received two probes concurrently. Therefore, it is very tricky to have this kind of restriction within a specification that allows clients to retain multiple backup paths.
I agree with NAME and NAME here. If a client sends a probe on interface A, and then interface A disconnects and then interface B appears immediately, the client should be able to probe on interface B even though to the server will receive two independent probes at roughly the same time. Servers need to be able to handle multiple concurrent probes in order for migration to be reliable.
Note that while the server needs to respond to the probes (so the client can validate the paths), I don't think there's any requirement that the server continue to regard all those paths as validated. When the client actually migrates, if the server wants to treat the migration as an unvalidated path and initiate validation again, that seems fine.
Well, the paths aren't validated until the server explicitly validates them, so it seems like the cost is pretty minimal until the server decides to engage resources for that path. The server can also respond on the old path if the client is just probing.
NAME do you want to think about how you reached your conclusion here and see if there is something we can do to avoid that? It seems to me that the cost to a server is limited by how many paths it wants to probe, not how many the client probes, so the state is well managed. It's a little less neat if the client actively migrates multiple times. But I would say that the server can abandon probes to keep the number of active probes down to at most 2 (one for the most recent validated path, one for the most recent active path). Is that something we need to explain more thoroughly?
I have read this text quite a bit, but not end-to-end in quite a long time, so I believe that's the cause of my confusion. I'm planning to re-read all the path validation and connection migration text and I'll send out a PR if there are points that I think I are unclear.
NAME have you had a chance to do this?
Unfortunately, not yet.
Note that man-on-the-side attacker can take a valid packet and resend it from a different address. The crypto does not protect against that. If the attacker somehow isolates probe packets, the server will end up receiving probes from a variety of endpoints. Better not do something silly in that condition.
Would people please take a final look at the PR?
A few comments, but pretty close.

8.2.  <ins> Path validation is used by both peers during connection migration (see migration) to verify reachability after a change of address.  In path validation, endpoints test reachability between a specific local address and a specific peer address, where an address is the two- tuple of IP address and port. </ins> Path validation tests that packets sent on a path to a peer are received by that peer.  Path validation is used to ensure that address. Path validation does not validate that a peer can send in the return  <ins> direction.  Acknowledgments cannot be used for return path validation as they contain insufficient entropy and might be spoofed.  Endpoints independently determine reachability on each direction of a path, and therefore return reachability can only be established by the peer. </ins> Path validation can be used at any time by either endpoint.  For instance, an endpoint might check that a peer is still in possession packet on that path.  Effective NAT traversal needs additional synchronization mechanisms that are not provided here.  <ins> An endpoint MAY include other frames with the PATH_CHALLENGE and PATH_RESPONSE frames used for path validation.  In particular, an endpoint can include PADDING frames with a PATH_CHALLENGE frame for Path Maximum Transfer Unit (PMTU) discovery (see pmtud); it can also include a PATH_CHALLENGE frame with its own PATH_RESPONSE frame. </ins>  <ins> An endpoint uses a new connection ID for probes sent from a new local address; see migration-linkability.  When probing a new path, an endpoint expecting responses on the new path needs to ensure that its peer has an unused connection ID.  Sending NEW_CONNECTION_ID and PATH_CHALLENGE frames in the same packet ensures that an unused connection ID will be available to the peer when sending a response. An endpoint can choose to simultaneously probe multiple paths.  The number of simultaneous paths used for probes is limited by the number of extra connection IDs its peer has previously supplied, since each new local address used for a probe requires a previously unused connection ID. </ins>  <ins> 8.2.1. </ins> To initiate path validation, an endpoint sends a PATH_CHALLENGE frame containing an unpredictable payload on the path to be validated. An endpoint MAY send multiple PATH_CHALLENGE frames to guard against packet loss.  However, an endpoint SHOULD NOT send multiple  <ins> PATH_CHALLENGE frames in a single packet. An endpoint SHOULD NOT probe a new path with packets containing a PATH_CHALLENGE frame more frequently than it would send an Initial packet.  This ensures that connection migration is no more load on a new path than establishing a new connection. </ins> The endpoint MUST use unpredictable data in every PATH_CHALLENGE frame so that it can associate the peer's response with the corresponding PATH_CHALLENGE.  <ins> 8.2.2. </ins> On receiving a PATH_CHALLENGE frame, an endpoint MUST respond by echoing the data contained in the PATH_CHALLENGE frame in a information.  The peer is expected to send more PATH_CHALLENGE frames as necessary to evoke additional PATH_RESPONSE frames.  <ins> 8.2.3. </ins> Path validation succeeds when a PATH_RESPONSE frame is received that contains the data that was sent in a previous PATH_CHALLENGE frame. frame is not adequate validation, since the acknowledgment can be spoofed by a malicious peer.  <ins> 8.2.4. </ins> Path validation only fails when the endpoint attempting to validate the path abandons its attempt to validate the path. Failure to validate a path does not cause the connection to end unless there are no valid alternative paths available.  PATH_CHALLENGE, PATH_RESPONSE, NEW_CONNECTION_ID, and PADDING frames are "probing frames", and all other frames are "non-probing frames". A packet containing only probing frames is a "probing packet", and a address.  Thus an endpoint can migrate to a new local address without first validating the peer's address. <ins> To establish reachability on the new path, an endpoint initiates path validation (migrate-validate) on the new path.  An endpoint MAY defer path validation until after a peer sends the next non-probing frame to its new address. </ins> When migrating, the new path might not support the endpoint's current sending rate.  Therefore, the endpoint resets its congestion controller and RTT estimate, as described in migration-cc. The new path might not have the same ECN capability.  Therefore, the endpoint verifies ECN capability as described in ecn.  9.3. Receiving a packet from a new peer address containing a non-probing frame indicates that the peer has migrated to that address.  <ins> An endpoint only changes the address that it sends packets to in response to the highest-numbered non-probing packet.  This ensures that an endpoint does not send packets to an old peer address in the case that it receives reordered packets. In response to such a packet, an endpoint MUST send subsequent packets to the new peer address and MUST initiate path validation (migrate-validate) to verify the peer's ownership of the address if validation is not already underway. </ins> An endpoint MAY send data to an unvalidated peer address, but it MUST protect against potential attacks as described in address-spoofing loss detection and congestion state can reduce the performance impact of the attack.  After changing the address to which it sends non-probing packets, an endpoint could abandon any path validation for other addresses.


these countermeasures could be insufficient if UDP-based services do not properly authorize requests.  21.4.1. QUIC offers some opportunities for an attacker to influence or

I also think it might be worthwhile to move much of this section to security considerations, leaving only the requirements here. NAME WDYT?
This is already in the security considerations section, so we're good. Though it's hard to see, because the section is already so large.
I find Martin's argument that QUIC servers are able to control their topology well enough to prevent request forgery attacks somewhat persuasive. However: I think the document should be clearer that if you can't do this you can't deploy QUIC as a server. We should add text indicating that a candidate extension to allow migration by the client would need to deal with this (likely by adding one of the countermeasures we decided not to ass). This is a very long specification and otherwise the institutional knowledge about this issue is likely to be lost.
If people agree, I can provide PRs for these.
These sound like good additions that I would welcome. (I would write the PR, but I don't trust myself, I'd appreciate you providing text.)
Sounds like a good path forward.
Do you mean migration by the server?
I'm not clear on the grammar, but I mean "the server address changing"
FWIW, when we've talked about a server migration extension before, it was usually in the form of a frame that mimics the preferred_address TP, prompting the client to probe the new address and actually make the change. That approach seems like it limits the attacks to those already possible with SPA, though it broadens the window / number of attempts.
Could people take a look at the PR? Would like to move this along.
Thanks for writing this up. My only comment is that this is two points and therefore two paragraphs.

these countermeasures could be insufficient if UDP-based services do not properly authorize requests. <ins> Because the migration attack described in request-forgery-with- spoofed-migration is quite powerful and does not have adequate countermeasures, QUIC server implementations should assume that attackers can cause them to generate arbitrary UDP payloads to arbitrary destinations.  QUIC servers SHOULD NOT be deployed in networks that also have inadequately secured UDP endpoints. Although it is not generally possible to ensure that clients are not co-located with vulnerable endpoints, this version of QUIC does not allow servers to migrate, thus preventing spoofed migration attacks on clients.  Any future extension which allows server migration MUST also define countermeasures for forgery attacks. </ins> 21.4.1. QUIC offers some opportunities for an attacker to influence or


use a pre-shared key and validated the client's pre-shared key binder; see Section 4.2.11 of TLS13. <del> The client has not demonstrated liveness, unless a RETRY packet was used. </del>  Any received 0-RTT data that the server responds to might be due to a replay attack.

Three minor changes here; separate if needed: The section title refers to frames, but the content discusses packets The text refers to a RETRY packet; packet types are not shouting-case Retry is not the only way to verify liveness prior to handshake completion (e.g. Connection ID on packets with a multi-round-trip handshake); reference to address validation for more context

use a pre-shared key and validated the client's pre-shared key binder; see Section 4.2.11 of TLS13.  <ins> The client has not demonstrated liveness, unless the server has validated the client's address with a Retry packet or other means; see Section 8.1 of QUIC-TRANSPORT. </ins> Any received 0-RTT data that the server responds to might be due to a replay attack.


round-trip time multiplier, is 9/8.  The RECOMMENDED value of the timer granularity (kGranularity) is 1ms.  Implementations MAY experiment with absolute thresholds, thresholds from previous connections, adaptive thresholds, or including RTT variation.  Smaller thresholds reduce reordering resilience and

This draft already has an informative reference to RACK, so it seems sensible to note the small difference in time threshold.

round-trip time multiplier, is 9/8.  The RECOMMENDED value of the timer granularity (kGranularity) is 1ms. <ins> TCP's RACK (RACK) specifies a slightly larger threshold, equivalent to 5/4, for a similar purpose.  Experience with QUIC shows that 9/8 works well. </ins> Implementations MAY experiment with absolute thresholds, thresholds from previous connections, adaptive thresholds, or including RTT variation.  Smaller thresholds reduce reordering resilience and


discretionary as, depending on circumstances, they could interfere with or prevent legitimate uses. <del> Services offered over loopback interfaces (that is, the IPv6 address ::1 or the IPv4 address 127.0.0.1) often lack proper authentication. Endpoints MAY prevent connection attempts or migration to a loopback address.  Endpoints SHOULD NOT allow connections or migration to a loopback address if the same service was previously available at a different interface or if the address was provided by a service at a non-loopback address.  Endpoints that depend on these capabilities could offer an option to disable these protections. </del>  Similarly, endpoints could regard a change in address to link-local address RFC4291 or an address in a private use range RFC1918 from a

Text says >Services offered over loopback interfaces (that is, the IPv6 address ::1 or the IPv4 address 127.0.0.1) often lack proper authentication. On v4, loopback is technically the entire 127.0.0.0/8 network, not just that one address. But I wonder if we need the parenthetical anyway?

discretionary as, depending on circumstances, they could interfere with or prevent legitimate uses.  <ins> Services offered over loopback interfaces often lack proper authentication.  Endpoints MAY prevent connection attempts or migration to a loopback address.  Endpoints SHOULD NOT allow connections or migration to a loopback address if the same service was previously available at a different interface or if the address was provided by a service at a non-loopback address.  Endpoints that depend on these capabilities could offer an option to disable these protections. </ins> Similarly, endpoints could regard a change in address to link-local address RFC4291 or an address in a private use range RFC1918 from a


bytes. UDP datagrams MUST NOT be fragmented at the IP layer.  In IPv4 <del> (IPv4), the DF bit MUST be set to prevent fragmentation on the path. </del>  14.1.

For example, OSX doesn't have an API to set this bit (allegedly this will change with Big Sur). Furthermore, not all implementations might have access to an API that allows them to set bits in the IP header. If I remember correctly, this is one of the reasons we made ECN optional.
I think you wanted to quote this part ({{!IPv4=RFC0791}}), the DF bit MUST be set to prevent fragmentation on the path. Would a proposal be to make the MUST a SHOULD and say "unless this is not supported by the underlying OS"?
Yes, that was a weird copy-paste error. If we say "unless this is not supported by the underlying OS or the programming language" we can probably even keep the MUST.
This is an editorial issue, since the intent of the MUST cannot be to require something that isn't possible.
This seems fine. What do you think of the short version?

bytes. UDP datagrams MUST NOT be fragmented at the IP layer.  In IPv4  <ins> (IPv4), the DF bit MUST be set if possible, to prevent fragmentation on the path. </ins> 14.1.


authenticated negotiation of an application protocol (TLS uses ALPN ALPN for this purpose) <del> An endpoint verifies support for Explicit Congestion Notification (ECN) by observing whether the ACK frames acknowledging the first packets it sends carry ECN counts, as described in ecn-validation. </del>  The CRYPTO frame can be sent in different packet number spaces (packet-numbers).  The offsets used by CRYPTO frames to ensure

Yeah, this is better.
The sentence observing whether the ACK frames acknowledging the first packets it sends carry ECN counts, as described in {{ecn-validation}}. seems to not belong into that section.
Assuming one enables ECN on Initial and Handshake packets in order to perform ECN validation, then I think it's the right place?
NAME are you OK with NAME explanation, i.e., can we close with no action?
Not really happy with it, tbh. The sentence feels completely out of place as it stands now. I think we should either move it to the ECN section (I think that would make the most sense), or at least add an introductory sentence, along these lines:
I like the introductory sentence.
WFMThis is good, thanks!

authenticated negotiation of an application protocol (TLS uses ALPN ALPN for this purpose)  <ins> Endpoints can use packets sent during the handshake to test for Explicit Congestion Notification (ECN) support; see ecn.  An endpoint verifies support for ECN by observing whether the ACK frames acknowledging the first packets it sends carry ECN counts, as described in ecn-validation. </ins> The CRYPTO frame can be sent in different packet number spaces (packet-numbers).  The offsets used by CRYPTO frames to ensure


Receiving a packet from a new peer address containing a non-probing frame indicates that the peer has migrated to that address. <del> An endpoint only changes the address that it sends packets to in response to the highest-numbered non-probing packet.  This ensures that an endpoint does not send packets to an old peer address in the case that it receives reordered packets. In response to such a packet, an endpoint MUST send subsequent </del>  packets to the new peer address and MUST initiate path validation (migrate-validate) to verify the peer's ownership of the address if validation is not already underway.  An endpoint MAY send data to an unvalidated peer address, but it MUST protect against potential attacks as described in address-spoofing and on-path-spoofing.  An endpoint MAY skip validation of a peer

Reading over the text in the sections NAME commented on, it feels like the heart of the conflict is this: Some sections say that servers don't migrate in this version of QUIC. This section speaks generically about what an implementation MUST do if the peer migrates, suggesting clients have to obey them if the server does migrate. Additionally, we've discussed mitigations to other issues (e.g. ) in which a server might consider certain probes/migrations unacceptable and simply not respond to them. That possibility isn't currently discussed in the text. The solution to both appears to be the same: Condition the MUST on acceptance of the migration. If the migration is somehow unacceptable (the remote address violates some deployment-specific rule, or the migration is prohibited by this version of QUIC) these requirements simply don't attach. The recipient doesn't change where they're sending packets or validate the new address. That might mean the connection fails rather than migrating, but that's the price you pay. I think this is editorial, because it acknowledges a carve-out in the MUST that we've always implicitly used to accommodate other text in the doc.
says: for future work. If a client receives packets from a new server address not indicated by the preferred_address transport parameter, the client SHOULD discard these packets. However, this is after several sections which describe how connection migration works generically from either side, including this text in 9.3. frame indicates that the peer has migrated to that address. In response to such a packet, an endpoint MUST start sending subsequent packets to the new peer address and MUST initiate path validation (Section 8.2) to verify the peer's ownership of the unvalidated address. Which is confusing and in fact arguably contradictory. I would propose that we add a sentence to the top of 9.3 that explains that while the text is generic, it actually only applies to servers.
Is this enough? This appears in (that's paragraph 6, not sub-section 6 in case you look at the URL).
It's not awesome because it basically contradicts the generic text in 9.3. But maybe we're past the point of diminishing returns
The intent was to keep the path validation mechanisms generic so that an extension could potentially introduce server migration later. It's stated in 9.6 and the top of 9; before and after the generic part seems sufficient to me. That said, I think the server migration extension would still require that the client be the one to actually make the change. Only clients should ever spontaneously send from a new address. (P2P scenarios notwithstanding....)
NAME do I read your last comment as you being OK with closing this with no action?
To be honest, I still think this could use some work, but I'm willing to let it drop.
Thanks. Let's see if someone has energy/interest to do a PR during the next few days.
LGTM, but one nit. I'd like NAME to review this too.

Receiving a packet from a new peer address containing a non-probing frame indicates that the peer has migrated to that address.  <ins> If the recipient permits the migration, it MUST send subsequent </ins> packets to the new peer address and MUST initiate path validation (migrate-validate) to verify the peer's ownership of the address if validation is not already underway. <ins> An endpoint only changes the address to which it sends packets in response to the highest-numbered non-probing packet.  This ensures that an endpoint does not send packets to an old peer address in the case that it receives reordered packets. </ins> An endpoint MAY send data to an unvalidated peer address, but it MUST protect against potential attacks as described in address-spoofing and on-path-spoofing.  An endpoint MAY skip validation of a peer


The size of the first packet sent by a client will determine whether a server sends a Version Negotiation packet.  Clients that support <del> multiple QUIC versions SHOULD pad the first UDP datagram they send to the largest of the minimum datagram sizes from all versions they support.  This ensures that the server responds if there is a </del>  mutually supported version.  A server might not send a Version Negotiation packet if the datagram it receives is smaller than the minimum size specified in a different version; see initial-size. that contain packets that are all discarded. Clients MUST ensure that UDP datagrams containing Initial packets <del> have UDP payloads of at least 1200 bytes, adding padding to packets in the datagram as necessary.  A client that sends padded datagrams allows the server to send more data prior to completing address validation. </del>  Loss of an Initial or Handshake packet from the server can cause a deadlock if the client does not send additional Initial or Handshake The resulting minimum size of 21 bytes does not guarantee that a stateless reset is difficult to distinguish from other packets if the recipient requires the use of a connection ID.  To achieve that end, <del> the endpoint SHOULD pad all packets it sends to at least 22 bytes longer than the minimum connection ID that it might request the peer to include in packets that the peer sends.  This ensures that any stateless reset sent by the peer is indistinguishable from a valid packet sent to the endpoint.  An endpoint that sends a stateless reset in response to a packet that is 43 bytes or shorter SHOULD send a stateless reset that is one byte shorter than the packet it responds to. </del>  These values assume that the Stateless Reset Token is the same length as the minimum expansion of the packet protection AEAD.  Additional

Changes to explain Padding == PADDING Frame
Thanks again Gorry.
I have a long comment on a scattered observation that could be very simple to fix: In section 6, it wasn’t clear what were the ways that "padding" could be performed. There could be various ways - simply leave UDP payload unused, fill it with coalesced QUIC packets, add padding and other frames, etc and when the text says this I think it’s unclear to me what is expected - any or all of these, but it doesn't seem to be said. e.g.: /Clients that support multiple QUIC versions SHOULD pad the first UDP datagram they send to the largest of the minimum datagram sizes from all versions they support. / Is the assumption here, more like: /Clients that support multiple QUIC versions SHOULD coalesce QUIC packets or add PADDING frames to pad the first UDP datagram they send to the largest of the minimum datagram sizes from all versions they support. / … rather than some other technique to pad-out the datagram payload? See also: /Clients MUST ensure that UDP datagrams containing Initial packets have UDP payloads of at least 1200 bytes, adding padding to packets in the datagram as necessary. A client that sends padded datagrams allows the server to send more data prior to completing address validation./ and later: /In particular, an endpoint can pad a packet carrying a PATH_CHALLENGE for Path Maximum Transfer Unit (PMTU) discovery / So, I think the topic of padding needs a tiny bit more clarification - it’s almost there, but maybe a sentence that explicitly explains the permitted options in section 14 would be good, which could be cited rather than this scattered mention of “pad-out”
Perhaps these mentions of the word "padding" just need to reference section 19.1. "PADDING Frames" ?
Yes a REF is good: If that is all that is intended, we should say so.
Agreed, I think the right phrasing is PADDING Frames, though I don't think we need to reference 19.1 in every spot we talk about PADDING frames.
So could we grep for Padding and ensure we say /PADDING Frame/? - I guess the REF first time used would be good - if not already there?
How about something like: PR ?
Closing now that we have .
The commas for parentheticals got a little awkward there. Otherwise, this is a good change.I've done a bit of editorializing, and this LGTM. NAME should take a look.

The size of the first packet sent by a client will determine whether a server sends a Version Negotiation packet.  Clients that support  <ins> multiple QUIC versions SHOULD ensure that the first UDP datagram they send is sized to the largest of the minimum datagram sizes from all versions they support, using PADDING frames (frame-padding) as necessary.  This ensures that the server responds if there is a </ins> mutually supported version.  A server might not send a Version Negotiation packet if the datagram it receives is smaller than the minimum size specified in a different version; see initial-size. that contain packets that are all discarded. Clients MUST ensure that UDP datagrams containing Initial packets  <ins> have UDP payloads of at least 1200 bytes, adding PADDING frames as necessary.  A client that sends padded datagrams allows the server to send more data prior to completing address validation. </ins> Loss of an Initial or Handshake packet from the server can cause a deadlock if the client does not send additional Initial or Handshake The resulting minimum size of 21 bytes does not guarantee that a stateless reset is difficult to distinguish from other packets if the recipient requires the use of a connection ID.  To achieve that end,  <ins> the endpoint SHOULD ensure that all packets it sends are at least 22 bytes longer than the minimum connection ID length that it requests the peer to include in its packets, adding PADDING frames as necessary.  This ensures that any stateless reset sent by the peer is indistinguishable from a valid packet sent to the endpoint.  An endpoint that sends a stateless reset in response to a packet that is 43 bytes or shorter SHOULD send a stateless reset that is one byte shorter than the packet it responds to. </ins> These values assume that the Stateless Reset Token is the same length as the minimum expansion of the packet protection AEAD.  Additional


7.1. <del> If a path has been verified to support ECN (RFC3168, RFC8311), QUIC </del>  treats a Congestion Experienced (CE) codepoint in the IP header as a signal of congestion.  This document specifies an endpoint's response when its peer receives packets with the ECN-CE codepoint. shown with a '*'.  Once completed, endpoints are able to exchange application data. <del> An endpoint verifies support for Explicit Congestion Notification </del>  (ECN) by observing whether the ACK frames acknowledging the first packets it sends carry ECN counts, as described in ecn-validation. controller and RTT estimate, as described in migration-cc. The new path might not have the same ECN capability.  Therefore, the <del> endpoint verifies ECN capability as described in ecn. </del>  9.3.

URL verified. Consistency in terms helps.
mentions /verification/ of ECN but defines /validation/ of ECN.: In section 9.2: the term /verify/ is used 3 times, but not explained: /verifies ECN capability/ and /verifies ECN support/ should these both be /validates ECN support/ ? … ALSO is there a difference between ECN support and ECN capability? - I didn’t expect a subtle difference, can we use one set of words instead? (and sorry if I compounded any confusion in the terms...)

7.1.  <ins> If a path has been validated to support ECN (RFC3168, RFC8311), QUIC </ins> treats a Congestion Experienced (CE) codepoint in the IP header as a signal of congestion.  This document specifies an endpoint's response when its peer receives packets with the ECN-CE codepoint. shown with a '*'.  Once completed, endpoints are able to exchange application data.  <ins> An endpoint validates support for Explicit Congestion Notification </ins> (ECN) by observing whether the ACK frames acknowledging the first packets it sends carry ECN counts, as described in ecn-validation. controller and RTT estimate, as described in migration-cc. The new path might not have the same ECN capability.  Therefore, the  <ins> endpoint validates ECN capability as described in ecn. </ins> 9.3.


to transfer to a new network path. Frames are used in QUIC to communicate between endpoints.  One or <del> more frames are assembled into packets.  QUIC authenticates all </del>  packets and encrypts as much as is practical.  QUIC packets are carried in UDP datagrams (UDP) to better facilitate deployment in existing systems and networks. Path validation does not validate that a peer can send in the return direction.  Acknowledgments cannot be used for return path validation <del> as they contain insufficient entropy and might be spoofed.  Endpoints independently determine reachability on each direction of a path, and therefore return reachability can only be established by the peer. </del>  Path validation can be used at any time by either endpoint.  For instance, an endpoint might check that a peer is still in possession endpoint MAY send a stateless reset in response to receiving a packet that it cannot associate with an active connection. <del> A stateless reset is not appropriate for signaling error conditions. An endpoint that wishes to communicate a fatal connection error MUST use a CONNECTION_CLOSE frame if it has sufficient state to do so. </del>  To support this process, a token is sent by endpoints.  The token is carried in the Stateless Reset Token field of a NEW_CONNECTION_ID

Error? / A stateless reset is not appropriate for signaling error conditions./ I understand, but this sentence seems odd to me, is it possible to say /error conditions when there is connection state/ or something like that? Is it worth a cross ref to section 11 to be clear what a “normal” error is? Section 9 NiTs in wording: /Acknowledgments cannot be used for path validation as they contain insufficient entropy/ Acknowledgments cannot be used for path validation because they contain insufficient entropy/ /as endpoints independently determine reachability on each direction of a path./the endpoints independently determine reachability on each direction of a path./ Is it slightly easier to read: / One or more frames are assembled into packets./ / One or more frames are assembled into a QUIC packet./ In section 3.3. It says: /The receiver of a stream sends MAXSTREAMDATA (Section 19.10) and STOP_SENDING frames (Section 19.5)./ Is this a para where ACK frames should also be mentioned? It seemed odd to me that ACKs are not mentioned at some pointy around here.
I think that the intent is to be more general than just "this is not for connection errors". How about: I don't think that we want to talk about acknowledgements at the level of the stream section. They don't affect stream states and this is specifically about what stream-related frames can be sent.

to transfer to a new network path. Frames are used in QUIC to communicate between endpoints.  One or  <ins> more frames are assembled into a QUIC packet.  QUIC authenticates all </ins> packets and encrypts as much as is practical.  QUIC packets are carried in UDP datagrams (UDP) to better facilitate deployment in existing systems and networks. Path validation does not validate that a peer can send in the return direction.  Acknowledgments cannot be used for return path validation  <ins> because they contain insufficient entropy and might be spoofed. Endpoints independently determine reachability on each direction of a path, and therefore return reachability can only be established by the peer. </ins> Path validation can be used at any time by either endpoint.  For instance, an endpoint might check that a peer is still in possession endpoint MAY send a stateless reset in response to receiving a packet that it cannot associate with an active connection.  <ins> A stateless reset is not appropriate for indicating errors in active connections.  An endpoint that wishes to communicate a fatal connection error MUST use a CONNECTION_CLOSE frame if it is able. </ins> To support this process, a token is sent by endpoints.  The token is carried in the Stateless Reset Token field of a NEW_CONNECTION_ID


1.  In addition to providing secure, multiplexed transport, QUIC QUIC- <del> TRANSPORT includes the ability to negotiate a version.  This allows </del>  the protocol to change over time in response to new requirements. <del> Many characteristics of the protocol will change between versions. </del>  This document describes the subset of QUIC that is intended to remain stable as new versions are developed and deployed.  All of these assumptions that might be made based on knowledge of QUIC version 1; these do not apply to every version of QUIC. <del> 2. </del>  The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and This document uses terms and notational conventions from QUIC- TRANSPORT. <del> 3. QUIC is a connection-oriented protocol between two endpoints.  Those endpoints exchange UDP datagrams.  These UDP datagrams contain QUIC packets.  QUIC endpoints use QUIC packets to establish a QUIC connection, which is shared protocol state between those endpoints. </del> 4. Packet diagrams in this document use a format defined in QUIC-

Gorry thought that it might be worth saying what QUIC is first. I agree. Tiny tweaks to the intro to allow for the change. Including a new title.
Issue raised by NAME via :
I like it. I'll see what that looks like. I have read Version-Independent Properties of QUIC (-30) in the WGLC and think this is ready, I found 3 editorial comments that might need consideration after the WGLC: (i) I suggest the introduction for this spec starts abruptly. Is it worth considering one sentence top explain what QUIC is, in case someone read this document first? - One possible solution could be to move the text in section 3 to section 1? (To me, the section 3 text does not need to come after the Section 2 definitions.) (ii) Does this read better with /for/ moved within the sentence? OLD:/At the endpoint, the connection ID is used to identify which QUIC connection the packet is intended for./ NEW:/At the endpoint, the connection ID is used to identify the QUIC connection for which the packet is intended./ (iii) I see the desire to emphasise the “NOT”, but I query if this is correct usage of a RFC2119 keyword in Appendix A?: /The following statements are NOT guaranteed to be true for every QUIC version:/ Best wishes, Gorry

1. <ins> QUIC is a connection-oriented protocol between two endpoints.  Those endpoints exchange UDP datagrams.  These UDP datagrams contain QUIC packets.  QUIC endpoints use QUIC packets to establish a QUIC connection, which is shared protocol state between those endpoints. 2. </ins> In addition to providing secure, multiplexed transport, QUIC QUIC-  <ins> TRANSPORT allows for the option to negotiate a version.  This allows </ins> the protocol to change over time in response to new requirements.  <ins> Many characteristics of the protocol could change between versions. </ins> This document describes the subset of QUIC that is intended to remain stable as new versions are developed and deployed.  All of these assumptions that might be made based on knowledge of QUIC version 1; these do not apply to every version of QUIC.  <ins> 3. </ins> The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and This document uses terms and notational conventions from QUIC- TRANSPORT.  4. Packet diagrams in this document use a format defined in QUIC-


protect different content.  A server MAY abort the connection if it detects that the client reset the packet number. <del> A server acknowledges the use of a Retry packet for a connection using the retry_source_connection_id transport parameter; see transport-parameter-definitions.  If the server sends a Retry packet, it also subsequently includes the value of the Source Connection ID field from the Retry packet in its retry_source_connection_id transport parameter. If the client received and processed a Retry packet, it MUST validate that the retry_source_connection_id transport parameter is present and correct; otherwise, it MUST validate that the transport parameter is absent.  A client MUST treat a failed validation as a connection error of type PROTOCOL_VIOLATION. </del>  17.3.

Please review carefully, but I think we can replace these paragraphs with a reference.
Nice find, thanks NAME !

protect different content.  A server MAY abort the connection if it detects that the client reset the packet number.  <ins> The connection IDs used on Initial and Retry packets exchanged between client and server are copied to the transport parameters and validated as described in cid-auth. </ins> 17.3.