New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduces PATH_CHALLENGE and PATH_RESPONSE frames #1086
Changes from all commits
d14922c
9cc0cab
b96bd5d
5c3c0d6
4c470d8
5ce6149
fd34af6
d7a4c77
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -849,8 +849,9 @@ explained in more detail as they are referenced later in the document. | |
| 0x0a | STREAM_ID_BLOCKED | {{frame-stream-id-blocked}} | | ||
| 0x0b | NEW_CONNECTION_ID | {{frame-new-connection-id}} | | ||
| 0x0c | STOP_SENDING | {{frame-stop-sending}} | | ||
| 0x0d | PONG | {{frame-pong}} | | ||
| 0x0e | ACK | {{frame-ack}} | | ||
| 0x0d | ACK | {{frame-ack}} | | ||
| 0x0e | PATH_CHALLENGE | {{frame-path-challenge}} | | ||
| 0x0f | PATH_RESPONSE | {{frame-path-response}} | | ||
| 0x10 - 0x17 | STREAM | {{frame-stream}} | | ||
{: #frame-types title="Frame Types"} | ||
|
||
|
@@ -1562,25 +1563,34 @@ consider the possibility that packets are sent without congestion feedback. | |
|
||
Once a connection is established, address validation is relatively simple (see | ||
{{address-validation}} for the process that is used during the handshake). An | ||
endpoint validates a remote address by sending a PING frame containing a payload | ||
that is hard to guess. This frame MUST be sent in a packet that is sent to the | ||
new address. Once a PONG frame containing the same payload is received, the | ||
address is considered to be valid. The PONG frame can use any path on its | ||
return. A PING frame containing 12 randomly generated {{?RFC4086}} octets is | ||
sufficient to ensure that it is easier to receive the packet than it is to guess | ||
the value correctly. | ||
|
||
If the PING frame is determined to be lost, a new PING frame SHOULD be | ||
generated. This PING frame MUST include a new Data field that is similarly | ||
difficult to guess. | ||
endpoint validates a remote address by sending a PATH_CHALLENGE frame containing | ||
a payload that is hard to guess. This frame MUST be sent in a packet that is | ||
sent to the new address. Once a PATH_RESPONSE frame containing the same payload | ||
is received, the address is considered to be valid. | ||
|
||
The new address is not considered valid until a PATH_RESPONSE frame containing | ||
the same payload is received, even if the packet containing the PATH_CHALLENGE | ||
frame is acknowledged. | ||
|
||
The PATH_RESPONSE frame can use any path on its return. | ||
|
||
An endpoint MAY send multiple PATH_CHALLENGE frames to handle packet loss or to | ||
make additional measurements on a new network path. | ||
|
||
An endpoint MUST use fresh random data in every PATH_CHALLENGE frame so that it | ||
can associate the peer's response with the causative PATH_CHALLENGE. | ||
|
||
If the PATH_CHALLENGE frame is determined to be lost, a new PATH_CHALLENGE frame | ||
SHOULD be generated. This PATH_CHALLENGE frame MUST include new data that is | ||
similarly difficult to guess. | ||
|
||
If validation of the new remote address fails, after allowing enough time for | ||
possible loss and recovery of packets carrying PING and PONG frames, the | ||
endpoint MUST terminate the connection. When setting this timer, | ||
implementations are cautioned that the new path could have a longer round trip | ||
time than the original. The endpoint MUST NOT send a CONNECTION_CLOSE frame in | ||
this case; it has to assume that the remote peer does not want to receive any | ||
more packets. | ||
recovering from possible loss of packets carrying PATH_CHALLENGE and | ||
PATH_RESPONSE frames, the endpoint MUST terminate the connection. When setting | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why should the connection be terminated if the response wasn't received? What if the new path was just being interrogated for viability and it just wasn't viable (too much loss, blocked in one direction or got disconnected)? Should that really cause the good path to be killed as well? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is specific to address validation, I think. In practice, this is determined by what role you're playing (are you the one changing addresses or are you the one seeing the remote endpoint change). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's imagine a client is on path A and probes path B with a challenge. The server receives this challenge and then responds with the response and a challenge of its own. Then, for some reason, that packet never makes it (too much loss, blocked in that direction, or client got disconnected). The both sides won't receive their challenge's response. The current spec text indicates both sides would kill the connection on path A now. I am just questioning if that is the best course of action. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should probably be clarified in the text: I read this as only the server would be performing address validation for this example (since the client isn't seeing the server's address change) and therefore would be the only one required to kill the connection if the client cannot demonstrate that it owns the new address. In the scenario you mentioned, the client should be sending additional PATH_CHALLENGES when it doesn't get the response (and I think it's possible and probably a good idea for a server implementation to send a new one as well instead of waiting for the client to retransmit, if it wishes). I agree with your general point, however -- I think we should clarify that the server must not send additional data to the new client's address rather than closing the connection as a whole. Otherwise I can trivially get the server to close your connection just by failing address validation on your behalf. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the language should be around not considering that path viable. If that was your only path, that means you'll have to kill the connection, yes. If you think you still have the old path but you're wrong, you'll discover that via timeout soon enough. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The strategy that I'd like to suggest is to go back to the last best path. That said, this text is the same as it was previously with PING+Data and I'd like to not change migration behavior. I'd like to change this behavior but in a different PR that fixes migration. This PR simply replaces PING+Data / PONG with new frames. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, let's just keep this constrained to replacement as much as possible. |
||
this timer, implementations are cautioned that the new path could have a longer | ||
round trip time than the original. The endpoint MUST NOT send a | ||
CONNECTION_CLOSE frame in this case; it has to assume that the remote peer | ||
cannot want to receive any more packets. | ||
|
||
If the remote address is validated successfully, the endpoint MAY increase the | ||
rate that it sends on the new path using the state from the previous path. The | ||
|
@@ -1597,17 +1607,17 @@ After verifying an address, the endpoint SHOULD update any address validation | |
tokens ({{address-validation}}) that it has issued to its peer if those are no | ||
longer valid based on the changed address. | ||
|
||
Address validation using the PING frame MAY be used at any time by either peer. | ||
For instance, an endpoint might check that a peer is still in possession of its | ||
address after a period of quiescence. | ||
Address validation using the PATH_CHALLENGE frame MAY be used at any time by | ||
either peer. For instance, an endpoint might check that a peer is still in | ||
possession of its address after a period of quiescence. | ||
|
||
Upon seeing a connection migration, an endpoint that sees a new address MUST | ||
abandon any address validation it is performing with other addresses on the | ||
expectation that the validation is likely to fail. Abandoning address | ||
validation primarily means not closing the connection when a PONG frame is not | ||
received, but it could also mean ceasing retransmissions of the PING frame. An | ||
endpoint that doesn't retransmit a PING frame might receive a PONG frame, which | ||
it MUST ignore. | ||
validation primarily means not closing the connection when a PATH_RESPONSE frame | ||
is not received, but it could also mean ceasing subsequent transmissions of the | ||
PATH_CHALLENGE frame. An endpoint MUST ignore any subsequently received | ||
PATH_RESPONSE frames from that address. | ||
|
||
|
||
## Spurious Connection Migrations | ||
|
@@ -1619,10 +1629,10 @@ the legitimate copy will be dropped as a duplicate. | |
|
||
After a spurious migration, validation of the source address will fail because | ||
the entity at the source address does not have the necessary cryptographic keys | ||
to read or respond to the PING frame that is sent to it, even if it wanted to. | ||
Such a spurious connection migration could result in the connection being | ||
dropped when the source address validation fails. This grants an attacker the | ||
ability to terminate the connection. | ||
to read or respond to the PATH_CHALLENGE frame that is sent to it, even if it | ||
wanted to. Such a spurious connection migration could result in the connection | ||
being dropped when the source address validation fails. This grants an attacker | ||
the ability to terminate the connection. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about a recommendation to probe both old and new addresses to guard against this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think Jana's planning to improve this in a follow up, so I'm ok with it as is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup. Happy to take this comment in the follow-up. |
||
|
||
Receipt of packets with higher packet numbers from the legitimate address will | ||
trigger another connection migration. This will cause the validation of the | ||
|
@@ -1634,8 +1644,8 @@ the old remote address before attempting to validate the new address. If the | |
connection migration is spurious, then the legitimate address will be used to | ||
respond and the connection will migrate back to the old address. | ||
|
||
As with any address validation, packets containing retransmissions of the PING | ||
frame validating an address MUST be sent to the address being validated. | ||
As with any address validation, packets containing a PATH_CHALLENGE frame | ||
validating an address MUST be sent to the address being validated. | ||
Consequently, during a migration of a peer, an endpoint could be sending to | ||
multiple remote addresses. | ||
|
||
|
@@ -2149,43 +2159,20 @@ than it has sent, unless this is a result of a change in the initial limits (see | |
## PING Frame {#frame-ping} | ||
|
||
Endpoints can use PING frames (type=0x07) to verify that their peers are still | ||
alive or to check reachability to the peer. | ||
|
||
The PING frame contains a variable-length payload. | ||
|
||
~~~ | ||
0 1 2 3 | ||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| Length(8) | Data (*) ... | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
~~~ | ||
alive or to check reachability to the peer. The PING frame contains no | ||
additional fields. | ||
|
||
Length: | ||
The receiver of a PING frame simply needs to acknowledge the packet containing | ||
this frame. | ||
|
||
: This 8-bit value describes the length of the Data field. | ||
|
||
Data: | ||
|
||
: This variable-length field contains arbitrary data. | ||
|
||
A PING frame with an empty Data field causes the packet containing it to be | ||
acknowledged as normal. No other action is required of the recipient. | ||
|
||
An empty PING frame can be used to keep a connection alive when an application | ||
or application protocol wishes to prevent the connection from timing out. An | ||
The PING frame can be used to keep a connection alive when an application or | ||
application protocol wishes to prevent the connection from timing out. An | ||
application protocol SHOULD provide guidance about the conditions under which | ||
generating a PING is recommended. This guidance SHOULD indicate whether it is | ||
the client or the server that is expected to send the PING. Having both | ||
endpoints send PING frames without coordination can produce an excessive number | ||
of packets and poor performance. | ||
|
||
If the Data field is not empty, the recipient of this frame MUST generate a PONG | ||
frame ({{frame-pong}}) containing the same Data. A PING frame with data is not | ||
appropriate for use in keeping a connection alive, because the PONG frame | ||
elicits an acknowledgement, causing the sender of the original PING to send two | ||
packets. | ||
|
||
A connection will time out if no packets are sent or received for a period | ||
longer than the time specified in the idle_timeout transport parameter (see | ||
{{termination}}). However, state in middleboxes might time out earlier than | ||
|
@@ -2352,22 +2339,9 @@ Application Error Code: | |
{{app-error-codes}}). | ||
|
||
|
||
|
||
## PONG Frame {#frame-pong} | ||
|
||
The PONG frame (type=0x0d) is sent in response to a PING frame that contains | ||
data. Its format is identical to the PING frame ({{frame-ping}}). | ||
|
||
An endpoint that receives an unsolicited PONG frame - that is, a PONG frame | ||
containing a payload that is empty MUST generate a connection error of type | ||
FRAME_ERROR, indicating the PONG frame (that is, 0x10d). If the content of a | ||
PONG frame does not match the content of a PING frame previously sent by the | ||
endpoint, the endpoint MAY generate a connection error of type UNSOLICITED_PONG. | ||
|
||
|
||
## ACK Frame {#frame-ack} | ||
|
||
Receivers send ACK frames (type=0xe) to inform senders which packets they have | ||
Receivers send ACK frames (type=0x0d) to inform senders which packets they have | ||
received and processed. A sent packet that has never been acknowledged is | ||
missing. The ACK frame contains any number of ACK blocks. ACK blocks are | ||
ranges of acknowledged packets. | ||
|
@@ -2593,6 +2567,48 @@ by a client in protected packets, because it is certain that the server is able | |
to decipher the packet. | ||
|
||
|
||
## PATH_CHALLENGE Frame {#frame-path-challenge} | ||
|
||
Endpoints can use PATH_CHALLENGE frames (type=0x0e) to check reachability to the | ||
peer and for address validation during connection establishment and connection | ||
migration. | ||
|
||
PATH_CHALLENGE frames contain an 8-byte payload. | ||
|
||
~~~ | ||
0 1 2 3 | ||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| | | ||
+ Data (8) + | ||
| | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
|
||
~~~ | ||
|
||
Data: | ||
|
||
: This 8-byte field contains arbitrary data. | ||
|
||
A PATH_CHALLENGE frame containing 8 octets that are hard to guess is sufficient | ||
to ensure that it is easier to receive the packet than it is to guess the value | ||
correctly. | ||
|
||
The recipient of this frame MUST generate a PATH_RESPONSE frame | ||
({{frame-path-response}}) containing the same Data. | ||
|
||
|
||
## PATH_RESPONSE Frame {#frame-path-response} | ||
|
||
The PATH_RESPONSE frame (type=0x0f) is sent in response to a PATH_CHALLENGE | ||
frame. Its format is identical to the PATH_CHALLENGE frame | ||
({{frame-path-challenge}}). | ||
|
||
If the content of a PATH_RESPONSE frame does not match the content of a | ||
PATH_CHALLENGE frame previously sent by the endpoint, the endpoint MAY generate | ||
a connection error of type UNSOLICITED_PATH_RESPONSE. | ||
|
||
|
||
## STREAM Frames {#frame-stream} | ||
|
||
STREAM frames implicitly create a stream and carry stream data. The STREAM | ||
|
@@ -2692,12 +2708,11 @@ implementation decision, and an implementation should be careful to delay | |
conservatively, since any delay is likely to increase application-visible | ||
latency. | ||
|
||
Regular QUIC packets are "containers" of frames; a packet is never retransmitted | ||
whole. How an endpoint handles the loss of the frame depends on the type of the | ||
frame. Some frames are simply retransmitted, some have their contents moved to | ||
new frames, and others are never retransmitted. | ||
|
||
When a packet is detected as lost, the sender re-sends any frames as necessary: | ||
Regular QUIC packets are "containers" of frames. A packet is never | ||
retransmitted whole. How an endpoint handles the loss of a frame depends on the | ||
type of the frame. Some frames are simply retransmitted, some have their | ||
contents moved to new frames, and others are never retransmitted. When a packet | ||
is detected as lost, the sender re-sends any frames as necessary: | ||
|
||
* All application data sent in STREAM frames MUST be retransmitted, unless the | ||
endpoint has sent a RST_STREAM for that stream. When an endpoint sends a | ||
|
@@ -2724,6 +2739,10 @@ When a packet is detected as lost, the sender re-sends any frames as necessary: | |
increased since the frame was originally sent, the frame SHOULD NOT be | ||
retransmitted. | ||
|
||
* PATH_CHALLENGE frames MUST NOT be retransmitted, but a new PATH_CHALLENGE | ||
frame MAY be sent with new data. PATH_RESPONSE frames MUST NOT be | ||
retransmitted. | ||
|
||
* All other frames MUST be retransmitted. | ||
|
||
Upon detecting losses, a sender MUST take appropriate congestion control action. | ||
|
@@ -3620,10 +3639,10 @@ PROTOCOL_VIOLATION (0xA): | |
: An endpoint detected an error with protocol compliance that was not covered by | ||
more specific error codes. | ||
|
||
UNSOLICITED_PONG (0xB): | ||
UNSOLICITED_PATH_RESPONSE (0xB): | ||
|
||
: An endpoint received a PONG frame that did not correspond to any PING frame | ||
that it previously sent. | ||
: An endpoint received a PATH_RESPONSE frame that did not correspond to any | ||
PATH_CHALLENGE frame that it previously sent. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really need to error out if a response is received for a challenge we don't have saved at the moment? What if the sender of the challenge ended up timing out the packet too aggressively and threw away its state for it? If we really need to reliability validate this, how long would the sender of the challenge need to keep this state around then? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Defining the error code doesn't create a mandate to enforce and send it. Implementations that know they have accounting for every PATH_CHALLENGE they've ever sent (e.g. because they've never sent any) can enforce this, while implementations which have discarded state can let an unknown PATH_RESPONSE pass without comment. |
||
|
||
FRAME_ERROR (0x1XX): | ||
|
||
|
@@ -3830,7 +3849,7 @@ the range from 0xFE00 to 0xFFFF. | |
| 0x8 | TRANSPORT_PARAMETER_ERROR | Error in transport parameters | {{error-codes}} | | ||
| 0x9 | VERSION_NEGOTIATION_ERROR | Version negotiation failure | {{error-codes}} | | ||
| 0xA | PROTOCOL_VIOLATION | Generic protocol violation | {{error-codes}} | | ||
| 0xB | UNSOLICITED_PONG | Unsolicited PONG frame | {{error-codes}} | | ||
| 0xB | UNSOLICITED_PATH_RESPONSE | Unsolicited PATH_RESPONSE frame | {{error-codes}} | | ||
| 0x100-0x1FF | FRAME_ERROR | Specific frame format error | {{error-codes}} | | ||
{: #iana-error-table title="Initial QUIC Transport Error Codes Entries"} | ||
|
||
|
@@ -3868,6 +3887,11 @@ thanks to all. | |
|
||
Issue and pull request numbers are listed with a leading octothorp. | ||
|
||
## Since draft-ietf-quic-transport-09 | ||
|
||
- Added PATH_CHALLENGE and PATH_RESPONSE frames to replace PING with Data and | ||
PONG frame. Changed ACK frame type from 0x0e to 0x0d. (#000) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you open an issue for this, so we can track it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, good point. Filed #1091. |
||
|
||
## Since draft-ietf-quic-transport-08 | ||
|
||
- Clarified requirements for BLOCKED usage (#65, #924) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about "The new address is not validated until a PATH_RESPONSE frame containing the same payload is received, even if the packet containing the PATH_RESPONSE frame is acknowledged." (and then remove the text below I think is a bit confusing)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added. Which text is weird? The text about measuring time taken? It's caused me enough trouble so I'll remove it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't find my comment, but I thought it was weird to take an RTT sample of one path in one direction and a different path in another, which is what happens if PATH_RESPONSE comes back on a different path from the one PATH_CHALLENGE is sent on.