Skip to content

Commit 7e11553

Browse files
committed
fix: process packets from different sources before handshake confirmed
1 parent 8bfc8a8 commit 7e11553

File tree

5 files changed

+76
-62
lines changed

5 files changed

+76
-62
lines changed

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

Lines changed: 17 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -249,44 +249,25 @@ impl<Config: endpoint::Config> Manager<Config> {
249249
) -> Result<(Id, AmplificationOutcome), DatagramDropReason> {
250250
let valid_initial_received = self.valid_initial_received();
251251

252-
if let Some((id, path)) = self.path_mut(path_handle) {
253-
let source_cid_changed = datagram
254-
.source_connection_id
255-
.is_some_and(|scid| scid != path.peer_connection_id && valid_initial_received);
256-
257-
if source_cid_changed {
258-
//= https://www.rfc-editor.org/rfc/rfc9000#section-7.2
259-
//# Once a client has received a valid Initial packet from the server, it MUST
260-
//# discard any subsequent packet it receives on that connection with a
261-
//# different Source Connection ID.
262-
263-
//= https://www.rfc-editor.org/rfc/rfc9000#section-7.2
264-
//# Any further changes to the Destination Connection ID are only
265-
//# permitted if the values are taken from NEW_CONNECTION_ID frames; if
266-
//# subsequent Initial packets include a different Source Connection ID,
267-
//# they MUST be discarded.
268-
269-
return Err(DatagramDropReason::InvalidSourceConnectionId);
270-
}
271-
272-
// Update the address if it was resolved
273-
//
274-
// NOTE: We don't update the server address since this would cause the client to drop
275-
// packets from the server.
276-
277-
//= https://www.rfc-editor.org/rfc/rfc9000#section-9
278-
//# If a client receives packets from an unknown server address, the client MUST discard these packets.
279-
252+
let matched_path = if handshake_confirmed {
253+
self.path_mut(path_handle)
254+
} else {
280255
//= https://www.rfc-editor.org/rfc/rfc9000#section-9
281-
//# If the peer sent the disable_active_migration transport parameter, an endpoint also MUST NOT send
282-
//# packets (including probing packets; see Section 9.1) from a different local address to the address
283-
//# the peer used during the handshake, unless the endpoint has acted on a preferred_address transport
284-
//# parameter from the peer.
285-
if Config::ENDPOINT_TYPE.is_client() {
286-
path.handle.maybe_update(path_handle);
287-
}
256+
//# The design of QUIC relies on endpoints retaining a stable address
257+
//# for the duration of the handshake. An endpoint MUST NOT initiate
258+
//# connection migration before the handshake is confirmed, as defined
259+
//# in section 4.1.2 of [QUIC-TLS].
260+
261+
// NOTE: while we must not _initiate_ a migration before the handshake is done,
262+
// it doesn't mean we can't handle the packet. So instead we pick the default path.
263+
let path_id = self.active_path_id();
264+
let path = self.active_path_mut();
265+
Some((path_id, path))
266+
};
288267

289-
let amplification_outcome = path.on_bytes_received(datagram.payload_len);
268+
if let Some((id, path)) = matched_path {
269+
let amplification_outcome =
270+
path.on_datagram_received(path_handle, datagram, valid_initial_received)?;
290271
return Ok((id, amplification_outcome));
291272
}
292273

@@ -297,15 +278,6 @@ impl<Config: endpoint::Config> Manager<Config> {
297278
return Err(DatagramDropReason::UnknownServerAddress);
298279
}
299280

300-
//= https://www.rfc-editor.org/rfc/rfc9000#section-9
301-
//# The design of QUIC relies on endpoints retaining a stable address
302-
//# for the duration of the handshake. An endpoint MUST NOT initiate
303-
//# connection migration before the handshake is confirmed, as defined
304-
//# in section 4.1.2 of [QUIC-TLS].
305-
if !handshake_confirmed {
306-
return Err(DatagramDropReason::ConnectionMigrationDuringHandshake);
307-
}
308-
309281
//= https://www.rfc-editor.org/rfc/rfc9000#section-9
310282
//# If the peer
311283
//# violates this requirement, the endpoint MUST either drop the incoming

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -765,7 +765,7 @@ fn test_adding_new_path() {
765765
// - call on_datagram_received with new remote address bit handshake_confirmed false
766766
//
767767
// Expectation:
768-
// - asset on_datagram_received errors
768+
// - assert on_datagram_received does not error
769769
// - assert we have one paths
770770
fn do_not_add_new_path_if_handshake_not_confirmed() {
771771
// Setup:
@@ -811,7 +811,7 @@ fn do_not_add_new_path_if_handshake_not_confirmed() {
811811
);
812812

813813
// Expectation:
814-
assert!(on_datagram_result.is_err());
814+
assert!(on_datagram_result.is_ok());
815815
assert!(manager.path(&new_addr).is_none());
816816
assert_eq!(manager.paths.len(), 1);
817817
}

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

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ use crate::{
1313
};
1414
use s2n_quic_core::{
1515
counter::{Counter, Saturating},
16-
event::{self, IntoEvent},
17-
frame, packet, random,
16+
event::{self, builder::DatagramDropReason, IntoEvent},
17+
frame,
18+
inet::DatagramInfo,
19+
packet, random,
1820
time::{timer, Timestamp},
1921
};
2022

@@ -229,6 +231,54 @@ impl<Config: endpoint::Config> Path<Config> {
229231
}
230232
}
231233

234+
#[inline]
235+
pub fn on_datagram_received(
236+
&mut self,
237+
path_handle: &Config::PathHandle,
238+
datagram: &DatagramInfo,
239+
valid_initial_received: bool,
240+
) -> Result<AmplificationOutcome, DatagramDropReason> {
241+
let source_cid_changed = datagram
242+
.source_connection_id
243+
.is_some_and(|scid| scid != self.peer_connection_id && valid_initial_received);
244+
245+
if source_cid_changed {
246+
//= https://www.rfc-editor.org/rfc/rfc9000#section-7.2
247+
//# Once a client has received a valid Initial packet from the server, it MUST
248+
//# discard any subsequent packet it receives on that connection with a
249+
//# different Source Connection ID.
250+
251+
//= https://www.rfc-editor.org/rfc/rfc9000#section-7.2
252+
//# Any further changes to the Destination Connection ID are only
253+
//# permitted if the values are taken from NEW_CONNECTION_ID frames; if
254+
//# subsequent Initial packets include a different Source Connection ID,
255+
//# they MUST be discarded.
256+
257+
return Err(DatagramDropReason::InvalidSourceConnectionId);
258+
}
259+
260+
// Update the address if it was resolved
261+
//
262+
// NOTE: We don't update the server address since this would cause the client to drop
263+
// packets from the server.
264+
265+
//= https://www.rfc-editor.org/rfc/rfc9000#section-9
266+
//# If a client receives packets from an unknown server address, the client MUST discard these packets.
267+
268+
//= https://www.rfc-editor.org/rfc/rfc9000#section-9
269+
//# If the peer sent the disable_active_migration transport parameter, an endpoint also MUST NOT send
270+
//# packets (including probing packets; see Section 9.1) from a different local address to the address
271+
//# the peer used during the handshake, unless the endpoint has acted on a preferred_address transport
272+
//# parameter from the peer.
273+
if Config::ENDPOINT_TYPE.is_client() {
274+
self.handle.maybe_update(path_handle);
275+
}
276+
277+
let amplification_outcome = self.on_bytes_received(datagram.payload_len);
278+
279+
Ok(amplification_outcome)
280+
}
281+
232282
#[inline]
233283
pub fn on_timeout<Pub: event::ConnectionPublisher>(
234284
&mut self,

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

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -225,15 +225,13 @@ fn ip_and_port_rebind_test() {
225225
#[derive(Default)]
226226
struct RebindPortBeforeHandshakeConfirmed {
227227
datagram_count: usize,
228-
changed_port: bool,
229228
}
230229

231230
const REBIND_PORT: u16 = 55555;
232231
impl Interceptor for RebindPortBeforeHandshakeConfirmed {
233232
fn intercept_rx_remote_address(&mut self, _subject: &Subject, addr: &mut RemoteAddress) {
234-
if self.datagram_count == 1 && !self.changed_port {
233+
if (1..5).contains(&self.datagram_count) {
235234
addr.set_port(REBIND_PORT);
236-
self.changed_port = true;
237235
}
238236
}
239237

@@ -279,14 +277,10 @@ fn rebind_before_handshake_confirmed() {
279277
.unwrap();
280278

281279
let datagram_dropped_events = datagram_dropped_events.lock().unwrap();
282-
283-
assert_eq!(1, datagram_dropped_events.len());
284-
let event = datagram_dropped_events.first().unwrap();
285-
assert!(matches!(
286-
event.reason,
287-
DatagramDropReason::ConnectionMigrationDuringHandshake { .. },
288-
));
289-
assert_eq!(REBIND_PORT, event.remote_addr.port());
280+
assert!(
281+
datagram_dropped_events.is_empty(),
282+
"the server should allow packets to be processed before the handshake completes"
283+
);
290284
}
291285

292286
// Changes the remote address to ipv4-mapped after the first packet

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,14 +169,12 @@ event_recorder!(
169169
use s2n_quic_core::event::api::DatagramDropReason;
170170
#[derive(Debug)]
171171
pub struct DatagramDroppedEvent {
172-
pub remote_addr: SocketAddr,
173172
pub reason: DatagramDropReason,
174173
}
175174

176175
impl<'a> From<&events::DatagramDropped<'a>> for DatagramDroppedEvent {
177176
fn from(value: &events::DatagramDropped<'a>) -> Self {
178177
DatagramDroppedEvent {
179-
remote_addr: value.remote_addr.to_string().parse().unwrap(),
180178
reason: value.reason.clone(),
181179
}
182180
}

0 commit comments

Comments
 (0)