QUIC connections are established as described in QUIC-TRANSPORT. During connection establishment, HTTP/3 support is indicated by <del> selecting the ALPN token "hq" in the TLS handshake.  Support for </del>  other application-layer protocols MAY be offered in the same handshake.

Section 2.3, paragraph 2 still uses "hq"
Good spot, PR fixes this
Whoops!

QUIC connections are established as described in QUIC-TRANSPORT. During connection establishment, HTTP/3 support is indicated by  <ins> selecting the ALPN token "h3" in the TLS handshake.  Support for </ins> other application-layer protocols MAY be offered in the same handshake.


ALTSVC frame (ALTSVC), using the ALPN token defined in connection- establishment. <del> For example, an origin could indicate in an HTTP/1.1 or HTTP/2 response that HTTP/3 was available on UDP port 50781 at the same hostname by including the following header field in any response: </del>  On receipt of an Alt-Svc record indicating HTTP/3 support, a client MAY attempt to establish a QUIC connection to the indicated host and

HTTP response headers are common across HTTP/1.1, HTTP/2 and HTTP/3. There seems no need to be so precise on what version of the protocol returns the example header.

ALTSVC frame (ALTSVC), using the ALPN token defined in connection- establishment.  <ins> For example, an origin could indicate in an HTTP response that HTTP/3 was available on UDP port 50781 at the same hostname by including the following header field: </ins> On receipt of an Alt-Svc record indicating HTTP/3 support, a client MAY attempt to establish a QUIC connection to the indicated host and


10.4.1. <del> An endpoint detects a potential stateless reset when a packet with a short header either cannot be decrypted or is marked as a duplicate packet.  The endpoint then compares the last 16 bytes of the packet with the Stateless Reset Token provided by its peer, either in a </del>  NEW_CONNECTION_ID frame or the server's transport parameters.  If these values are identical, the endpoint MUST enter the draining period and not send any further packets on this connection.  If the

If connection for incoming packet cannot be found, check whether this is a stateless reset packet.
states: This only works for endpoints whose SCID is zero length. If SCID is not zero length, the connection corresponding to the stateless reset packet will not be found. This latter case should be included in the stateless reset detection algorithm.
OK with the requested changes.

10.4.1.  <ins> An endpoint detects a potential stateless reset when a incoming packet with a short header either cannot be associated with a connection, cannot be decrypted, or is marked as a duplicate packet. The endpoint then compares the last 16 bytes of the packet with the Stateless Reset Token provided by its peer, either in a </ins> NEW_CONNECTION_ID frame or the server's transport parameters.  If these values are identical, the endpoint MUST enter the draining period and not send any further packets on this connection.  If the


version downgrade prior to the completion of the handshake, though it means that a downgrade causes a handshake failure. <del> TLS uses Application Layer Protocol Negotiation (ALPN) RFC7301 to select an application protocol.  The application-layer protocol MAY restrict the QUIC versions that it can operate over.  Servers MUST select an application protocol compatible with the QUIC version that the client has selected. If the server cannot select a compatible combination of application </del>  protocol and QUIC version, it MUST abort the connection.  A client MUST abort a connection if the server picks an incompatible combination of QUIC version and ALPN identifier.

The transport already requires that the cryptographic handshake provide authenticated negotiation of application protocol. However, the TLS doc was a little weak on whether ALPN should be used. This clears that up.
Authenticated application protocol
One suggestion.
TCP-based protocols have historically had a lot of issues with protocol confusion, where one protocol could be interpreted as another and that lead to security issues; that lead to various ways of working that issue around, sometimes quite ugly (e.g. WebSockets). The root issue in TCP or regular UDP is that the application protocol is multiplexed by the port, but the port is never a certain indicator of the protocol. ALPN, on other hand, is exactly that. Since there are no b/c concerns in our case, can we make ALPN mandatory in QUIC? This could save us a lot of security issues down the road.
Is this not already the case? In transport section 7 is says:
This doesn't require the endpoints to actually use the ALPN, does it?
A little earlier section 7 says: If the TLS handshake fails to provide ALPN, or some other TLS-compatible negotiation method (of which I know none), I'd interpret that as failing the required condition of "MUST provide authenticated negotiation".
NAME I would be concerned of the side-effects that we might have. RFC 7301 (ALPN) requires Expert Review for registering new application protocols. My concern is that mandating the "use" of ALPN might forbid QUIC being used in in-house protocols. NAME My interpretation would be the same as NAME that the intent of the text is to state that the handshake protocol must protect the protocol negotiation when it is negotiated. PS. OTOH I do agree that H3 MUST require the use of ALPN, as H2 does.
NAME I don't see read any optionality, so perhaps this is the ambiguity in the spec. (FWIW optionality of client authentication is explicitly highlighted in that section). I also share your concerns about mandating ALPN. There still seem to be a few open questions about de-multiplexing different UDP-based protocols when a server listens on the same port - how does ALPN for QUIC mitigate that? We also have WIP like DATAGRAM frame that are proposing use of TP for enabling the transport-layer feature that could be used by an application to talk arbitrary protocols. It seems like ALPN is too rigid a model to support multiple application layers within a single QUIC connection.
I'm OK with the proposed requirement. Using ALPN protects you from others. Not using ALPN might make you ulnerable to weird cross-protocol attacks. However, I take NAME and NAME points collectively as "if ALPN2 is better, mandating ALPN specifically might be bad". I'm OK with loosening the straps on registrations a little (maybe just for <N character names) there if that's the only real concern. Would people be OK with "explicit protocol negotiation, such as ALPN"? Like NAME I don't believe that the text explicitly mandates that the protocol negotiation facility be used, so I'd like to add that.
Sgtm
I don't know enough about ALPN, but if QUIC doesn't handle inhouse protocols well without bureaucracy, ALPN will get abused. What about future? decentralised approaches e.g. via DNS records?
NAME The general requirement SG, though I assume you'd still have to spell out the TLS specifics in the TLS mapping document. NAME I do not expect the Expert Review requirement to be a particularly strong barrier since from what I understand the bar is "the designated experts do not consider you obviously bogus". Either way I think we can discuss within TLS working group about relaxing some of those.
I think requiring some form of explicit protocol negotiation would be good. The TLS draft could potentially be a bit more specific about what that looks like, too, but I agree that we don't want to be too restrictive here. In other words, what NAME said above sounds good to me.
NAME SGTM. NAME I think that the concern will be resolved if we adopt NAME proposal. You would not be required to send ALPN for example when name resolution with combined protocol negotiation is being used.
The transport doc currently says: So I think that is fine. I've opened to make the ALPN requirement clearer. As NAME says, if you have other means of agreeing on protocol, then you can avoid ALPN. For instance, a QUIC version that could only be used with one application protocol would be one way. In the end, you have to be sure that both client and server agree on what protocol they are talking. I can see how binding to name resolution would work; though I can also see how it might not work (multiple subjectAltNames, failing to communicate SNI properly, coalescing, etc...).

version downgrade prior to the completion of the handshake, though it means that a downgrade causes a handshake failure.  <ins> QUIC requires that the cryptographic handshake provide authenticated protocol negotiation.  TLS uses Application Layer Protocol Negotiation (ALPN) RFC7301 to select an application protocol.  Unless another mechanism is used for agreeing on an application protocol, endpoints MUST use ALPN for this purpose. An application-layer protocol MAY restrict the QUIC versions that it can operate over.  Servers MUST select an application protocol compatible with the QUIC version that the client has selected.  If the server cannot select a compatible combination of application </ins> protocol and QUIC version, it MUST abort the connection.  A client MUST abort a connection if the server picks an incompatible combination of QUIC version and ALPN identifier.


