Skip to content
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

Merged
merged 8 commits into from Feb 1, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
194 changes: 109 additions & 85 deletions draft-ietf-quic-transport.md
Expand Up @@ -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"}

Expand Down Expand Up @@ -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
Copy link
Contributor

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)

Copy link
Contributor Author

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.

Copy link
Contributor

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.

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
Copy link
Member

Choose a reason for hiding this comment

The 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?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is specific to address validation, I think.
So, if you're just probing new paths for viability then you're not doing this and it's totally fine to consider those packets lost and move on.
If you're doing address validation and the remote side cannot prove that it actually owns that address, we need to not send it anything more.

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).

Copy link
Member

@nibanks nibanks Jan 31, 2018

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

Copy link
Member

Choose a reason for hiding this comment

The 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
Expand All @@ -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
Expand All @@ -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.
Copy link
Contributor

Choose a reason for hiding this comment

The 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?

Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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
Expand All @@ -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.

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Copy link
Member

@nibanks nibanks Jan 31, 2018

Choose a reason for hiding this comment

The 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?

Copy link
Contributor

Choose a reason for hiding this comment

The 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):

Expand Down Expand Up @@ -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"}

Expand Down Expand Up @@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you open an issue for this, so we can track it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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)
Expand Down