Skip to content

Commit 6db8a62

Browse files
committed
feat(quic): add experimental client-side connection migration support
Add support for QUIC connection migration, allowing outbound connections to switch network paths (e.g., from a primary interface to a failover interface) without disrupting active streams. This feature leverages quic-go's connection migration APIs (AddPath, Probe, Switch) introduced in v0.51.0. Migration is opt-in via the EnableExperimentalConnectionMigration() transport option. Key changes: - Add MigratableConn and Path interfaces in core/network - Add migration event types in core/event - Implement MigratableConn in QUIC connection via Conn.As() - Add TransportForMigration to ConnManager for alternate paths - Add comprehensive tests and documentation Only client-initiated (outbound) connections support migration per the QUIC specification. Server-side connections return SupportsMigration() == false. Signed-off-by: Willian Paixao <willian@ufpa.br>
1 parent b0b2a18 commit 6db8a62

File tree

9 files changed

+1126
-12
lines changed

9 files changed

+1126
-12
lines changed

core/event/migration.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package event
2+
3+
import (
4+
"time"
5+
6+
"github.com/libp2p/go-libp2p/core/peer"
7+
8+
ma "github.com/multiformats/go-multiaddr"
9+
)
10+
11+
// EvtConnectionMigrationStarted is emitted when a connection migration begins.
12+
// This event would be emitted when AddPath() completes successfully, before
13+
// the user calls Probe() on the returned path.
14+
//
15+
// Note: Event emission is not yet implemented. These types are defined for
16+
// future use and API stability.
17+
//
18+
// This is an EXPERIMENTAL event and may change in future versions.
19+
type EvtConnectionMigrationStarted struct {
20+
// Peer is the remote peer ID of the connection being migrated.
21+
Peer peer.ID
22+
// ConnID is the unique identifier of the connection.
23+
ConnID string
24+
// FromLocalAddr is the current local address before migration.
25+
FromLocalAddr ma.Multiaddr
26+
// ToLocalAddr is the target local address to migrate to.
27+
ToLocalAddr ma.Multiaddr
28+
}
29+
30+
// EvtConnectionMigrationCompleted is emitted when a connection migration succeeds.
31+
// At this point, the connection is using the new path for all communication.
32+
//
33+
// Note: In client-side QUIC migration, only the local address changes. The remote
34+
// address remains the same since we're changing which local interface we use to
35+
// reach the same peer.
36+
//
37+
// This is an EXPERIMENTAL event and may change in future versions.
38+
type EvtConnectionMigrationCompleted struct {
39+
// Peer is the remote peer ID of the migrated connection.
40+
Peer peer.ID
41+
// ConnID is the unique identifier of the connection.
42+
ConnID string
43+
// FromLocalAddr is the previous local address.
44+
FromLocalAddr ma.Multiaddr
45+
// ToLocalAddr is the new local address after migration.
46+
ToLocalAddr ma.Multiaddr
47+
// ProbeRTT is the round-trip time measured during path probing.
48+
ProbeRTT time.Duration
49+
}
50+
51+
// EvtConnectionMigrationFailed is emitted when a connection migration fails.
52+
// The connection may still be usable on the original path, or it may be closed
53+
// if both the new path and rollback failed.
54+
//
55+
// This is an EXPERIMENTAL event and may change in future versions.
56+
type EvtConnectionMigrationFailed struct {
57+
// Peer is the remote peer ID of the connection.
58+
Peer peer.ID
59+
// ConnID is the unique identifier of the connection.
60+
ConnID string
61+
// FromLocalAddr is the original local address.
62+
FromLocalAddr ma.Multiaddr
63+
// ToLocalAddr is the target local address that failed.
64+
ToLocalAddr ma.Multiaddr
65+
// Error describes why the migration failed.
66+
Error error
67+
// ConnectionClosed indicates whether the connection was closed due to
68+
// the failure (e.g., if rollback to the original path also failed).
69+
ConnectionClosed bool
70+
}
71+
72+
// EvtPathAdded is emitted when a new path is added to a connection.
73+
// This happens when MigratableConn.AddPath() is called successfully.
74+
//
75+
// This is an EXPERIMENTAL event and may change in future versions.
76+
type EvtPathAdded struct {
77+
// Peer is the remote peer ID of the connection.
78+
Peer peer.ID
79+
// ConnID is the unique identifier of the connection.
80+
ConnID string
81+
// LocalAddr is the local address of the new path.
82+
LocalAddr ma.Multiaddr
83+
}
84+
85+
// EvtPathRemoved is emitted when a path is removed from a connection.
86+
// This happens when Path.Close() is called.
87+
//
88+
// This is an EXPERIMENTAL event and may change in future versions.
89+
type EvtPathRemoved struct {
90+
// Peer is the remote peer ID of the connection.
91+
Peer peer.ID
92+
// ConnID is the unique identifier of the connection.
93+
ConnID string
94+
// LocalAddr is the local address of the removed path.
95+
LocalAddr ma.Multiaddr
96+
}