transmitted on a stream.  For a stream that is reset, the final offset is carried explicitly in a RESET_STREAM frame.  Otherwise, the final offset is the offset of the end of the data carried in a STREAM <del> frame marked with a FIN flag, or 0 in the case of incoming unidirectional streams. </del>  An endpoint will know the final offset for a stream when the receiving part of the stream enters the "Size Known" or "Reset Recvd"

I think the text is a leftover from time before we introduced STOP_SENDING.
There was an earlier draft where RST could be used for both locally and peer initiated streams. Not sure if that is relevant here.

transmitted on a stream.  For a stream that is reset, the final offset is carried explicitly in a RESET_STREAM frame.  Otherwise, the final offset is the offset of the end of the data carried in a STREAM  <ins> frame marked with a FIN flag. </ins> An endpoint will know the final offset for a stream when the receiving part of the stream enters the "Size Known" or "Reset Recvd"


This parameter is an integer value specifying the initial flow control limit for unidirectional streams.  This limit applies to <del> newly created bidirectional streams opened by the endpoint that </del>  receives the transport parameter.  In client transport parameters, this applies to streams with an identifier with the least significant two bits set to 0x3; in server transport parameters,

Quoted from draft 17, value specifying the initial flow control limit for unidirectional streams. This limit applies to newly created bidirectional streams It should be - This limit applies to newly created unidirectional...

This parameter is an integer value specifying the initial flow control limit for unidirectional streams.  This limit applies to  <ins> newly created unidirectional streams opened by the endpoint that </ins> receives the transport parameter.  In client transport parameters, this applies to streams with an identifier with the least significant two bits set to 0x3; in server transport parameters,


A server conveys a preferred address by including the preferred_address transport parameter in the TLS handshake. <del> Once the handshake is finished, the client SHOULD initiate path validation (see migrate-validate) of the server's preferred address using the connection ID provided in the preferred_address transport parameter. </del>  If path validation succeeds, the client SHOULD immediately begin sending all future packets to the new server address using the new NOT send more than a minimum congestion window's worth of non-probing packets to the new address before path validation is complete.  10. Connections should remain open until they become idle for a pre- The server's preferred address is used to effect a change in server address at the end of the handshake, as described in preferred-address.  The format of this transport parameter is the <del> PreferredAddress struct shown in fig-preffered-address.  This transport parameter is only sent by a server. </del>  If present, transport parameters that set initial flow control limits (initial_max_stream_data_bidi_local,

This change allows servers to send a preferred address of each address family. The previous definition of preferred_address only allowed sending one address family, and this had the disadvantage of picking the non-deterministic address family used by the Initial if the client is performing Happy Eyeballs. Clients may now use the best address family based on their network attachment.
NAME I went with this approach to minimize changes from the current design. Having a list that allows different ports and future versions of IP are fine by me, but there seemed to be more pushback when that was proposed in . As long as we have a way of communicating one IPv4 address and one IPv6 address I'm happy.
We could allow multiple PreferredAddress TPs and avoid the zero-length address problem.
NAME Thank you for the clarification. I think I'm fine with that. NAME The PreferredAddress structure includes Connection ID and Stateless Reset Token. I think we should avoid requiring the server to send them twice when it is constrained by the 3-packet rule.
I've simplified the wire-format using NAME suggestion with separate port numbers
NAME I think migration and preferred address are separate parts of the protocol. The goal of this PR is to improve preferred address. It allows the client to pick the address family it transfers to right after the handshake. If the server only provides you with a preferred address family you don't have access to, you can simply ignore it - and that's not new from this PR. I've slightly tweaked the text to clarify how clients respond to preferred_address. Does this address your concerns?
I'm good with this PR. NAME -- can you see if your concern is addressed?
NAME is there anything left for me to do so this can get merged in?
You could wait until I had time to check it, that's all.
After reading my text, I am not entirely happy with it, but I lack a better suggestion.Still LG

A server conveys a preferred address by including the preferred_address transport parameter in the TLS handshake.  <ins> Servers MAY communicate a preferred address of each address family (IPv4 and IPv6) to allow clients to pick the one most suited to their network attachment. Once the handshake is finished, the client SHOULD select one of the two server's preferred addresses and initiate path validation (see migrate-validate) of that address using the connection ID provided in the preferred_address transport parameter. </ins> If path validation succeeds, the client SHOULD immediately begin sending all future packets to the new server address using the new NOT send more than a minimum congestion window's worth of non-probing packets to the new address before path validation is complete. <ins> A client that migrates to a new address SHOULD use a preferred address from the same address family for the server. </ins> 10. Connections should remain open until they become idle for a pre- The server's preferred address is used to effect a change in server address at the end of the handshake, as described in preferred-address.  The format of this transport parameter is the  <ins> PreferredAddress struct shown in fig-preferred-address.  This transport parameter is only sent by a server.  Servers MAY choose to only send a preferred address of one address family by sending an all-zero address and port (0.0.0.0:0 or ::.0) for the other family. </ins> If present, transport parameters that set initial flow control limits (initial_max_stream_data_bidi_local,


the receiver SHOULD repeatedly acknowledge newly received packets in preference to packets received in the past.  13.1.2. ACK frames MUST only be carried in a packet that has the same packet

