Skip to content

Commit 4500593

Browse files
authored
feat: emit migration deny reason with datagram drop event (#2456)
1 parent 36fbf86 commit 4500593

File tree

7 files changed

+97
-13
lines changed

7 files changed

+97
-13
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -784,7 +784,7 @@ enum DatagramDropReason {
784784
/// The peer initiated a connection migration before the handshake was confirmed.
785785
ConnectionMigrationDuringHandshake,
786786
/// The attempted connection migration was rejected.
787-
RejectedConnectionMigration,
787+
RejectedConnectionMigration { reason: MigrationDenyReason },
788788
/// The maximum number of paths per connection was exceeded.
789789
PathLimitExceeded,
790790
/// The peer initiated a connection migration without supplying enough connection IDs to use.

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

+5-3
Original file line numberDiff line numberDiff line change
@@ -878,7 +878,7 @@ pub mod api {
878878
ConnectionMigrationDuringHandshake {},
879879
#[non_exhaustive]
880880
#[doc = " The attempted connection migration was rejected."]
881-
RejectedConnectionMigration {},
881+
RejectedConnectionMigration { reason: MigrationDenyReason },
882882
#[non_exhaustive]
883883
#[doc = " The maximum number of paths per connection was exceeded."]
884884
PathLimitExceeded {},
@@ -4985,7 +4985,7 @@ pub mod builder {
49854985
#[doc = " The peer initiated a connection migration before the handshake was confirmed."]
49864986
ConnectionMigrationDuringHandshake,
49874987
#[doc = " The attempted connection migration was rejected."]
4988-
RejectedConnectionMigration,
4988+
RejectedConnectionMigration { reason: MigrationDenyReason },
49894989
#[doc = " The maximum number of paths per connection was exceeded."]
49904990
PathLimitExceeded,
49914991
#[doc = " The peer initiated a connection migration without supplying enough connection IDs to use."]
@@ -5010,7 +5010,9 @@ pub mod builder {
50105010
Self::RejectedConnectionAttempt => RejectedConnectionAttempt {},
50115011
Self::UnknownServerAddress => UnknownServerAddress {},
50125012
Self::ConnectionMigrationDuringHandshake => ConnectionMigrationDuringHandshake {},
5013-
Self::RejectedConnectionMigration => RejectedConnectionMigration {},
5013+
Self::RejectedConnectionMigration { reason } => RejectedConnectionMigration {
5014+
reason: reason.into_event(),
5015+
},
50145016
Self::PathLimitExceeded => PathLimitExceeded {},
50155017
Self::InsufficientConnectionIds => InsufficientConnectionIds {},
50165018
}

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

+10-5
Original file line numberDiff line numberDiff line change
@@ -336,10 +336,13 @@ impl<Config: endpoint::Config> Manager<Config> {
336336
self.active_path().local_connection_id != datagram.destination_connection_id;
337337

338338
if active_migration {
339-
ensure!(
340-
limits.active_migration_enabled(),
341-
Err(DatagramDropReason::RejectedConnectionMigration)
342-
)
339+
ensure!(limits.active_migration_enabled(), {
340+
let reason = migration::DenyReason::ConnectionMigrationDisabled;
341+
publisher.on_connection_migration_denied(reason.into_event());
342+
Err(DatagramDropReason::RejectedConnectionMigration {
343+
reason: reason.into_event().reason,
344+
})
345+
})
343346
}
344347

345348
// TODO set alpn if available
@@ -367,7 +370,9 @@ impl<Config: endpoint::Config> Manager<Config> {
367370
}
368371
migration::Outcome::Deny(reason) => {
369372
publisher.on_connection_migration_denied(reason.into_event());
370-
return Err(DatagramDropReason::RejectedConnectionMigration);
373+
return Err(DatagramDropReason::RejectedConnectionMigration {
374+
reason: reason.into_event().reason,
375+
});
371376
}
372377
_ => {
373378
unimplemented!("unimplemented migration outcome");

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ impl Model {
196196
match datagram_drop_reason {
197197
// Ignore errors emitted by the migration::validator and peer_id_registry
198198
DatagramDropReason::InsufficientConnectionIds => {}
199-
DatagramDropReason::RejectedConnectionMigration => {}
199+
DatagramDropReason::RejectedConnectionMigration { .. } => {}
200200
DatagramDropReason::PathLimitExceeded => {}
201201
datagram_drop_reason => panic!("{:?}", datagram_drop_reason),
202202
};

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

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ 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 }
67
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 } }
78
MtuUpdated { path_id: 1, mtu: 1200, cause: NewPath, search_complete: false }
89
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 } }

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

+4-2
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::testing::Publisher,
17+
event::{builder::MigrationDenyReason, testing::Publisher},
1818
inet::{DatagramInfo, ExplicitCongestionNotification, SocketAddress},
1919
path::{migration, RemoteAddress},
2020
random::{self, Generator},
@@ -1043,7 +1043,9 @@ fn active_connection_migration_disabled() {
10431043
// The active migration is rejected
10441044
assert!(matches!(
10451045
res,
1046-
Err(DatagramDropReason::RejectedConnectionMigration)
1046+
Err(DatagramDropReason::RejectedConnectionMigration {
1047+
reason: MigrationDenyReason::ConnectionMigrationDisabled { .. }
1048+
})
10471049
));
10481050
assert_eq!(1, manager.paths.len());
10491051

quic/s2n-quic/src/tests/connection_migration.rs

+75-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use super::*;
55
use s2n_codec::DecoderBufferMut;
66
use s2n_quic_core::{
7-
event::api::{DatagramDropReason, Subject},
7+
event::api::{DatagramDropReason, MigrationDenyReason, Subject},
88
packet::interceptor::{Datagram, Interceptor},
99
path::{LocalAddress, RemoteAddress},
1010
};
@@ -373,3 +373,77 @@ fn rebind_ipv4_mapped_before_handshake_confirmed() {
373373
}
374374
}
375375
}
376+
377+
/// Rebinds to a port after a specified number of packets
378+
struct RebindToPort {
379+
port: u16,
380+
after: usize,
381+
}
382+
383+
impl Interceptor for RebindToPort {
384+
fn intercept_rx_remote_address(&mut self, _subject: &Subject, addr: &mut RemoteAddress) {
385+
if self.after == 0 {
386+
addr.set_port(self.port);
387+
}
388+
}
389+
390+
fn intercept_rx_datagram<'a>(
391+
&mut self,
392+
_subject: &Subject,
393+
_datagram: &Datagram,
394+
payload: DecoderBufferMut<'a>,
395+
) -> DecoderBufferMut<'a> {
396+
self.after = self.after.saturating_sub(1);
397+
payload
398+
}
399+
}
400+
401+
/// Ensures that a blocked port is not migrated to
402+
#[test]
403+
fn rebind_blocked_port() {
404+
let model = Model::default();
405+
let subscriber = recorder::DatagramDropped::new();
406+
let datagram_dropped_events = subscriber.events();
407+
408+
test(model, move |handle| {
409+
let server = Server::builder()
410+
.with_io(handle.builder().build()?)?
411+
.with_tls(SERVER_CERTS)?
412+
.with_event((tracing_events(), subscriber))?
413+
.with_random(Random::with_seed(456))?
414+
.with_packet_interceptor(RebindToPort { port: 53, after: 2 })?
415+
.start()?;
416+
417+
let client = Client::builder()
418+
.with_io(handle.builder().build()?)?
419+
.with_tls(certificates::CERT_PEM)?
420+
.with_event(tracing_events())?
421+
.with_random(Random::with_seed(456))?
422+
.start()?;
423+
424+
let addr = start_server(server)?;
425+
426+
primary::spawn(async move {
427+
let mut connection = client
428+
.connect(Connect::new(addr).with_server_name("localhost"))
429+
.await
430+
.unwrap();
431+
let mut stream = connection.open_bidirectional_stream().await.unwrap();
432+
let _ = stream.send(Bytes::from_static(b"hello")).await;
433+
let _ = stream.finish();
434+
let _ = stream.receive().await;
435+
});
436+
437+
Ok(addr)
438+
})
439+
.unwrap();
440+
441+
let datagram_dropped_events = datagram_dropped_events.lock().unwrap();
442+
443+
assert!(!datagram_dropped_events.is_empty());
444+
for event in datagram_dropped_events.iter() {
445+
if let DatagramDropReason::RejectedConnectionMigration { reason, .. } = &event.reason {
446+
assert!(matches!(reason, MigrationDenyReason::BlockedPort { .. }));
447+
}
448+
}
449+
}

0 commit comments

Comments
 (0)