fn handle_connection_migration()

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))
    }