in quic/s2n-quic-transport/src/path/manager.rs [323:498]
fn handle_connection_migration<Pub: event::ConnectionPublisher>(
&mut self,
path_handle: &Config::PathHandle,
datagram: &DatagramInfo,
congestion_controller_endpoint: &mut Config::CongestionControllerEndpoint,
migration_validator: &mut Config::PathMigrationValidator,
mtu: &mut mtu::Manager<Config::Mtu>,
limits: &Limits,
publisher: &mut Pub,
) -> Result<(Id, AmplificationOutcome), DatagramDropReason> {
//= https://www.rfc-editor.org/rfc/rfc9000#section-9
//# Clients are responsible for initiating all migrations.
debug_assert!(Config::ENDPOINT_TYPE.is_server());
let remote_address = path_handle.remote_address();
let local_address = path_handle.local_address();
let active_local_addr = self.active_path().local_address();
let active_remote_addr = self.active_path().remote_address();
// TODO set alpn if available
let attempt: migration::Attempt = migration::AttemptBuilder {
active_path: event::builder::Path {
local_addr: active_local_addr.into_event(),
local_cid: self.active_path().local_connection_id.into_event(),
remote_addr: active_remote_addr.into_event(),
remote_cid: self.active_path().peer_connection_id.into_event(),
id: self.active_path_id().into_event(),
is_active: true,
}
.into_event(),
packet: migration::PacketInfoBuilder {
remote_address: &remote_address,
local_address: &local_address,
}
.into(),
}
.into();
match migration_validator.on_migration_attempt(&attempt) {
migration::Outcome::Allow => {
// no-op: allow the migration to continue
}
migration::Outcome::Deny(reason) => {
publisher.on_connection_migration_denied(reason.into_event());
return Err(DatagramDropReason::RejectedConnectionMigration {
reason: reason.into_event().reason,
});
}
_ => {
unimplemented!("unimplemented migration outcome");
}
}
// Determine which index will be used for the newly created path
//
// If a previously allocated path failed to contain an authenticated packet, we
// use that index instead of pushing on to the end.
let new_path_idx = if let Some(idx) = self.pending_packet_authentication {
idx as _
} else {
let idx = self.paths.len();
self.pending_packet_authentication = Some(idx as _);
idx
};
// TODO: Support deletion of old paths: https://github.com/aws/s2n-quic/issues/741
// The current path manager implementation does not delete or reuse indices
// in the path array. This can result in an unbounded number of paths. To prevent
// this we limit the max number of paths per connection.
if new_path_idx >= MAX_ALLOWED_PATHS {
return Err(DatagramDropReason::PathLimitExceeded);
}
let new_path_id = path_id(new_path_idx as u8);
//= https://www.rfc-editor.org/rfc/rfc9000#section-9.4
//= type=TODO
//# Because port-only changes are commonly the
//# result of NAT rebinding or other middlebox activity, the endpoint MAY
//# instead retain its congestion control state and round-trip estimate
//# in those cases instead of reverting to initial values.
//= https://www.rfc-editor.org/rfc/rfc9000#section-9.4
//# On confirming a peer's ownership of its new address, an endpoint MUST
//# immediately reset the congestion controller and round-trip time
//# estimator for the new path to initial values (see Appendices A.3 and
//# B.3 of [QUIC-RECOVERY]) unless the only change in the peer's address
//# is its port number.
// Since we maintain a separate congestion controller and round-trip time
// estimator for the new path, and they are initialized with initial values,
// we do not need to reset congestion controller and round-trip time estimator
// again on confirming the peer's ownership of its new address.
let rtt = self
.active_path()
.rtt_estimator
.for_new_path(limits.initial_round_trip_time());
let mtu_config = mtu.config(&remote_address).map_err(|_err| {
event::builder::DatagramDropReason::InvalidMtuConfiguration {
endpoint_mtu_config: mtu.endpoint_config().into_event(),
}
})?;
let path_info = congestion_controller::PathInfo::new(&mtu_config, &remote_address);
let cc = congestion_controller_endpoint.new_congestion_controller(path_info);
let peer_connection_id = {
if self.active_path().local_connection_id != datagram.destination_connection_id {
//= https://www.rfc-editor.org/rfc/rfc9000#section-9.5
//# Similarly, an endpoint MUST NOT reuse a connection ID when sending to
//# more than one destination address.
// The peer changed destination CIDs, so we will attempt to switch to a new
// destination CID as well. This could still just be a NAT rebind though, so
// we continue with the existing destination CID if there isn't a new one
// available.
self.peer_id_registry
.consume_new_id_for_new_path()
.unwrap_or(self.active_path().peer_connection_id)
} else {
//= https://www.rfc-editor.org/rfc/rfc9000#section-9.5
//# Due to network changes outside
//# the control of its peer, an endpoint might receive packets from a new
//# source address with the same Destination Connection ID field value,
//# in which case it MAY continue to use the current connection ID with
//# the new remote address while still sending from the same local
//# address.
self.active_path().peer_connection_id
}
};
//= https://www.rfc-editor.org/rfc/rfc9000#section-9.3.1
//# Until a peer's address is deemed valid, an endpoint limits
//# the amount of data it sends to that address; see Section 8.
//
//= https://www.rfc-editor.org/rfc/rfc9000#section-9.3
//# An endpoint MAY send data to an unvalidated peer address, but it MUST
//# protect against potential attacks as described in Sections 9.3.1 and
//# 9.3.2.
//
// New paths for a Server endpoint start in AmplificationLimited state until they are validated.
let mut path = Path::new(
*path_handle,
peer_connection_id,
datagram.destination_connection_id,
rtt,
cc,
true,
mtu_config,
limits.anti_amplification_multiplier(),
);
let amplification_outcome = path.on_bytes_received(datagram.payload_len);
let active_path = self.active_path();
let active_path_id = self.active_path_id();
publisher.on_path_created(event::builder::PathCreated {
active: path_event!(active_path, active_path_id),
new: path_event!(path, new_path_id),
});
publisher.on_mtu_updated(event::builder::MtuUpdated {
path_id: new_path_id.into_event(),
mtu: path.mtu_controller.max_datagram_size() as u16,
cause: MtuUpdatedCause::NewPath,
search_complete: path.mtu_controller.is_search_completed(),
});
// create a new path
if new_path_idx < self.paths.len() {
self.paths[new_path_idx] = path;
} else {
self.paths.push(path);
}
Ok((new_path_id, amplification_outcome))
}