This is stacked on top of . So it looks worse than it really is. Review .
As discussed in URL, I'd like to advocate for introducing a new error code for loss recovery-related errors, e.g. when an unsent packet (in a respective packet number space) is acknowledged. In general, we have transport error codes to describe the area where an error occurred. For example, we have a STREAMSTATEERROR, which is used when the peer sends frames for the wrong stream type or unopened stream types. And we have a FLOWCONTROLERROR, for, obviously, flow-control related errors. Generating ACKs for multiple packet number spaces is not a trivial thing to do, and it would be of great help for debugging to have a more specific error here than the catch-all PROTOCOLVIOLATION.
I think this is best served in the associated error message string. In production this is a real attack, and less information to the attacker is a good thing.
Then why do we have different error codes stream states, flow control etc.? There are real attacks here as well, and I don't see why they would be less severe than loss recovery-related attacks. At least we should be consistent. If we really say that all errors just give an attacker more information, let's kill all error codes except NOERROR and SERVERBUSY.
(I sense sarcasm) MQTT tried to do that, but people were unhappy so they added more error codes, e.g. when incorrectly connecting multiple clients with the same client ID which was an easy mistake.
More seriously, an attacker could use the error code to find a way to avoid hitting gaps in optimistic acks with high probability. Granted, the attacker could probably also use PROTOCOL_VIOLATION to infer that, once it is confident it has a working implementation.
I'd like to add an error code for this. In general, I don't think we have a consistent view on error codes and error strings at the moment. We removed a lot of error codes around the time of the Paris interim, many of which could not be sent on the wire and so really needed to be removed. But we haven't added many(if any?) back and this seems like a good one to add. But if we think the error code is providing too much information to the peer, then error details is almost certainly even more, unless it's always empty, in which case what's the point?
The error details can be flagged out in production and used for debugging. Perhaps there should be some guidelines on that.
Actually, optimistic ACK's could also be defended against by ignoring the invalid ACK's and silently downgrade the connection to min CWND. That would provide even less feedback to an attacker. But of course an error is needed for debugging. Not sure I would recommend this - but it is an option.
NAME I like the way you frame the issue. It is my understanding that we have so far assigned error codes for each feature rather than every one of the specific errors. Calling it "loss recovery-related errors" certainly follows that practice. The question is if there is a group of errors that fall into the category. Do you have something other than ACK containing an invalid packet number in mind?
I can think of three different cases why an ACK would contain an invalid packet number: the obvious case: you ack a too high packet number, which wasn't sent yet you ack in the wrong packet number space. This will most likely look like you acked a packet that the peer didn't send (in that packet number space) I assume that most implementations will sooner or later implement skipping of packet numbers as a mitigation of the optimistic ack attack. This error would occur when you ack a packet number that was intentionally skipped.
NAME Thank you for elaborating. To me it sounds that they would look like one symptom to the reciever; i.e. an invalid PN in ACK. I was wondering if there is a intent to use the error code for other symptoms, because it would imply that there are group of errors that might be categorized as a group (based on my understanding that we assign error codes to each feature or category). Though I am not strongly opposed to having an error code for this case, I fear if having an error code for this opens can of worms (i.e. "we have error code for this, so why not have another error code for X"). Therefore am trying to see what the line we would be trying to hold is.
I must have been dumb, but I now realize that the proposed error code would be equivalent of a connection error of (errorcode=PROTOCOLVIOLATION, frame_type=ACK)? Am I correct? Assuming that is correct, I wonder if we achieve anything by having a new error code.
NAME you make a good point. I think it'd be preferable to remove the frame type and have more error codes, but I think we'll get to have that larger discussion in Tokyo.
This error condition is a symptom of the "optimistic ack" attack. If I am under attack, I don't want to give more info to the attacker. Generic connection close is fine.
In Tokyo: close with no action.
I can't find text about this case anywhere. A peer that receives an acknowledgement for a packet that it didn't send should be required to close the connection. I'm not sure which error code to use for that though, none of those currently defined really matches this case, so you'd have to use INTERNAL_ERROR. We might want to consider introducing a more specific error code.
I think you are expected to use PROTOCOLVIOLATION in such case. Quoting from editors' draft: An endpoint detected an error with protocol compliance that was not covered by more specific error codes._ FWIW, I think that the requirement should be no stronger than "SHOULD if detected", to avoid endpoints memoizing the gaps for the previous 231 range. I assume that endpoints would typically just drop packets carrying a PN that is deemed too old.
This is always considered to be an Optimistic ACK attack. The only response is to treat the peer as hostile. URL
NAME If an endpoint cannot prove it didn't send the packet, it should just ignore the ACK for that particular packet. The congestion controller should not be sensitive to packets that old, so the Optimistic ACK attack is harmless in this case. Dropping the entire packet containing an ACK with an old packet is likely too strong - ignoring the ACK for the packet is sufficient. If a peer continually sends ACK's for very old packets, that might go under weird behaviour similar to sending too many gaps, and an implementation could decide the peer is being too confused and close the connection with an error.
FYI: the reason this always needs to be treated as hostile (when provable) is because it is the only defence against the potentially very harmful optimistic ack attack. There are very few other other reasons to have gaps in the range, such as when having multiple concurrent transmit queues.

the receiver SHOULD repeatedly acknowledge newly received packets in preference to packets received in the past. <ins> An endpoint SHOULD treat receipt of an acknowledgment for a packet it did not send as a connection error of type PROTOCOL_VIOLATION, if it is able to detect the condition. </ins> 13.1.2. ACK frames MUST only be carried in a packet that has the same packet


4.5. In order to be usable for 0-RTT, TLS MUST provide a NewSessionTicket <del> message that contains the "max_early_data" extension with the value 0xffffffff; the amount of data which the client can send in 0-RTT is controlled by the "initial_max_data" transport parameter supplied by the server.  A client MUST treat receipt of a NewSessionTicket that contains a "max_early_data" extension with any other value as a connection error of type PROTOCOL_VIOLATION. </del>  Early data within the TLS connection MUST NOT be used.  As it is for other TLS application data, a server MUST treat receiving early data

What exists is "earlydata" extension and its "maxearlydatasize" field.

4.5. In order to be usable for 0-RTT, TLS MUST provide a NewSessionTicket  <ins> message that contains the "early_data" extension with a max_early_data_size of 0xffffffff; the amount of data which the client can send in 0-RTT is controlled by the "initial_max_data" transport parameter supplied by the server.  A client MUST treat receipt of a NewSessionTicket that contains an "early_data" extension with any other value as a connection error of type PROTOCOL_VIOLATION. </ins> Early data within the TLS connection MUST NOT be used.  As it is for other TLS application data, a server MUST treat receiving early data


identifies the frame.  These frames are explained in more detail in frame-formats. <del> All QUIC frames are idempotent.  That is, a valid frame does not cause undesirable side effects or errors when received more than once. </del>  The Frame Type field uses a variable length integer encoding (see integer-encoding) with one exception.  To ensure simple and efficient

Is this just a statement about this spec or a normative requirement on future specs?
It is not an invariant.
There's more to requirements on future specs than invariants.
It's a statement about the current version, but it would presumptively apply to extensions to the current version that don't redefine the base spec.
Perhaps it'd be better to use to avoid confusion like this.

identifies the frame.  These frames are explained in more detail in frame-formats.  <ins> All QUIC frames are idempotent in this version of QUIC.  That is, a valid frame does not cause undesirable side effects or errors when received more than once. </ins> The Frame Type field uses a variable length integer encoding (see integer-encoding) with one exception.  To ensure simple and efficient


13.1.1. <del> To avoid creating an indefinite feedback loop, an endpoint MUST NOT send an ACK frame in response to a packet containing only ACK or PADDING frames, even if there are packet gaps which precede the received packet.  The endpoint MUST however acknowledge packets containing only ACK or PADDING frames when sending ACK frames in response to other packets. </del>  Packets containing PADDING frames are considered to be in flight for congestion control purposes QUIC-RECOVERY.  Sending only PADDING that other frames are sent in addition to PADDING frames to elicit acknowledgments from the receiver. <del> An endpoint MUST NOT send more than one packet containing only an ACK frame per received packet that contains frames other than ACK and PADDING frames. </del> The receiver's delayed acknowledgment timer SHOULD NOT exceed the current RTT estimate or the value it indicates in the "max_ack_delay" transport parameter.  This ensures an acknowledgment is sent at least Strategies and implications of the frequency of generating acknowledgments are discussed in more detail in QUIC-RECOVERY. <del> To limit the ranges of acknowledged packet numbers to those that have not yet been received by the sender, the receiver SHOULD track which ACK frames have been acknowledged by its peer.  The receiver SHOULD exclude already acknowledged packets from future ACK frames whenever these packets would unnecessarily contribute to the ACK frame size. </del>  Because ACK frames are not sent in response to ACK-only packets, a receiver that is only sending ACK frames will only receive packets preceding the Largest Acknowledged that are being acknowledged.  The First ACK Range is encoded as an ACK Range (see ack-ranges) starting from the Largest Acknowledged.  That is, the <del> smallest packet number included in the range is determined by </del>  subtracting the First ACK Range value from the Largest Acknowledged. <del> Contains additional ranges of packet numbers which are alternately not acknowledged (Gap) and acknowledged (ACK Range), see ack- ranges. </del>  The three ECN Counts, see ack-ecn-counts. packet number for the range, the smallest value is determined by the formula: <del> An ACK Range acknowledges all packet numbers between the smallest packet number and the largest, inclusive. </del>  The largest value for an ACK Range is determined by cumulatively subtracting the size of all preceding ACK Ranges and Gaps.

Packets are acknowledged, though packet numbers are communicated to acknowledge them.
One high-level comment. I believe the intent was to move most of the text about sending ACKs from recovery to transport. Also, if we defined ack-eliciting in transport, I think this PR and some other text would read more clearly. I can do that in a follow up PR.
Thanks for cleaning this up. One more TODO down.

13.1.1.  <ins> An endpoint MUST NOT send more than one packet containing only an ACK frame per received packet that contains frames other than ACK and PADDING frames.  An endpoint MUST NOT send a packet containing only an ACK frame in response to a packet containing only ACK or PADDING frames, even if there are packet gaps which precede the received packet.  This prevents an indefinite feedback loop of ACKs.  The endpoint MUST however acknowledge packets containing only ACK or PADDING frames when sending ACK frames in response to other packets. </ins> Packets containing PADDING frames are considered to be in flight for congestion control purposes QUIC-RECOVERY.  Sending only PADDING that other frames are sent in addition to PADDING frames to elicit acknowledgments from the receiver.  The receiver's delayed acknowledgment timer SHOULD NOT exceed the current RTT estimate or the value it indicates in the "max_ack_delay" transport parameter.  This ensures an acknowledgment is sent at least Strategies and implications of the frequency of generating acknowledgments are discussed in more detail in QUIC-RECOVERY.  <ins> To limit ACK Ranges (see ack-ranges) to those that have not yet been received by the sender, the receiver SHOULD track which ACK frames have been acknowledged by its peer.  The receiver SHOULD exclude already acknowledged packets from future ACK frames whenever these packets would unnecessarily contribute to the ACK frame size. </ins> Because ACK frames are not sent in response to ACK-only packets, a receiver that is only sending ACK frames will only receive packets preceding the Largest Acknowledged that are being acknowledged.  The First ACK Range is encoded as an ACK Range (see ack-ranges) starting from the Largest Acknowledged.  That is, the  <ins> smallest packet acknowledged in the range is determined by </ins> subtracting the First ACK Range value from the Largest Acknowledged.  <ins> Contains additional ranges of packets which are alternately not acknowledged (Gap) and acknowledged (ACK Range), see ack-ranges. </ins> The three ECN Counts, see ack-ecn-counts. packet number for the range, the smallest value is determined by the formula:  <ins> An ACK Range acknowledges all packets between the smallest packet number and the largest, inclusive. </ins> The largest value for an ACK Range is determined by cumulatively subtracting the size of all preceding ACK Ranges and Gaps.


which is not yet in the tree.  Such elements are added to the tree with the requested priority. <del> When a prioritized element is first created it has a default inital </del>  weight of 16 and a default dependency.  Requests and placeholders are <del> dependent on the root of the priority tree, pushes are dependent on </del>  the client request on which the PUSH_PROMISE frame was sent. Requests may override the default intial values by including a PRIORTIY frame (see frame-priority) at the beginning of the stream. <del> Pushes and placeholders may be later updated by sending a PRIORITY frame on the control stream. </del>  5.3.1.

Maybe takes things too far, please discuss.
Draft 17 says If I understand H3 Placeholders correctly, the PRIORITY frame would be used to set their priority too. Perhaps we can say: .
To me it seems that the current text is more accurate. While a PRIORITY frame might use a placeholder to designate the target, what is actually prioritized is the stream that carries the response. The transmission of the placeholder itself is never prioritized.
But the PRIORITY frame contains the Prioritized Element ID field, that allows us to set the priority of Placeholder type.
Yeah, my view is that it is an act of prioritizing a stream by specifying a placeholder ID that maps to the stream. Section 5.3 talks about prioritizing "streams".
That's a good observation, so I'd go further to say that IMO the textual description of H3 prioritisation is not completely aligned with the protocol mechanisms. Cue NAME to tell me I am completely off the mark :)
Also, the table of prioritized element types in seems to be duplicated
The two tables in section 4.2.3 are slightly different.
Correct, I just independently spotted that. Its logical but a bit annoying
makes some basic edits to section 4.2.3, adding Table captions and linking to them from the paragraph above. It reworks 5.3 slightly to reframe the language toward describing a tree of abstract elements but still provides concrete examples of request prioritisation.
Yeah, there was originally one table and I had to split it to accommodate the current design. If anyone sees an elegant way to recombine them, I'm fully supportive.
I thought perhaps make the labels multi line and then combine the last two columns of the second table onto the first. So there would be two colums containing two sub columns each, plus the left most bits column. I don't know the Markdown foo to make that happen though.
NAME My bad. I was confused. I now understand the issue and think that the PR will provide us the necessary fixes.
A comment on this text: we should be prioritizing requests and maybe pushes, not streams.
That's a fair point I'll see if I can incorporate that into the PR
Yes, I like this formulation better.

which is not yet in the tree.  Such elements are added to the tree with the requested priority.  <ins> When a prioritized element is first created, it has a default initial </ins> weight of 16 and a default dependency.  Requests and placeholders are  <ins> dependent on the root of the priority tree; pushes are dependent on </ins> the client request on which the PUSH_PROMISE frame was sent. Requests may override the default intial values by including a PRIORTIY frame (see frame-priority) at the beginning of the stream.  <ins> These priorities can be updated by sending a PRIORITY frame on the control stream. </ins> 5.3.1.


4.2.6. The PUSH_PROMISE frame (type=0x05) is used to carry a promised <del> request header set from server to client, as in HTTP/2. </del>  The payload consists of: the client has advertised or a Push ID which has already been promised as a connection error of type HTTP_MALFORMED_FRAME.  See server-push for a description of the overall server push mechanism.

All other frame types in section 4 explicitly call out which stream types are allowed. For example DATA: If a DATA frame is received on either control stream, the recipient MUST respond with a connection error (Section 8) of type HTTPWRONGSTREAM.
FWIW this is explained in section 5. But I agree that conistency in the frame descriptions could help.

4.2.6. The PUSH_PROMISE frame (type=0x05) is used to carry a promised  <ins> request header set from server to client on a request stream, as in HTTP/2. </ins> The payload consists of: the client has advertised or a Push ID which has already been promised as a connection error of type HTTP_MALFORMED_FRAME. <ins> If a PUSH_PROMISE frame is received on either control stream, the recipient MUST respond with a connection error (errors) of type HTTP_WRONG_STREAM. </ins> See server-push for a description of the overall server push mechanism.


than the server's advertised idle timeout, and SHOULD do so if approaching the idle timeout. <del> HTTP clients are expected to use QUIC PING frames to keep connections open while there are responses outstanding for requests or server pushes.  If the client is not expecting a response from the server, allowing an idle connection to time out is preferred over expending effort maintaining a connection that might not be needed.  A gateway MAY use PING to maintain connections in anticipation of need rather than incur the latency cost of connection establishment to servers. Servers SHOULD NOT use PING frames to keep a connection open. </del>  6.2.

The transport Draft states: This guidance was added to the HTTP Draft in , but was subsequently removed.
I guess that something to discuss in the applicability draft as well
Looks like made this text more generic, leaving the specifics of how to keep a connection alive (e.g. with PING frames) to the transport implementation. The text in question is now in Section 5.1: It's possible the transport draft needs a similar generalization, to recommend implementations expose a way for the application layer to control whether an connection is idle.
Actually there is a section on "Session resumption versus Keep-alive" in the applicability draft already. Do we need to say more? Is that needed in the transport draft as well?
In , the direction of the discussion is that PING frames are an application-layer concept that won't necessarily be directly under the application-layer's control. The HTTP draft currently says: This should be reworded in terms of requesting the transport stack to keep the connection from idling out, rather than directly discussing PING frames.
Or maybe we just need to kinds of PING?
Gah! Not more types of PING please!
KEEPTHECARRUNNING frame (ID=0xAF)