core/network/migration.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package network
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
ma "github.com/multiformats/go-multiaddr"
8+
)
9+
10+
// PathInfo contains information about a network path.
11+
//
12+
// This is an EXPERIMENTAL type and may change in future versions.
13+
type PathInfo struct {
14+
// LocalAddr is the local address of this path.
15+
LocalAddr ma.Multiaddr
16+
// RemoteAddr is the remote address of this path.
17+
RemoteAddr ma.Multiaddr
18+
// Active indicates whether this is the currently active path.
19+
Active bool
20+
// RTT is the round-trip time measured for this path.
21+
RTT time.Duration
22+
}
23+
24+
// Path represents a network path that can be used for connection migration.
25+
// A path is created via MigratableConn.AddPath() and can be probed and
26+
// switched to.
27+
//
28+
// This is an EXPERIMENTAL interface and may change in future versions.
29+
type Path interface {
30+
// Probe tests the path connectivity by sending a probe packet and
31+
// waiting for acknowledgment. This validates that the path is usable
32+
// before switching to it.
33+
//
34+
// The context can be used to set a timeout for the probe operation.
35+
Probe(ctx context.Context) error
36+
37+
// Switch makes this path the active path for the connection.
38+
// This should only be called after a successful Probe().
39+
// After switching, all subsequent packets will use this path.
40+
Switch() error
41+
42+
// Info returns information about this path.
43+
Info() PathInfo
44+
45+
// Close removes this path from the connection.
46+
// If this is the active path, Close will fail; switch to a different
47+
// path first.
48+
Close() error
49+
}
50+
51+
// MigratableConn is implemented by connections that support path migration.
52+
// This allows switching the underlying network path (e.g., from a primary
53+
// interface to a failover interface) without disrupting active streams.
54+
//
55+
// Only client-initiated (outbound) QUIC connections support migration per
56+
// the QUIC specification.
57+
//
58+
// This is an EXPERIMENTAL interface and may change in future versions.
59+
type MigratableConn interface {
60+
// SupportsMigration returns true if this connection supports path migration.
61+
// Returns false for server-side connections or when migration is disabled.
62+
SupportsMigration() bool
63+
64+
// AddPath adds a new potential path using the given local address.
65+
// The returned Path can be probed and then switched to.
66+
//
67+
// The context can be used to set a timeout for path creation.
68+
AddPath(ctx context.Context, localAddr ma.Multiaddr) (Path, error)
69+
70+
// ActivePath returns information about the currently active path.
71+
ActivePath() PathInfo
72+
73+
// AvailablePaths returns information about all available paths,
74+
// including the active path and any paths added via AddPath().
75+
AvailablePaths() []PathInfo
76+
}

0 commit comments

Comments
 (0)