msquic
msquic copied to clipboard
Source Connection ID selection on MsQuic server
Describe the bug
When testing commit 5c070cdc, we discovered a behavior on the MsQuic server that deviates from the specification.
As described in RFC 9000 (https://www.rfc-editor.org/rfc/rfc9000.html#name-negotiating-connection-ids), the client and server must select their Source Connection ID in their first Initial packet. This Source Connection ID must then be used by the peer as the Destination Connection ID in their subsequent messages. However, the MsQuic server deviates from this specification by selecting its Source Connection ID only when receiving a Crypto frame that carries the Client Hello message. This may cause a Connection ID mismatch on the client side if the client did not send the Client Hello as its first Initial packet to the server.
Affected OS
- [ ] Windows
- [ ] Linux
- [ ] macOS
- [ ] Other (specify below)
Additional OS information
No response
MsQuic version
Older
Steps taken to reproduce bug
(1) Client sends an Initial packet that carries a Ping frame. (2) Client sends an Initial packet that carries a Client Hello message.
Expected behavior
The server should choose its Source Connection ID when it receives and process the first Initial packet (which may carry CRYPTO, Ping and ConnectionClose frames) from the client.
Actual outcome
The client expects the server to choose its Source Connection ID after the server receives (1). However, the server uses the Destination Connection ID from (1) as its Source Connection ID when acknowledging the client's Ping and only chooses its Source Connection ID when acknowledging the Client Hello message in (2). This will confuse the client when MsQuic chooses its Source Connection ID in (2).
Additional details
No response
Thanks for opening the issue! We'll take a look.
MsQuic server deviates from this specification by selecting its Source Connection ID only when receiving a Crypto frame that carries the Client Hello message
Took a quick look. The initial CID is generated on the server side in QuicConnGenerateNewSourceCid when called by QuicListenerAcceptConnection. This is called here, because only here do we have the necessary state (been assigned a registration and accepted by a listener) to generate a correct CID.
This cannot be changed. We cannot generate a new CID until the listener has accepted the connection.
the server uses the Destination Connection ID from (1) as its Source Connection ID when acknowledging the client's Ping
We need to figure out how to fix this part without changing the place where CID is generated....
I was thinking more on the supposed problem related to not updating the CID in the first packet response from the server, and I was looking at our client code to see how this is handled. In QuicConnUpdateDestCid is where we handle this, and it only updates the CID if it is different from the originally chosen one.
if (Packet->SourceCidLen != DestCid->CID.Length ||
memcmp(Packet->SourceCid, DestCid->CID.Data, DestCid->CID.Length) != 0) {
Using this method, the client is able to handle either the original CID or the updated one.
This may cause a Connection ID mismatch on the client side if the client did not send the Client Hello as its first Initial packet to the server.
I thought the spec requires the client to accept either original or new CIDs for the lifetime of the handshake (long header packets). So long as the client does, it will have no problem. So, I'm leaning towards closing this bug with no action, but I'm interested to hear your thoughts. Thanks!
Thank you for your explanation and follow-up.
Yes, from what you describe, the MsQuic client should not have this issue as the client only update the CID if it is different from the initially chosen one.
My thought is that this may still affect a non-MsQuic client that tries to communicate with a MsQuic server due to the deviation from specification. Other developers may implement as stated in another statement in RFC 9000: A client MUST change the Destination Connection ID it uses for sending packets in response to only the first received Initial or Retry packet. A server MUST set the Destination Connection ID it uses for sending packets based on the first received Initial packet. Any further changes to the Destination Connection ID are only permitted if the values are taken from NEW_CONNECTION_ID frames.
I'd be curious to see how many QUIC stacks out there handle this the same way as MsQuic on the client. On the server side, until we have enough information to properly generate the right CID we have to wait. I guess we could create a temporary CID and update it later (retiring the old one) but I'm not sure if that would cause other problems. Personally, I'd like to push for a spec change if possible.
Hi, maybe I can provide some info regarding your question.
We have tested a list of servers (still adding new servers to the list) and from our investigation, only the MsQuic server behaves like what I describe in this issue.
What about clients?
We don't have any information about the clients currently. We haven't extended the testing to the clients.