than the server's advertised idle timeout, and SHOULD do so if approaching the idle timeout.  <ins> HTTP clients are expected to request that the transport keep connections open while there are responses outstanding for requests or server pushes, as described in Section 19.2 of QUIC-TRANSPORT.  If the client is not expecting a response from the server, allowing an idle connection to time out is preferred over expending effort maintaining a connection that might not be needed.  A gateway MAY maintain connections in anticipation of need rather than incur the latency cost of connection establishment to servers.  Servers SHOULD NOT actively keep connections open. </ins> 6.2.


5.1.2. <del> Either client or server can cancel requests by aborting the stream (QUIC RESET_STREAM and/or STOP_SENDING frames, as appropriate) with an error code of HTTP_REQUEST_CANCELLED (http-error-codes).  When the client cancels a response, it indicates that this response is no longer of interest.  Implementations SHOULD cancel requests by aborting both directions of a stream. When the server aborts its response stream using HTTP_REQUEST_CANCELLED, it indicates that no application processing was performed.  In this context, "processed" means that some data from the stream was passed to some higher layer of software that </del>  might have taken some action as a result.  The client can treat <del> requests cancelled by the server as though they had never been sent at all, thereby allowing them to be retried later on a new connection.  Servers MUST NOT use the HTTP_REQUEST_CANCELLED status for requests which were partially or fully processed. </del>  If a stream is cancelled after receiving a complete response, the client MAY ignore the cancellation and use the response.  However, if frame (frame-goaway).  The GOAWAY frame indicates that client- initiated requests on lower stream IDs were or might be processed in this connection, while requests on the indicated stream ID and <del> greater were not accepted.  This enables client and server to agree on which requests were accepted prior to the connection shutdown. This identifier MAY be lower than the stream limit identified by a QUIC MAX_STREAM_ID frame, and MAY be zero if no requests were processed.  Servers SHOULD NOT increase the QUIC MAX_STREAM_ID limit after sending a GOAWAY frame. Once GOAWAY is sent, the server MUST cancel requests sent on streams with an identifier higher than the indicated last Stream ID.  Clients MUST NOT send new requests on the connection after receiving GOAWAY, although requests might already be in transit.  A new connection can be established for new requests. If the client has sent requests on streams with a higher Stream ID than indicated in the GOAWAY frame, those requests are considered cancelled (request-cancellation).  Clients SHOULD reset any streams above this ID with the error code HTTP_REQUEST_CANCELLED.  Servers MAY also cancel requests on streams below the indicated ID if these requests were not processed. </del>  Requests on Stream IDs less than the Stream ID in the GOAWAY frame might have been processed; their status cannot be known until they Servers SHOULD send a GOAWAY frame when the closing of a connection is known in advance, even if the advance notice is small, so that the <del> remote peer can know whether a stream has been partially processed or not.  For example, if an HTTP client sends a POST at the same time </del>  that a server closes a QUIC connection, the client cannot know if the server started to process that POST request if the server does not send a GOAWAY frame to indicate what streams it might have acted on. A frame was received which was not permitted in the current state.  Peer violated protocol requirements in a way which doesn't match a more specific error code, or endpoint declines to use the more specific error code.

This change sounds good to me, but to make sure I understand: is the only purpose of this change to split HTTPREQUESTCANCELLED in two; client to server stays HTTPREQUESTCANCELLED and server to client now becomes HTTPREQUESTREJECTED? And is the goal to add clarity when parsing connection logs later?
NAME basically. In the previous text, CANCELLED had specific semantics when sent server->client. However, with the change to make RESETSTREAM reflect back the error code in STOPSENDING, the client->server STOPSENDING(CANCELLED) would provoke a RESETSTREAM(CANCELLED) in the server->client direction with that error code that didn't necessarily match those semantics. Hence the split -- one code has specific semantics when sent by a server, and clients are instructed to use the other code. (I would prohibit clients from sending it, but if the server sends a STOP_SENDING, the client would reflect it back and violate the prohibition, so....)

5.1.2.  <ins> Clients can cancel requests by aborting the stream (QUIC RESET_STREAM and/or STOP_SENDING frames, as appropriate) with an error code of HTTP_REQUEST_CANCELLED (http-error-codes).  When the client cancels a response, it indicates that this response is no longer of interest. Implementations SHOULD cancel requests by aborting both directions of a stream. When the server rejects a request without performing any application processing, it SHOULD abort its response stream with the error code HTTP_REQUEST_REJECTED.  In this context, "processed" means that some data from the stream was passed to some higher layer of software that </ins> might have taken some action as a result.  The client can treat  <ins> requests rejected by the server as though they had never been sent at all, thereby allowing them to be retried later on a new connection. Servers MUST NOT use the HTTP_REQUEST_REJECTED error code for requests which were partially or fully processed.  When a client sends a STOP_SENDING with HTTP_REQUEST_CANCELLED, a server MAY indicate the error code HTTP_REQUEST_REJECTED in the corresponding RESET_STREAM if no processing was performed.  Clients MUST NOT reset streams with the HTTP_REQUEST_REJECTED error code except in response to a QUIC STOP_SENDING frame. </ins> If a stream is cancelled after receiving a complete response, the client MAY ignore the cancellation and use the response.  However, if frame (frame-goaway).  The GOAWAY frame indicates that client- initiated requests on lower stream IDs were or might be processed in this connection, while requests on the indicated stream ID and  <ins> greater were rejected.  This enables client and server to agree on which requests were accepted prior to the connection shutdown.  This identifier MAY be lower than the stream limit identified by a QUIC MAX_STREAM_ID frame, and MAY be zero if no requests were processed. Servers SHOULD NOT increase the QUIC MAX_STREAM_ID limit after sending a GOAWAY frame. Once GOAWAY is sent, the server MUST reject requests sent on streams with an identifier greater than or equal to the indicated last Stream ID.  Clients MUST NOT send new requests on the connection after receiving GOAWAY, although requests might already be in transit.  A new connection can be established for new requests. If the client has sent requests on streams with a Stream ID greater than or equal to that indicated in the GOAWAY frame, those requests are considered rejected (request-cancellation).  Clients SHOULD cancel any requests on streams above this ID.  Servers MAY also reject requests on streams below the indicated ID if these requests were not processed. </ins> Requests on Stream IDs less than the Stream ID in the GOAWAY frame might have been processed; their status cannot be known until they Servers SHOULD send a GOAWAY frame when the closing of a connection is known in advance, even if the advance notice is small, so that the  <ins> remote peer can know whether a request has been partially processed or not.  For example, if an HTTP client sends a POST at the same time </ins> that a server closes a QUIC connection, the client cannot know if the server started to process that POST request if the server does not send a GOAWAY frame to indicate what streams it might have acted on. A frame was received which was not permitted in the current state. <ins> A server rejected a request without performing any application processing. </ins> Peer violated protocol requirements in a way which doesn't match a more specific error code, or endpoint declines to use the more specific error code.


When a PRIORITY frame claims to reference a request, the associated ID MUST identify a client-initiated bidirectional stream.  A server <del> MUST treat receipt of PRIORITY frame with a Stream ID of any other type as a connection error of type HTTP_MALFORMED_FRAME. </del>  <del> A PRIORITY frame that references a non-existent Push ID or a Placeholder ID greater than the server's limit MUST be treated as an HTTP_MALFORMED_FRAME error. </del>  A PRIORITY frame received on any stream other than a request or control stream MUST be treated as a connection error of type server can promise.  A server SHOULD use Push IDs sequentially, starting at 0.  A client MUST treat receipt of a push stream with a Push ID that is greater than the maximum Push ID as a connection <del> error of type HTTP_PUSH_LIMIT_EXCEEDED. </del>  The header of the request message is carried by a PUSH_PROMISE frame (see frame-push-promise) on the request stream which generated the A frame was received on a stream where it is not permitted. <del> A Push ID greater than the current maximum Push ID was referenced. </del>  A Push ID was referenced in two different stream headers.

Generalizes HTTPPUSHLIMITEXCEEDED to HTTPLIMIT_EXCEEDED and mandates use of that error code where a PRIORITY frame attempts to prioritize a stream the client isn't permitted to open yet. This has the effect of bounding priority tree state to requests the client could initiate (though might not have yet).
RFC 7540 at some length, especially what to do with priority information after a stream is closed. In addition to this, in HTTP/3, we have to deal with priority information before a stream is opened. That is: just how large is the tree allowed to grow before the referenced streams' frames begin to come in? The draft should provide some recommendations. I can think of two ways to do limit this "front-loaded" state: Open the stream when a PRIORITY frame referencing it arrives on the control stream. This will invoke the stream limit checks. Limit number of priority entries to number -- perhaps a function of the total number of maximum unidirectional and bidirectional streams.
Just clarifying: this would be advice to implementations, nothing normative, right?
I don't know. I think I will be happy as long as something is put into the draft.
Assuming that PRIORITY can only reference a stream that already "exists" client-side, the maximum number would be the number of permitted client-initiated bidirectional streams. Perhaps the requirements should simply be that PRIORITY cannot reference a stream that the client isn't permitted to open? I don't really want to "actually open" the stream when the PRIORITY frame arrives, but checking that it's less than the MAXSTREAMID on receipt seems like a reasonable server-side check.

When a PRIORITY frame claims to reference a request, the associated ID MUST identify a client-initiated bidirectional stream.  A server  <ins> MUST treat receipt of a PRIORITY frame identifying a stream of any other type as a connection error of type HTTP_MALFORMED_FRAME. </ins>  <ins> A PRIORITY frame that references a non-existent Push ID, a Placeholder ID greater than the server's limit, or a Stream ID the client is not yet permitted to open MUST be treated as an HTTP_LIMIT_EXCEEDED error. </ins> A PRIORITY frame received on any stream other than a request or control stream MUST be treated as a connection error of type server can promise.  A server SHOULD use Push IDs sequentially, starting at 0.  A client MUST treat receipt of a push stream with a Push ID that is greater than the maximum Push ID as a connection  <ins> error of type HTTP_LIMIT_EXCEEDED. </ins> The header of the request message is carried by a PUSH_PROMISE frame (see frame-push-promise) on the request stream which generated the A frame was received on a stream where it is not permitted.  <ins> A Stream ID, Push ID, or Placeholder ID greater than the current maximum for that identifier was referenced. </ins> A Push ID was referenced in two different stream headers.


6.2.1.2. <del> As described in Section 17.5.1 of QUIC-TRANSPORT, endpoints stop sending and receiving Initial packets once they start exchanging Handshake packets.  At this point, all loss recovery state for the Initial packet number space is also discarded.  Packets that are in flight for the packet number space are not declared as either acknowledged or lost.  After discarding state, new Initial packets will not be sent. </del>  6.2.2. 7.5.  Probe packets MUST NOT be blocked by the congestion controller.  A sender MUST however count these packets as being additionally in flight, since these packets adds network load without establishing is functionally similar to a sender's response on a Retransmission Timeout (RTO) in TCP RFC5681. <del> 7.6. </del>  This document does not specify a pacer, but it is RECOMMENDED that a sender pace sending of all in-flight packets based on input from the of a flow pacer, implementers are referred to the Fair Queue packet scheduler (fq qdisc) in Linux (3.11 onwards). <del> 7.7. </del>  A sender becomes idle if it ceases to send data and has no bytes in flight.  A sender's congestion window MUST NOT increase while it is A sender MAY implement alternate mechanisms to update its congestion window after idle periods, such as those proposed for TCP in RFC7661. <del> 7.8. When keys for a packet number space are discarded, any in-flight packets sent with those keys are removed from the count of bytes in flight.  Loss recovery state is also discarded, so no loss events will occur for any in-flight packets from that space (see discard- initial).  Note that it is expected that keys are discarded after those packets would be declared lost, but Initial secrets are destroyed earlier. When 0-RTT is rejected, all in-flight 0-RTT packets are removed from the count of bytes in flight.  Loss recovery state is also discarded, so no loss events will occur for any in-flight 0-RTT packets. If a server accepts 0-RTT, but does not buffer 0-RTT packets that arrive before Initial packets, early 0-RTT packets will be declared lost, but that is expected to be infrequent. </del> 7.9. 7.9.1.

Ignore the loss of Handshake, 0-RTT, and 1-RTT packets if the peer doesn't have the keys to process them. Also unifies the text on discarding keys and no longer implies that 0-RTT has it's own packet number space. Replaces
merging this one as well to get this in -18.
A few comments, editorial.

6.2.1.2.  <ins> When packet protection keys are discarded (see Section 4.9 of QUIC- TLS), all packets that were sent with those keys can no longer be acknowledged because their acknowledgements cannot be processed anymore.  The sender considers them no longer in flight.  That is, the sender SHOULD discard all recovery state associated with those packets and MUST remove them from the count of bytes in flight. Endpoints stop sending and receiving Initial packets once they start exchanging Handshake packets (see Section 17.2.2.1 of QUIC- TRANSPORT).  At this point, recovery state for all in-flight Initial packets is discarded. When 0-RTT is rejected, recovery state for all in-flight 0-RTT packets is discarded. If a server accepts 0-RTT, but does not buffer 0-RTT packets that arrive before Initial packets, early 0-RTT packets will be declared lost, but that is expected to be infrequent. It is expected that keys are discarded after packets encrypted with them would be acknowledged or declared lost.  Initial secrets however might be destroyed sooner, as soon as handshake keys are available (see Section 4.10 of QUIC-TLS). </ins> 6.2.2. 7.5. <ins> During the handshake, some packet protection keys might not be available when a packet arrives.  In particular, Handshake and 0-RTT packets cannot be processed until the Initial packets arrive, and 1-RTT packets cannot be processed until the handshake completes. Endpoints MAY ignore the loss of Handshake, 0-RTT, and 1-RTT packets that might arrive before the peer has packet protection keys to process those packets. 7.6. </ins> Probe packets MUST NOT be blocked by the congestion controller.  A sender MUST however count these packets as being additionally in flight, since these packets adds network load without establishing is functionally similar to a sender's response on a Retransmission Timeout (RTO) in TCP RFC5681.  <ins> 7.7. </ins> This document does not specify a pacer, but it is RECOMMENDED that a sender pace sending of all in-flight packets based on input from the of a flow pacer, implementers are referred to the Fair Queue packet scheduler (fq qdisc) in Linux (3.11 onwards).  <ins> 7.8. </ins> A sender becomes idle if it ceases to send data and has no bytes in flight.  A sender's congestion window MUST NOT increase while it is A sender MAY implement alternate mechanisms to update its congestion window after idle periods, such as those proposed for TCP in RFC7661.  7.9. 7.9.1.


result is smaller than the min_rtt, the RTT should be used, but the ack delay field should be ignored. <del> Like TCP, QUIC calculates both smoothed RTT and RTT variance similar to those specified in RFC6298. </del>  min_rtt is the minimum RTT measured over the connection, prior to adjusting by ack delay.  Ignoring ack delay for min RTT prevents

Looks good to me, always nice to pull that note out of the reference and make sure people see it.
merging, so we can get this into -18
Based on the recovery draft, a largest newly acked packet should be considered as sample for the RTT estimation. This means that if the receiver sends 1 ACK for every packet, then every single ACK will be taken as sample for RTT estimation. But RFC6298 suggests to take at least one RTT measurement per RTT and also states, "Additionally, when multiple samples are taken per RTT, the alpha and beta defined in Section 2 may keep an inadequate RTT history." I think taking every single largest ACK into consideration doesn't comply with RFC6298.
The next sentence is: "A method for changing these constants is currently an open research question." Unless this research question has been answered, ideally in an RFC, I think we need to keep the text as is.
For context, the entire paragraph is: It is true that counting each packet causes us to retire history faster. Where this matters is when the sender might RTO or TLP too soon, since too late is usually not a problem. This might happen still, but our deployment experience so far has not shown this to cause any undue issues. Specifically, a QUIC sender detects and handles spurious timeouts better than TCP senders did when RFC 2988 (the precursor to RFC 6298) was written. It may be possible to tweak these constants to make them better for the "correct" timescale, but as NAME (and RFC 6298) points out, that is very much open research issue. The numbers in 6298 come from 2988 which was based on measurement studies from 1999 (the end-to-end path properties in the refs section of 6298).
I am worried if the new RTT samples could lower the TLP/RTO values significantly by over-writing the history faster especially if the receiver is sending an ACK for every packet. Tweaking the constants would require extensive experimentation. I am thinking maybe we can limit number of RTT samples per RTT to a finite number.
The easiest thing to implement is to use an RTT sample per packet. In the absence of newer information, it's hard to know what the right answer is. I'm open to any data you (or anyone else) might have that recommends better multipliers.
NAME can you take a look at NAME proposed resolution here? I know that it's not great, because it ties the depth of the history to the ACK rate, but we don't have a concrete alternative solution.
The note looks fine to me. Once we have some experience with RTT samples per ACK and how it impacts the history, we can make an informed decision.

result is smaller than the min_rtt, the RTT should be used, but the ack delay field should be ignored.  <ins> A sender calculates both smoothed RTT and RTT variance similar to those specified in RFC6298, see on-ack-received. A sender takes an RTT sample when an ACK frame is received that acknowledges a larger packet number than before (see on-ack- received).  A sender will take multiple RTT samples per RTT when multiple such ACK frames are received within an RTT.  When multiple samples are generated within an RTT, the smoothed RTT and RTT variance could retain inadequate history, as suggested in RFC6298. Changing these computations is currently an open research question. </ins> min_rtt is the minimum RTT measured over the connection, prior to adjusting by ack delay.  Ignoring ack delay for min RTT prevents


with a Retry packet (see validate-retry) or in a previous connection using the NEW_TOKEN frame (see validate-future).  8.1.1. Upon receiving the client's Initial packet, the server can request

Clarifies how we prohibit a server from sending too many packets to a client prior to validating the address.
NAME That's definitely true. Addressed in f7b0187.
I'm not sure if this is correct. If a packet is declared lost, it can be retransmitted, so in that case the amount of data sent is larger than initial cwnd.
NAME That's a good point. Does changing the text to refer to "congestion avoidance algorithm" instead of "initial congestion window" resolve the concern?
This looks fine, but I have a new concern: this makes it appear as though the congestion window doesn't apply to the server. It does. Would this be better?
NAME Thanks. Applied in 5522bd5.
Thanks everyone. This looks nothing like what it did when it started, but it's much clearer as a result.
To avoid it being used as a DDoS attack vector, we forbid the server from sending more than three datagrams before the path is validated during the handshake. Do we need similar safeguard for the number of datagrams that a client may emit before it sees a packet from the server? IMO, it might be worth considering, due to the following reasons: The attack vector exists. For example, an attacker can inject a DNS response for a huge website to a DNS server. Then, clients would start sending 0-RTT packets to the victim. The number of packets that the client can send prior to path being validated was one for TCP. The QUIC transport draft is silent about the limit (though INITCWND would practically become in play here).
We limit amount of bytes now, not number of packets, no? If we limit the client, it would need to be a rate limit, otherwise it would never be able to connect again later when network conditions improve. I'm not sure it makes sense to have a MUST limit on client, as there can be many deployments types with different attack vectors. DNS might not be in the equation. However, obviously a client should not saturate a link on connection retries.
That's a good point. This is not a reflection attack (unlike the server's packets) and therefore it makes more sense to define a limit using number of bytes. IMO it can be a fixed number of bytes that can be transmitted in one RTO. For example, "a client SHOULD NOT send more than 3 * MTU bytes per every retransmission timeout, until it receives a packet from a server."
My thinking was that the initial CWND was a sufficient limit.
NAME If that's the consensus, I am happy to accept it (I do not have a strong opinion here).
That's what I remember us arriving at before. But I'm happy to have it explicitly stated if it's not.
NAME I don't see anyone objecting to that approach, is there some text you'd like added to one of the drafts?
I was thinking: The client creates a valid connection. Then initiates a migration to a new path that it does not own for the sole purpose of DoS'ing some other poor individual. While there is a setup cost, the migration can be repeated for the same or different targets until the peer decides it is enough and closes. How is the initial CWND sufficient protection against that?
NAME Malicious clients can always send an unlimited number of packets anywhere. QUIC doesn't change that.
NAME I mean that the client causes the peer to respond to a fake migration attempt and coerces the peer into sending packets elsewhere. I'm not sure if that can amplify, e.g. via PMTU probe.
I think we had a similar discussion twice, getting some text, and later removed. But I don't recall anymore.
There's a section on address validation which should prevent reflection attacks: URL This issue is about what the client can send.
NAME thanks, that covers it.
NAME Filed . I do not have a strong opinion on whether this needs to be clarified, therefore I would not oppose if the PR gets refused.
Thanks for the PR. I'm not against clarification, I'm just not sure what to write and I'd like to close this before Tokyo if we can. Taking a look at the PR now.
I think that you have this backwards. The point here is that the client is limited by INITCWND. The server has to abide by the x3 rule.

with a Retry packet (see validate-retry) or in a previous connection using the NEW_TOKEN frame (see validate-future). <ins> In addition to sending limits imposed prior to address validation, servers are also constrained in what they can send by the limits set by the congestion controller.  Clients are only constrained by the congestion controller. </ins> 8.1.1. Upon receiving the client's Initial packet, the server can request


A server might receive a packet addressed to its preferred IP address at any time after it accepts a connection.  If this packet contains a PATH_CHALLENGE frame, the server sends a PATH_RESPONSE frame as per <del> migrate-validate.  The server MAY send other non-probing frames from its preferred address, but MUST continue sending all probing packets from its original IP address. </del>  <del> The server SHOULD also initiate path validation of the client using its preferred address and the address from which it received the client probe.  This helps to guard against spurious migration </del>  initiated by an attacker. Once the server has completed its path validation and has received a

Editorial because it now says what we originally thought it said, but design because it is changing normative language. Does not interact with .
The following text is kind of hard to track: I'm not really following what's going on here. The server knows that it has two addresses and it wants to use its preferred address. Why does it have to probe from its preferred address. Also, I don't understand how these two paragraphs work together. The first one says you have to send probing packets from your original address but the second paragraph tells you to probe from the preferrd address.
You're right, that last restriction in the first paragraph looks backwards. IIRC, it's supposed to be a reiteration of the fact that the client decides when to actually cut over, so the server shouldn't be sending non-probing packets from the preferred address until the client starts sending them there. This is section 9.6.2 of Transport.
I think that the first paragraph is just missing "... until it receives a non-probing packet from the client at its preferred address." Then maybe a simple: "The server MUST probe on the path toward its preferred address before sending non-probing packets."

