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

CID Lifetime Management #1496

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 95 additions & 22 deletions draft-ietf-quic-transport.md
Original file line number Diff line number Diff line change
Expand Up @@ -1014,28 +1014,30 @@ For all other frames, the Frame Type field simply identifies the frame. These
frames are explained in more detail as they are referenced later in the
document.

| Type Value | Frame Type Name | Definition |
|:------------|:------------------|:----------------------------|
| 0x00 | PADDING | {{frame-padding}} |
| 0x01 | RST_STREAM | {{frame-rst-stream}} |
| 0x02 | CONNECTION_CLOSE | {{frame-connection-close}} |
| 0x03 | APPLICATION_CLOSE | {{frame-application-close}} |
| 0x04 | MAX_DATA | {{frame-max-data}} |
| 0x05 | MAX_STREAM_DATA | {{frame-max-stream-data}} |
| 0x06 | MAX_STREAM_ID | {{frame-max-stream-id}} |
| 0x07 | PING | {{frame-ping}} |
| 0x08 | BLOCKED | {{frame-blocked}} |
| 0x09 | STREAM_BLOCKED | {{frame-stream-blocked}} |
| 0x0a | STREAM_ID_BLOCKED | {{frame-stream-id-blocked}} |
| 0x0b | NEW_CONNECTION_ID | {{frame-new-connection-id}} |
| 0x0c | STOP_SENDING | {{frame-stop-sending}} |
| 0x0d | ACK | {{frame-ack}} |
| 0x0e | PATH_CHALLENGE | {{frame-path-challenge}} |
| 0x0f | PATH_RESPONSE | {{frame-path-response}} |
| 0x10 - 0x17 | STREAM | {{frame-stream}} |
| 0x18 | CRYPTO | {{frame-crypto}} |
| 0x19 | NEW_TOKEN | {{frame-new-token}} |
| 0x20 | ACK_ECN | {{frame-ack-ecn}} |
| Type Value | Frame Type Name | Definition |
| :---------- | :-------------------- | :-------------------------- |
| 0x00 | PADDING | {{frame-padding}} |
| 0x01 | RST_STREAM | {{frame-rst-stream}} |
| 0x02 | CONNECTION_CLOSE | {{frame-connection-close}} |
| 0x03 | APPLICATION_CLOSE | {{frame-application-close}} |
| 0x04 | MAX_DATA | {{frame-max-data}} |
| 0x05 | MAX_STREAM_DATA | {{frame-max-stream-data}} |
| 0x06 | MAX_STREAM_ID | {{frame-max-stream-id}} |
| 0x07 | PING | {{frame-ping}} |
| 0x08 | BLOCKED | {{frame-blocked}} |
| 0x09 | STREAM_BLOCKED | {{frame-stream-blocked}} |
| 0x0a | STREAM_ID_BLOCKED | {{frame-stream-id-blocked}} |
| 0x0b | NEW_CONNECTION_ID | {{frame-new-connection-id}} |
| 0x0c | STOP_SENDING | {{frame-stop-sending}} |
| 0x0d | ACK | {{frame-ack}} |
| 0x0e | PATH_CHALLENGE | {{frame-path-challenge}} |
| 0x0f | PATH_RESPONSE | {{frame-path-response}} |
| 0x10 - 0x17 | STREAM | {{frame-stream}} |
| 0x18 | CRYPTO | {{frame-crypto}} |
| 0x19 | NEW_TOKEN | {{frame-new-token}} |
| 0x20 | ACK_ECN | {{frame-ack-ecn}} |
| 0x21 | RETIRE_CONNECTION_ID | {{frame-retire-cid}} |
| 0x22 | REQUEST_CONNECTION_ID | {{frame-request-cid}} |
{: #frame-types title="Frame Types"}

All QUIC frames are idempotent. That is, a valid frame does not cause
Expand Down Expand Up @@ -3399,6 +3401,77 @@ blocks all those streams from making progress. An implementation is therefore
advised to bundle as few streams as necessary in outgoing packets without losing
transmission efficiency to underfilled packets.

## RETIRE_CONNECTION_ID Frame {#frame-retire-cid}

The RETIRE_CONNECTION_ID frame (type=0x21) informs the peer that older
connection IDs are being discarded by the peer and will no longer be recognized.

The frame is as follows:

~~~
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~~~

The fields in the RETIRE_CONNECTION_ID frame are as follows:

Sequence:
: Sequence number of the oldest connection ID issued by the sender which the
sender will still recognize as associated with the current connection. The
sequence number is formatted as a variable-length integer.

Upon receipt of a RETIRE_CONNECTION_ID frame, an endpoint MUST consider
connection IDs older than the specified sequence number as unusable and MUST NOT
consider the associated stateless reset tokens to be valid. If an older
Copy link
Member

Choose a reason for hiding this comment

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

I don't think older CIDs should be considered unusable. I can imagine a use for this to retire a CID that you used to try to probe a path that didn't work. In that case, you might still be using the older CID for your main path, and you just want to throw away the new one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Perhaps, but then you'd need to individually retire old CIDs one at a time. I'm envisioning this as more of a sliding window -- an implementation is moving the end of the window. Is there a reason you wouldn't just roll forward to another CID when you send this frame, in that situation?

Copy link
Member

Choose a reason for hiding this comment

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

We should be dealing is so few numbers of simultaneous CIDs that it should be just fine to be explicit. I think a sliding window puts to much of a restriction that you are constantly moving the primary path to these new CIDs. I must prefer being explicit, per CID.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm inclined to think that if you're dealing in small numbers of CIDs, it's fine to just remember all of them. If you reach the point where you need to abandon old CIDs because you have too many outstanding, then we're already in a scenario where you're using a lot of them.

Copy link
Member

Choose a reason for hiding this comment

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

While, we haven't implemented migration yet in WinQuic, the following is the design we'd have been considering:

  • Have a hard coded limit on the number of CIDs to give to the peer, X (probably 3 or 4)
  • Have a hard coded limit on the number of valid paths to maintain, Y (probably 2 or 3)
  • Whenever we need to give a new CID out, if we are at our limit, we throw away the oldest, non-primary path, CID in our cache. If that CID was for a validated path, throw the path away.
  • Trickier logic is around new paths being created. Not quite worked through the details on that one.

But bottom line, with this design, we don't expect to age out more than one at a time. And additionally, the peer could indicate they have to throw a CID away because they tried and failed to use it for a new path validation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm surprised at throwing the path away. I'd think there are separate criteria to throw away a path, independent of needing new CIDs. Still, I can see where a reasonable implementation might get into a state where it's always discarding a single CID at a time and perhaps wanting to discard CIDs non-contiguously.

Perhaps this is a question to discuss on-list, rather than in a PR comment?

Copy link
Member

Choose a reason for hiding this comment

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

I agree that we can follow up on the migration logic separate from this discussion. For this PR though, I don't think we should use a window style for CIDs. I think it restricts implementations from doing other things.

connection ID is currently being used on any local address, the endpoint MUST
advance to a more recent connection ID. If a more recent connection ID is not
available, no packets can be sent until a NEW_CONNECTION_ID frame is received.

Endpoints sending a RETIRE_CONNECTION_ID frame MUST continue to accept packets
with older connection IDs until the packet containing the frame is acknowledged.

## REQUEST_CONNECTION_ID Frame {#frame-request-cid}

The REQUEST_CONNECTION_ID frame (type=0x22) informs the peer that the sender
has fewer available connection IDs than it wishes to maintain available.

The frame is as follows:

~~~
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Received (i) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Requested (8) |
+-+-+-+-+-+-+-+-+
~~~

The fields in the REQUEST_CONNECTION_ID frame are as follows:

Received:
: The greatest connection ID sequence number received by the sender
from the receiver in a NEW_CONNECTION_ID frame, formatted as a
variable-length integer.

Requested:
: A one-byte integer specifying the number of additional connection
IDs the sender would like to be issued.
Copy link
Member

Choose a reason for hiding this comment

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

Why not a single integer that says "fill to here" ?


Upon receipt of a REQUEST_CONNECTION_ID frame, an endpoint SHOULD
ensure that it has issued at least the requested number of connection
IDs beyond the specified sequence number. An endpoint MAY impose
implementation-specific limitations on the number of outstanding
connection IDs, but packet loss and failed path migrations can result
in some connection IDs being considered used by one peer and unused
by another.

The number of older connection IDs still outstanding MAY be constrained
using the RETIRE_CONNECTION_ID frames.


## CRYPTO Frame {#frame-crypto}

Expand Down