Learning objective. Understand why a QUIC connection can survive a change of IP address, the rules that gate migration (client-only, after the handshake), and the path-validation handshake (PATH_CHALLENGE / PATH_RESPONSE plus the anti-amplification limit) that lets a server safely follow a peer to a new address.
Why a connection can move at all #
A TCP connection is identified by its four-tuple — source IP, source port, destination IP, destination port — so the moment a client's address changes (Wi-Fi to cellular, a NAT reassigning a port), the connection is, by definition, a different connection, and it breaks. QUIC breaks that coupling. A connection is identified by its connection ID, not its address: "The use of a connection ID allows connections to survive changes to endpoint addresses (IP address and port), such as those caused by an endpoint migrating to a new network" ([RFC 9000 §9]). The packets still carry the same connection ID, so the server recognizes them as the same connection even arriving from a new IP, and the handshake, the keys, and the open streams all continue.
This is connection migration, and it is the feature behind "my video call survived the walk out of the building." But letting a connection follow an address change is also a gift to attackers, so QUIC wraps it in strict rules.
The rules that gate migration #
Two constraints bound when migration may happen ([RFC 9000 §9]):
- After the handshake only. "An endpoint MUST NOT initiate connection migration before the handshake is confirmed" ([RFC 9000 §9]). The handshake relies on a stable address; migration is a post-handshake capability.
- Client-initiated only. "Clients are responsible for initiating all migrations. Servers do not
send non-probing packets... toward a client address until they see a non-probing packet from that
address" ([RFC 9000 §9]). A server never spontaneously moves; the one exception is a server's
advertised
preferred_address(§10.2), which is still the client doing the migrating.
Probing versus non-probing packets #
QUIC distinguishes two kinds of packet, and the distinction is what signals a migration ([RFC 9000 §9.1]):
"PATH_CHALLENGE, PATH_RESPONSE, NEW_CONNECTION_ID, and PADDING frames are 'probing frames', and all other frames are 'non-probing frames'. A packet containing only probing frames is a 'probing packet', and a packet containing any other frame is a 'non-probing packet'."
Probing packets exist to test a path without committing to it. A non-probing packet (one carrying a STREAM, ACK, or other real frame) arriving from a new address is the signal: "Receiving a packet from a new peer address containing a non-probing frame indicates that the peer has migrated to that address" ([RFC 9000 §9.3]).
Validating the new path #
A server cannot simply believe a new address: a packet's source address is trivially spoofable, and following a spoofed address would let an attacker aim the server's traffic at a victim. So the server validates the path before committing to it ([RFC 9000 §8.2]):
- The server sends a PATH_CHALLENGE frame (
0x1a) containing "an unpredictable payload" ([RFC 9000 §8.2.1]) — 8 bytes of randomness ([RFC 9000 §19.17],constants.md§4). - The peer "MUST respond by echoing the data contained in the PATH_CHALLENGE frame in a
PATH_RESPONSE frame" (
0x1b, [RFC 9000 §8.2.2]). - Validation "succeeds when a PATH_RESPONSE frame is received that contains the data that was sent in a previous PATH_CHALLENGE" ([RFC 9000 §8.2.3]).
The 8 bytes of entropy are the whole security argument: an off-path attacker who spoofed the new address never sees the challenge, so it cannot produce the matching response. Only an endpoint genuinely at address Y can echo the bytes back.
Two protections that ride along #
Validation is paired with two safeguards that matter especially on a fresh path:
- The anti-amplification limit. Until an address is validated, an endpoint "MUST limit the amount of data it sends to the unvalidated address to three times the amount of data received" ([RFC 9000 §8], and §8.1). Without it, a spoofed migration could turn the server into a reflector aimed at a victim ([RFC 9000 §9.3.1]); with it, the server can send only a little to an unproven path.
- Congestion-control and RTT reset. The new path may be nothing like the old one: different bandwidth, different latency. So "on confirming a peer's ownership of its new address, an endpoint MUST immediately reset the congestion controller and round-trip time estimator for the new path to initial values... unless the only change in the peer's address is its port number" ([RFC 9000 §9.4]). The port-only exception exists because a bare port change is usually a NAT rebinding on the same physical path (§10.3), where the old estimates still hold.
Worked example: walking off Wi-Fi #
Trace Figure 10.1-1. A client is streaming 1-RTT data over Wi-Fi (address X) when the handset drops to cellular and its address becomes Y. The client keeps the same connection ID and sends its next STREAM-bearing packet (a non-probing packet) from Y. The server sees a known connection ID from an unknown address and concludes the peer migrated, but does not yet trust Y: it sends a PATH_CHALLENGE with 8 random bytes toward Y and, until Y answers, will send no more than three times what it has received. The genuine client at Y echoes the bytes in a PATH_RESPONSE; the path is validated. Because this is a real network change (not just a port), the server resets its congestion controller and RTT estimator to initial values for the cellular path, and full data flow resumes. The video call never dropped.
Migration is not free of cost, and the RTT/congestion reset is why. After validating the new path the server is back at its initial congestion window and initial RTT estimate (§8.1). It has to re-probe the new path's capacity from scratch. So a migration preserves the connection (no handshake, no lost streams) but briefly throttles throughput while the controller re-ramps. For a latency-sensitive but low-bandwidth flow like a voice call that is invisible; for a bulk transfer it is a momentary slowdown, the price of not tearing everything down.
editorial Migration only works if it was set up before it was
needed. The path validation above assumes the migrating endpoint has an unused connection ID to move
to. That requires the peer to have supplied a pool of them in advance (§10.2). When
you enable migration in a client (mobile apps are the big win), confirm the server advertises an
active_connection_id_limit above the minimum and actually issues spare connection IDs; otherwise the
first network change will find no CID to migrate to and the connection will drop exactly when
migration was supposed to save it.
Takeaways #
QUIC connections survive address changes because they are identified by a connection ID, not a four-tuple. Migration is client-initiated and only after the handshake; a non-probing packet from a new address signals it. The server validates the new path with a PATH_CHALLENGE/PATH_RESPONSE exchange of 8 unpredictable bytes, bounded by the three-times anti-amplification limit, and resets congestion control and RTT for the new path (except on a bare port change). The connection-ID machinery that makes a spare address available to migrate to (NEW_CONNECTION_ID, RETIRE_CONNECTION_ID, and the linkability rules) is §10.2.