A server might receive a packet addressed to its preferred IP address at any time after it accepts a connection.  If this packet contains a PATH_CHALLENGE frame, the server sends a PATH_RESPONSE frame as per  <ins> migrate-validate.  The server MUST send other non-probing frames from its original address until it receives a non-probing packet from the client at its preferred address and until the server has validated the new path. </ins>  <ins> The server MUST probe on the path toward the client from its preferred address.  This helps to guard against spurious migration </ins> initiated by an attacker. Once the server has completed its path validation and has received a


packet. A client MUST ignore a Version Negotiation packet that lists the <del> client's chosen version. </del>  A client MAY attempt 0-RTT after receiving a Version Negotiation packet.  A client that sends additional 0-RTT packets MUST NOT reset

Cherry-picking a fix to this issue that got lost in one of the defunct VN-rework PRs.
I suggest adding "A client MAY choose to wait a while to see if a supported Version Negotiation packet arrives before failing the connection. This protects against injection attacks."
It looks like this: Client supports A and B only. Server supports C and D only. Client sends initial in A (or B), server sends Version Negotiation in response. Client does what exactly? Might as well say. We could get into the client maybe ignoring that because it's an attack, but we decided not to worry too much about that, so probably not worth saying.
It's not (generally) an attack and it is a very possible because no-one supports the insecure A or B anymore (like TLS 1.0, 1.1). Client API returns the equivalent of 501 Not Implemented. It could of course be an injected packet so the client could wait for something better to show up before failing. No action required.
" The client MUST choose the version that it most prefers from those supported by the server" -> or abort the connection attempt with an error indicating that no supported version was available. A client MAY discard such a Version Negotion packet and wait a short while in order to mitigate injection attacks.
Yeah, all I'm looking for here is the "If the client does not support any of the versions the server offers, it aborts the connection attempt." bit you pointed at.
I was about to do a PR, but the text is in your PR
Yep, I dumped it on top of that pile.
NAME trying to mitigate this injection DoS attack is much harder than merely "waiting a bit before failing". If your implementation waits a bit in case of no common version in VN packet, what would it do, if an attacker injects a VN packet with a version you do support (which arrives before a reply from the server that actually supported your initial version)? What if the attacker races to the server a packet with your 4-tuple, CIDs, and version but different contents, establishing a connection on the server for you, which you would not be able to decrypt? Etc. I'd rather defer mitigations, if any are possible, to a later version of this draft (or a different draft).
NAME There are definitely many more scenarios better handled in v2 or which are not practical to defend against at all without out of band knowledge such as signatures of some sort. As to a VN with initial version, the draft already says: However "waiting a little" will give a valid packet a fighting chance against subversive attackers that wish to remain undetected.
That's right. I meant the attack claiming another version you support, not your original version. Given that browsers are likely to be supporting predictable version sets, picking a different one is not likely to be a challenge. The other problem with just waiting is that the client is likely better off just failing over to TCP quickly to improve user experience.
I won't necessarily argue against failover to TCP, but my working assumption is that QUIC is QUIC, even if the most likely implemention is QUIC/HTTP/HQ/H3 with support for TCP. Hence QUIC needs to be able to handle itself the best it can. Specific use cases may recommend fallbacks to TCP, but it won't work in general.
Right, though HTTP with TCP failover is a very important use case. But even without TCP, there are other options, such as failover to another IP returned by DNS. Of course, if you have exhausted all options, waiting may be the best alternative left. I would delegate guidance on this to a draft akin to Happy Eyeballs for QUIC (Applicability draft?).

packet. A client MUST ignore a Version Negotiation packet that lists the  <ins> client's chosen version.  If the client does not support any of the versions the server offers, it aborts the connection attempt. </ins> A client MAY attempt 0-RTT after receiving a Version Negotiation packet.  A client that sends additional 0-RTT packets MUST NOT reset


An endpoint uses a STOP_SENDING frame (type=0x05) to communicate that incoming data is being discarded on receipt at application request. <del> This signals a peer to abruptly terminate transmission on a stream. Receipt of a STOP_SENDING frame is invalid for a locally-initiated stream that has not yet been created or is in the "Ready" state (see stream-send-states).  Receiving a STOP_SENDING frame for a locally- initiated stream that is "Ready" or not yet created MUST be treated as a connection error of type STREAM_STATE_ERROR.  An endpoint that receives a STOP_SENDING frame for a receive-only stream MUST terminate the connection with error STREAM_STATE_ERROR. </del>  The STOP_SENDING frame is as follows: inform a peer of the maximum amount of data that can be sent on a stream. <del> An endpoint that receives a MAX_STREAM_DATA frame for a receive-only </del>  stream MUST terminate the connection with error STREAM_STATE_ERROR. <del> An endpoint that receives a MAX_STREAM_DATA frame for a send-only stream it has not opened MUST terminate the connection with error STREAM_STATE_ERROR. Note that an endpoint may legally receive a MAX_STREAM_DATA frame on a bidirectional stream it has not opened. </del> The MAX_STREAM_DATA frame is as follows: MAX_STREAM_DATA frames contain the following fields:

URL the case where those streams are implicitly created. There's probably another state here if we were being entirely honest with ourselves to distinguish between a stream that has been created and one that has had frames sent for it, but this is already more complex than it needs to be.
NAME does this sound normal to you?
To clarify, I +1'd your suggestion. More clearly: "Looks good, but I would like you to accept Dimitri's suggestion before submitting this PR."
Ah, alright. I thought it was the other way around.
This LGTM. I was just about to file a similar issue when I saw this.
MAXSTREAMDATA should not be able to be transmitted on uni-directional until frames have been received on the same or a higher numbered stream of same type. Or until the stream is created for bidi streams. If this is not the case, a peer can force the initiator to track unbounded state and frames or risk frames being received when an endpoint is not ready to handle them. See discussion here URL
As I said in that discussion, I don't think there's an unresolved issue here. :
Sorry if that is the case. My impression was that you used it as an example to where we did not have such protection, and didn't care. I'll leave it open for now since the term "has not opened" is also a bit vague. Exactly what state must the sender be in for this to count? And what must the peer observe to conclude this?
This is a non-issue, with the clarifications in the PRs incoming. That is, you can only receive MAXSTREAMDATA for a stream that you have decided to open. At that point you have decided to track considerably more state than a flow control limit. We can leave this until or similar lands.
Thanks
LG minus Dimitri's suggestion

An endpoint uses a STOP_SENDING frame (type=0x05) to communicate that incoming data is being discarded on receipt at application request.  <ins> STOP_SENDING requests that a peer cease transmission on a stream. A STOP_SENDING frame can be sent for streams in the Recv or Size Known states (see stream-send-states).  Receiving a STOP_SENDING frame for a locally-initiated stream that has not yet been created MUST be treated as a connection error of type STREAM_STATE_ERROR.  An endpoint that receives a STOP_SENDING frame for a receive-only stream MUST terminate the connection with error STREAM_STATE_ERROR. </ins> The STOP_SENDING frame is as follows: inform a peer of the maximum amount of data that can be sent on a stream.  <ins> A MAX_STREAM_DATA frame can be sent for streams in the Recv state (see stream-send-states).  Receiving a MAX_STREAM_DATA frame for a locally-initiated stream that has not yet been created MUST be treated as a connection error of type STREAM_STATE_ERROR.  An endpoint that receives a MAX_STREAM_DATA frame for a receive-only </ins> stream MUST terminate the connection with error STREAM_STATE_ERROR.  The MAX_STREAM_DATA frame is as follows: MAX_STREAM_DATA frames contain the following fields: