Skip to content

Commit eace166

Browse files
authored
fix: ignore local address when considering path migration (#2458)
1 parent 75c64e4 commit eace166

File tree

5 files changed

+101
-32
lines changed

5 files changed

+101
-32
lines changed

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

+14-9
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33

44
use crate::{
55
event,
6-
inet::{IpV4Address, IpV6Address, SocketAddress, SocketAddressV4, SocketAddressV6},
6+
inet::{
7+
IpV4Address, IpV6Address, SocketAddress, SocketAddressV4, SocketAddressV6, Unspecified as _,
8+
},
79
};
810
use core::fmt;
911

@@ -161,6 +163,16 @@ macro_rules! impl_addr {
161163

162164
impl_addr!(LocalAddress);
163165

166+
impl LocalAddress {
167+
#[inline]
168+
pub fn maybe_update(&mut self, other: &Self) {
169+
// only update the local address if it's specified
170+
ensure!(!other.is_unspecified());
171+
172+
*self = *other;
173+
}
174+
}
175+
164176
impl_addr!(RemoteAddress);
165177

166178
impl Handle for RemoteAddress {
@@ -263,14 +275,7 @@ impl Handle for Tuple {
263275

264276
#[inline]
265277
fn maybe_update(&mut self, other: &Self) {
266-
if other.local_address.port() == 0 {
267-
return;
268-
}
269-
270-
// once we discover our path, or the port changes, update the address with the new information
271-
if self.local_address.port() != other.local_address.port() {
272-
self.local_address = other.local_address;
273-
}
278+
self.local_address.maybe_update(&other.local_address);
274279
}
275280
}
276281

quic/s2n-quic-platform/src/message/msg/handle.rs

+1-8
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,7 @@ impl path::Handle for Handle {
109109

110110
#[inline]
111111
fn maybe_update(&mut self, other: &Self) {
112-
if other.local_address.port() == 0 {
113-
return;
114-
}
115-
116-
// once we discover our path, or the port changes, update the address with the new information
117-
if self.local_address.port() != other.local_address.port() {
118-
self.local_address = other.local_address;
119-
}
112+
self.local_address.maybe_update(&other.local_address);
120113
}
121114
}
122115

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

+16-2
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,22 @@ impl<Config: endpoint::Config> Manager<Config> {
269269
return Err(DatagramDropReason::InvalidSourceConnectionId);
270270
}
271271

272-
// update the address if it was resolved
273-
path.handle.maybe_update(path_handle);
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+
280+
//= 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+
}
274288

275289
let amplification_outcome = path.on_bytes_received(datagram.payload_len);
276290
return Ok((id, amplification_outcome));

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

+7-13
Original file line numberDiff line numberDiff line change
@@ -540,21 +540,15 @@ impl<Config: endpoint::Config> Path<Config> {
540540

541541
/// Compare a Path based on its PathHandle.
542542
///
543-
/// In case the local_address on the connection is unknown and set to
544-
/// a default un-specified value only the remote_address is used
545-
/// to compare Paths.
546-
///
547-
/// In the case of the local endpoint being a client, the remote address is only used
548-
/// since the client might experience address rebinding.
543+
/// QUIC only considers the remote address when identifying paths
544+
//= https://www.rfc-editor.org/rfc/rfc9000#section-9.3
545+
//# Receiving a packet from a new peer address containing a non-probing frame
546+
//# indicates that the peer has migrated to that address.
549547
#[inline]
550548
fn eq_by_handle(&self, handle: &Config::PathHandle) -> bool {
551-
if Config::ENDPOINT_TYPE.is_client() || self.handle.local_address().port() == 0 {
552-
self.handle
553-
.remote_address()
554-
.unmapped_eq(&handle.remote_address())
555-
} else {
556-
self.handle.unmapped_eq(handle)
557-
}
549+
self.handle
550+
.remote_address()
551+
.unmapped_eq(&handle.remote_address())
558552
}
559553
}
560554

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

+63
Original file line numberDiff line numberDiff line change
@@ -447,3 +447,66 @@ fn rebind_blocked_port() {
447447
}
448448
}
449449
}
450+
451+
// Changes the local address after N packets
452+
#[derive(Default)]
453+
struct RebindAddrAfter {
454+
count: usize,
455+
}
456+
457+
impl Interceptor for RebindAddrAfter {
458+
fn intercept_rx_local_address(&mut self, _subject: &Subject, addr: &mut LocalAddress) {
459+
if self.count == 0 {
460+
addr.0 = rebind_port(rebind_ip(addr.0.into())).into();
461+
}
462+
}
463+
464+
fn intercept_rx_datagram<'a>(
465+
&mut self,
466+
_subject: &Subject,
467+
_datagram: &Datagram,
468+
payload: DecoderBufferMut<'a>,
469+
) -> DecoderBufferMut<'a> {
470+
self.count = self.count.saturating_sub(1);
471+
payload
472+
}
473+
}
474+
475+
/// Ensures that a datagram received from a client on a different server IP/port is still
476+
/// accepted.
477+
#[test]
478+
fn rebind_server_addr_before_handshake_confirmed() {
479+
let model = Model::default();
480+
let subscriber = recorder::DatagramDropped::new();
481+
let datagram_dropped_events = subscriber.events();
482+
483+
test(model, move |handle| {
484+
let server = Server::builder()
485+
.with_io(handle.builder().build()?)?
486+
.with_tls(SERVER_CERTS)?
487+
.with_event((tracing_events(), subscriber))?
488+
.with_random(Random::with_seed(456))?
489+
.with_packet_interceptor(RebindAddrAfter { count: 1 })?
490+
.start()?;
491+
492+
let client = Client::builder()
493+
.with_io(handle.builder().build()?)?
494+
.with_tls(certificates::CERT_PEM)?
495+
.with_event(tracing_events())?
496+
.with_random(Random::with_seed(456))?
497+
.start()?;
498+
499+
let addr = start_server(server)?;
500+
start_client(client, addr, Data::new(1000))?;
501+
Ok(addr)
502+
})
503+
.unwrap();
504+
505+
let datagram_dropped_events = datagram_dropped_events.lock().unwrap();
506+
let datagram_dropped_events = &datagram_dropped_events[..];
507+
508+
assert!(
509+
datagram_dropped_events.is_empty(),
510+
"s2n-quic should not drop packets with different server addrs {datagram_dropped_events:?}"
511+
);
512+
}

0 commit comments

Comments
 (0)