Learning objective. Explain how Connection IDs let a QUIC connection survive a change of client address, how path validation confirms the new path safely, and what resets when a connection migrates.
A connection is not its address #
TCP binds a connection to the four-tuple of source and destination IP and port; change
any element and the connection breaks. QUIC binds a connection to a Connection ID
instead (§1.4), so when the client's address changes (a phone leaving Wi-Fi
for cellular, a NAT rebinding after an idle period), the packets still carry a Connection
ID the server recognizes, and the connection can continue on the new path
([RFC 9000 §9]). Only the client migrates in QUIC v1; the server keeps its address (aside
from the optional preferred_address).
Two mechanisms make this work. First, each endpoint issues its peer a pool of
Connection IDs over the connection's life, via NEW_CONNECTION_ID frames, each with a
sequence number and a stateless-reset token; RETIRE_CONNECTION_ID withdraws old ones.
The number a peer will hold is capped by active_connection_id_limit (default 2, minimum
2; constants.md §6). Second, before trusting a new path, the endpoint validates it.
Path validation: challenge and response #
When a server receives a non-probing packet (one carrying anything other than
PATH_CHALLENGE, PATH_RESPONSE, NEW_CONNECTION_ID, or PADDING) from an address it
has not validated, it starts path validation ([RFC 9000 §9.1, §8.2]). It sends a
PATH_CHALLENGE frame containing 8 bytes of unpredictable data to the new address;
the peer echoes those exact bytes back in a PATH_RESPONSE (both frames are drawn
byte-for-byte in §10.1). A matching response proves
two things at once: the peer is genuinely reachable at that address (not a spoofed source
in an amplification attack), and there is a working return path. Until validation
succeeds, the server is held to the 3× anti-amplification limit on the new path: it
may send at most three times the bytes it has received from that address
(§8.1).
What resets, and why #
A new path is a different network — possibly a different bottleneck, RTT, and loss rate. Carrying the old path's congestion window and RTT estimate onto it would be wrong, so on migrating to a new path an endpoint MUST reset its congestion controller and RTT estimate ([RFC 9000 §9.4]). In practice migration begins conservatively and re-probes the new path's capacity from a small window, which is one reason addresses should change infrequently rather than flapping.
Distinguish deliberate migration from a passive NAT rebinding. In both, packets arrive from a new source address and the server validates the new path identically. The difference is intent: a migrating client proactively switches to a fresh Connection ID for privacy; a NAT rebinding is involuntary and the client may not even notice. The server's response (validate, then continue) is the same. §10.3 treats NAT rebinding in depth.
editorial For migration to work, a client must switch
to a Connection ID the server has not seen tied to the old path — otherwise a passive
observer can trivially link the two paths by the constant CID, defeating the privacy
benefit. That requires the server to have supplied spare CIDs before the move, so keep
active_connection_id_limit at a few (not the minimum 2) on connections expected to roam,
and issue NEW_CONNECTION_ID frames early. A server that hands out too few CIDs forces
roaming clients to reuse one, quietly eroding privacy. See §10.2.
Takeaways #
Connection IDs turn a connection into something that outlives its address: a migrating or rebinding client keeps talking as long as it uses a known Connection ID, and path validation makes the switch safe by proving reachability before the server commits and by resetting congestion state for the new network. This closes the mechanics of establishing and maintaining a connection; §2.5 turns to what to do when the handshake goes wrong, reading failures out of a trace.