Skip to content

Commit c82daf7

Browse files
fix(s2n-quic-transport): allow migrations even when disable_active_migration is sent (#2516)
* fix: allow migrations even when disable_active_migrations is sent * update events
1 parent a9e7673 commit c82daf7

5 files changed

Lines changed: 53 additions & 33 deletions

File tree

quic/s2n-quic-core/events/common.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,12 +782,16 @@ enum DatagramDropReason {
782782
/// A datagram was received from an unknown server address.
783783
UnknownServerAddress,
784784
/// The peer initiated a connection migration before the handshake was confirmed.
785+
///
786+
/// Note: This drop reason is no longer emitted
785787
ConnectionMigrationDuringHandshake,
786788
/// The attempted connection migration was rejected.
787789
RejectedConnectionMigration { reason: MigrationDenyReason },
788790
/// The maximum number of paths per connection was exceeded.
789791
PathLimitExceeded,
790792
/// The peer initiated a connection migration without supplying enough connection IDs to use.
793+
///
794+
/// Note: This drop reason is no longer emitted
791795
InsufficientConnectionIds,
792796
}
793797

quic/s2n-quic-core/src/event/generated.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,8 @@ pub mod api {
875875
UnknownServerAddress {},
876876
#[non_exhaustive]
877877
#[doc = " The peer initiated a connection migration before the handshake was confirmed."]
878+
#[doc = ""]
879+
#[doc = " Note: This drop reason is no longer emitted"]
878880
ConnectionMigrationDuringHandshake {},
879881
#[non_exhaustive]
880882
#[doc = " The attempted connection migration was rejected."]
@@ -884,6 +886,8 @@ pub mod api {
884886
PathLimitExceeded {},
885887
#[non_exhaustive]
886888
#[doc = " The peer initiated a connection migration without supplying enough connection IDs to use."]
889+
#[doc = ""]
890+
#[doc = " Note: This drop reason is no longer emitted"]
887891
InsufficientConnectionIds {},
888892
}
889893
impl aggregate::AsVariant for DatagramDropReason {
@@ -5051,12 +5055,16 @@ pub mod builder {
50515055
#[doc = " A datagram was received from an unknown server address."]
50525056
UnknownServerAddress,
50535057
#[doc = " The peer initiated a connection migration before the handshake was confirmed."]
5058+
#[doc = ""]
5059+
#[doc = " Note: This drop reason is no longer emitted"]
50545060
ConnectionMigrationDuringHandshake,
50555061
#[doc = " The attempted connection migration was rejected."]
50565062
RejectedConnectionMigration { reason: MigrationDenyReason },
50575063
#[doc = " The maximum number of paths per connection was exceeded."]
50585064
PathLimitExceeded,
50595065
#[doc = " The peer initiated a connection migration without supplying enough connection IDs to use."]
5066+
#[doc = ""]
5067+
#[doc = " Note: This drop reason is no longer emitted"]
50605068
InsufficientConnectionIds,
50615069
}
50625070
impl IntoEvent<api::DatagramDropReason> for DatagramDropReason {

quic/s2n-quic-transport/src/path/manager.rs

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use crate::{
1212
use s2n_quic_core::{
1313
ack,
1414
connection::{self, Limits, PeerId},
15-
ensure,
1615
event::{
1716
self,
1817
builder::{DatagramDropReason, MtuUpdatedCause},
@@ -339,20 +338,6 @@ impl<Config: endpoint::Config> Manager<Config> {
339338
let local_address = path_handle.local_address();
340339
let active_local_addr = self.active_path().local_address();
341340
let active_remote_addr = self.active_path().remote_address();
342-
// The peer has intentionally tried to migrate to a new path because they changed
343-
// their destination_connection_id. This is considered an "active" migration.
344-
let active_migration =
345-
self.active_path().local_connection_id != datagram.destination_connection_id;
346-
347-
if active_migration {
348-
ensure!(limits.active_migration_enabled(), {
349-
let reason = migration::DenyReason::ConnectionMigrationDisabled;
350-
publisher.on_connection_migration_denied(reason.into_event());
351-
Err(DatagramDropReason::RejectedConnectionMigration {
352-
reason: reason.into_event().reason,
353-
})
354-
})
355-
}
356341

357342
// TODO set alpn if available
358343
let attempt: migration::Attempt = migration::AttemptBuilder {
@@ -441,15 +426,18 @@ impl<Config: endpoint::Config> Manager<Config> {
441426
let cc = congestion_controller_endpoint.new_congestion_controller(path_info);
442427

443428
let peer_connection_id = {
444-
if active_migration {
429+
if self.active_path().local_connection_id != datagram.destination_connection_id {
445430
//= https://www.rfc-editor.org/rfc/rfc9000#section-9.5
446431
//# Similarly, an endpoint MUST NOT reuse a connection ID when sending to
447432
//# more than one destination address.
448433

449-
// Active connection migrations must use a new connection ID
434+
// The peer changed destination CIDs, so we will attempt to switch to a new
435+
// destination CID as well. This could still just be a NAT rebind though, so
436+
// we continue with the existing destination CID if there isn't a new one
437+
// available.
450438
self.peer_id_registry
451439
.consume_new_id_for_new_path()
452-
.ok_or(DatagramDropReason::InsufficientConnectionIds)?
440+
.unwrap_or(self.active_path().peer_connection_id)
453441
} else {
454442
//= https://www.rfc-editor.org/rfc/rfc9000#section-9.5
455443
//# Due to network changes outside

quic/s2n-quic-transport/src/path/manager/snapshots/path__manager__tests__active_connection_migration_disabled__events.snap

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ source: quic/s2n-quic-core/src/event/snapshot.rs
33
input_file: quic/s2n-quic-transport/src/path/manager/tests.rs
44
---
55
ConnectionIdUpdated { path_id: 0, cid_consumer: Local, previous: 0x01, current: 0x69643032 }
6-
ConnectionMigrationDenied { reason: ConnectionMigrationDisabled }
76
PathCreated { active: Path { local_addr: 0.0.0.0:0, local_cid: 0x4c6f63616c4900000000000000004c6f63616c49, remote_addr: 127.0.0.1:1, remote_cid: 0x69643032, id: 0, is_active: true }, new: Path { local_addr: 0.0.0.0:0, local_cid: 0x69643032, remote_addr: 127.0.0.2:1, remote_cid: 0x69643033, id: 1, is_active: false } }
87
MtuUpdated { path_id: 1, mtu: 1200, cause: NewPath, search_complete: false }
9-
PathCreated { active: Path { local_addr: 0.0.0.0:0, local_cid: 0x4c6f63616c4900000000000000004c6f63616c49, remote_addr: 127.0.0.1:1, remote_cid: 0x69643032, id: 0, is_active: true }, new: Path { local_addr: 0.0.0.0:0, local_cid: 0x4c6f63616c4900000000000000004c6f63616c49, remote_addr: 127.0.0.3:1, remote_cid: 0x69643032, id: 2, is_active: false } }
8+
PathCreated { active: Path { local_addr: 0.0.0.0:0, local_cid: 0x4c6f63616c4900000000000000004c6f63616c49, remote_addr: 127.0.0.1:1, remote_cid: 0x69643032, id: 0, is_active: true }, new: Path { local_addr: 0.0.0.0:0, local_cid: 0x69643033, remote_addr: 127.0.0.2:1, remote_cid: 0x69643032, id: 2, is_active: false } }
109
MtuUpdated { path_id: 2, mtu: 1200, cause: NewPath, search_complete: false }
10+
PathCreated { active: Path { local_addr: 0.0.0.0:0, local_cid: 0x4c6f63616c4900000000000000004c6f63616c49, remote_addr: 127.0.0.1:1, remote_cid: 0x69643032, id: 0, is_active: true }, new: Path { local_addr: 0.0.0.0:0, local_cid: 0x4c6f63616c4900000000000000004c6f63616c49, remote_addr: 127.0.0.3:1, remote_cid: 0x69643032, id: 3, is_active: false } }
11+
MtuUpdated { path_id: 3, mtu: 1200, cause: NewPath, search_complete: false }

quic/s2n-quic-transport/src/path/manager/tests.rs

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::{
1414
use core::time::Duration;
1515
use s2n_quic_core::{
1616
connection::limits::ANTI_AMPLIFICATION_MULTIPLIER,
17-
event::{builder::MigrationDenyReason, testing::Publisher},
17+
event::testing::Publisher,
1818
inet::{DatagramInfo, ExplicitCongestionNotification, SocketAddress},
1919
path::{migration, RemoteAddress},
2020
random::{self, Generator},
@@ -982,6 +982,9 @@ fn limit_number_of_connection_migrations() {
982982
assert_eq!(total_paths, MAX_ALLOWED_PATHS);
983983
}
984984

985+
// Connection migration is still allowed to proceed even if the `disable_active_migration`
986+
// transport parameter is sent, as there is no way to definitely distinguish an active
987+
// migration from a NAT rebind.
985988
#[test]
986989
fn active_connection_migration_disabled() {
987990
// Setup:
@@ -1015,13 +1018,14 @@ fn active_connection_migration_disabled() {
10151018
let new_addr: SocketAddr = "127.0.0.2:1".parse().unwrap();
10161019
let new_addr = SocketAddress::from(new_addr);
10171020
let new_addr = RemoteAddress::from(new_addr);
1018-
let new_cid = connection::LocalId::try_from_bytes(b"id02").unwrap();
1021+
let new_cid_1 = connection::LocalId::try_from_bytes(b"id02").unwrap();
1022+
let new_cid_2 = connection::LocalId::try_from_bytes(b"id03").unwrap();
10191023
let now = NoopClock {}.get_time();
10201024
let mut datagram = DatagramInfo {
10211025
timestamp: now,
10221026
payload_len: 0,
10231027
ecn: ExplicitCongestionNotification::default(),
1024-
destination_connection_id: new_cid,
1028+
destination_connection_id: new_cid_1,
10251029
destination_connection_id_classification: connection::id::Classification::Local,
10261030
source_connection_id: None,
10271031
};
@@ -1040,16 +1044,21 @@ fn active_connection_migration_disabled() {
10401044
&mut publisher,
10411045
);
10421046

1043-
// The active migration is rejected
1044-
assert!(matches!(
1045-
res,
1046-
Err(DatagramDropReason::RejectedConnectionMigration {
1047-
reason: MigrationDenyReason::ConnectionMigrationDisabled { .. }
1048-
})
1049-
));
1050-
assert_eq!(1, manager.paths.len());
1047+
// The migration succeeds
1048+
assert!(res.is_ok());
1049+
assert_eq!(2, manager.paths.len());
1050+
// The new path uses a new CID since there were enough supplied
1051+
assert_eq!(
1052+
manager.paths[res.unwrap().0.as_u8() as usize].peer_connection_id,
1053+
id_3
1054+
);
1055+
1056+
// Clear the pending packet authentication to allow another migration to proceed
1057+
manager.pending_packet_authentication = None;
10511058

10521059
// Try an active connection migration with active migration enabled (default)
1060+
datagram.destination_connection_id = new_cid_2;
1061+
10531062
let res = manager.handle_connection_migration(
10541063
&new_addr,
10551064
&datagram,
@@ -1062,7 +1071,12 @@ fn active_connection_migration_disabled() {
10621071

10631072
// The migration succeeds
10641073
assert!(res.is_ok());
1065-
assert_eq!(2, manager.paths.len());
1074+
assert_eq!(3, manager.paths.len());
1075+
// The new path uses the existing id since there wasn't a new one available
1076+
assert_eq!(
1077+
manager.paths[res.unwrap().0.as_u8() as usize].peer_connection_id,
1078+
id_2
1079+
);
10661080

10671081
// Now try a non-active (passive) migration, with active migration disabled
10681082
// the same CID is used, so it's not an active migration
@@ -1088,7 +1102,12 @@ fn active_connection_migration_disabled() {
10881102

10891103
// The passive migration succeeds
10901104
assert!(res.is_ok());
1091-
assert_eq!(3, manager.paths.len());
1105+
assert_eq!(4, manager.paths.len());
1106+
// The new path uses the existing id since the peer did not change their destination CID
1107+
assert_eq!(
1108+
manager.paths[res.unwrap().0.as_u8() as usize].peer_connection_id,
1109+
id_2
1110+
);
10921111
}
10931112

10941113
#[test]

0 commit comments

Comments
 (0)