Skip to content

add client migration - #95

Open
harrharr wants to merge 7 commits into
ptrd:masterfrom
harrharr:client-migration
Open

harrharr wants to merge 7 commits into
ptrd:masterfrom
harrharr:client-migration

Conversation

@harrharr

@harrharr harrharr commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Background:
Routers and firewalls pick a path or a queue by hashing each flow's source and destination addresses and ports
(ECMP, per-flow load balancing, per-flow policing). If a flow lands on a congested, lossy or throttled path, it stays there.
So resending lost packets won't succeed and you have to wait for the timeout.
Changing the source port gives the flow a new hash and very likely a different path.
Google's PLB (Protective Load Balancing) does the same for TCP by changing the IPv6 flow label.
So this isn't a hack; it's what clients do on bad long-haul links.

And here's the point:
QUIC can move a connection to a new local port without disturbing its streams
(RFC 9000 §9), which is exactly what we need.

This patch allows applications using KWIK to migrate the client's source port:

  • QuicClientConnection.migrate(Duration timeout) (public API): opens a socket on a new ephemeral port,
    validates the path with PATH_CHALLENGE from that socket (padded to 1200 bytes, up to 3 attempts), and
    only on PATH_RESPONSE switches to it, retires the old path's connection ID (§9.5) and closes the old
    socket. It refuses (returns false) when the handshake isn't confirmed, the server sent
    disable_active_migration, or no unused peer CID is available. Built on your
    ClientSocketManager.addLocalAddress/changeLocalAddress, sendAlternateAddress and the per-address
    peer CID registry.
  • Statistics.consecutivePtoCount(): exposes RecoveryManager.ptoCount, so an application can detect a
    stalled path (N probe timeouts without any ack) and decide to migrate.
  • The client now provides the server with spare connection IDs once the handshake is confirmed
    (ConnectionIdManager.handshakeFinished() is no longer server-only), as the server needs an unused client
    CID to validate a new path.
  • A PATH_CHALLENGE is answered on the path it arrived on (PacketMetaData now carries the local address a
    datagram was received on). Otherwise, when the peer probes a new path because the current one is dead, the
    response is lost. On the server this only applies to paths the PathValidator knows about (i.e. that have
    a client CID assigned); before, an unconditional response to an unknown address threw "new connection ID's
    are exhausted" and aborted the connection.
  • With the migration flag enabled, the server no longer advertises disable_active_migration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant