Skip to content

Add support for QUIC stateless reset - #2584

Open
csujedihy wants to merge 10 commits into
cloudflare:masterfrom
csujedihy:yi/stateless-reset
Open

csujedihy wants to merge 10 commits into
cloudflare:masterfrom
csujedihy:yi/stateless-reset

Conversation

@csujedihy

@csujedihy csujedihy commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

This PR introduces RFC 9000 stateless reset support to quiche and tokio-quiche. Before this change, quiche already has an incomplete implementation of stateless reset where the stateless reset on-wire token is directly exposed in control interfaces (quiche::Config::set_stateless_reset_token and tokio-quiche::QuicSettings::stateless_reset_token). Basically, with raw quiche, the app would have to implement stateless reset on their own, like deriving a token from a static key, constructing a stateless reset packet from scratch etc. With tokio-quiche, its completely broken because it sends the same token for all incoming connections and it can only generate randomized token for NEW_CONNECTION_ID frames.

This PR:

  1. Introduces new control interfaces (quiche::Config::set_stateless_reset_key and QuicSettings::stateless_reset_key) where the app can supply the static key for token generation.
  2. Adds helpers for building the stateless reset packets for raw quiche applications.
  3. Adds support in tokio-quiche to send stateless reset token (derived from the static key) in TPs and issue new CIDs with reset token derived from the static key. Basically, the app just needs to set the key and everything works.
  4. Adds client support for handling stateless reset.
  5. Adds reset issuance rate-limiting, which only allows 1 reset outstanding per-2-tuple (IP:port).

Things to note:

  1. The old tokio-quiche::QuicSettings::stateless_reset_token isn't removed because we want to take this change in before the upstream change is merged and we don't wanna break internal cloudflare ZT stack.
  2. No server side reset receipt logic for simplicity (not practical in reality for a client to send resets).

@csujedihy
csujedihy requested a review from a team as a code owner August 13, 2026 23:07
@csujedihy
csujedihy force-pushed the yi/stateless-reset branch 2 times, most recently from da3fe0c to 4cb5a67 Compare August 13, 2026 23:52

@gregor-cf gregor-cf left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is great. I just had a couple of smaller nits.

Comment thread quiche/src/crypto/mod.rs Outdated
Comment thread quiche/src/tls/mod.rs Outdated
Comment thread quiche/src/lib.rs
Comment thread quiche/src/crypto/mod.rs Outdated
Comment thread quiche/src/crypto/mod.rs Outdated
Comment thread quiche/src/lib.rs Outdated
Comment thread quiche/src/lib.rs Outdated
Comment thread tokio-quiche/src/quic/router/mod.rs Outdated
Comment thread tokio-quiche/src/quic/router/mod.rs Outdated
Comment thread tokio-quiche/src/quic/router/mod.rs Outdated
@gregor-cf

Copy link
Copy Markdown
Contributor

When sending stateless resets, I'm wondering if we want to do more aggressive limiting than the current "in-flight" logic. E.g., if a connection with a lot of packets in flight arrives at a new server and it needs to send state-less resets, we could potentially send one stateless reset per incoming packet. Not the end of the world and TCP would do the same IIRC, but we could consider limiting to one reset per 2-tuple every couple 100ms to every second. But it's not clear that the complexity would be worth it.

@csujedihy

Copy link
Copy Markdown
Contributor Author

When sending stateless resets, I'm wondering if we want to do more aggressive limiting than the current "in-flight" logic. E.g., if a connection with a lot of packets in flight arrives at a new server and it needs to send state-less resets, we could potentially send one stateless reset per incoming packet. Not the end of the world and TCP would do the same IIRC, but we could consider limiting to one reset per 2-tuple every couple 100ms to every second. But it's not clear that the complexity would be worth it.

msquic exactly does this and I originally wanted to do the same but simplified it. I don't think it's super complicated. We'd need a hashmap instead of a hashset to store the last_sent timestamp and a heap to evict expired entries opportunistically. I can add it.

@ghedo ghedo added type: feature Adds new behavior or API. area: core Changes related to core. labels Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: core Changes related to core. type: feature Adds new behavior or API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants