From 7f42ecdc34b07b0c2f98b1794d4eeed38de1b386 Mon Sep 17 00:00:00 2001 From: Quentin De Coninck Date: Thu, 7 Nov 2024 16:40:26 +0100 Subject: [PATCH] do not enable app to abandon last active path --- quiche/src/lib.rs | 7 +++++++ quiche/src/path.rs | 18 +++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/quiche/src/lib.rs b/quiche/src/lib.rs index c5cedd1870..73a4511a9f 100644 --- a/quiche/src/lib.rs +++ b/quiche/src/lib.rs @@ -594,6 +594,9 @@ pub enum Error { /// No spare Path Identifier to perform the operation in multipath. OutOfPathId, + + /// There is no more path available on the connection. + NoMorePath, } /// QUIC error codes sent on the wire. @@ -708,6 +711,7 @@ impl Error { Error::UnavailablePath => -21, Error::PathIdViolation => -22, Error::OutOfPathId => -23, + Error::NoMorePath => -24, } } } @@ -6572,8 +6576,11 @@ impl Connection { /// If the path specified by the 4-tuple does not exist, returns an /// [`Done`]. /// + /// If the path to abandon is the only active one, returns a [`NoMorePath`]. + /// /// [`InvalidState`]: enum.Error.html#InvalidState /// [`Done`]: enum.Error.html#Done + /// [`NoMorePath`]: enum.Error.html#NoMorePath pub fn abandon_path( &mut self, local: SocketAddr, peer: SocketAddr, err_code: u64, ) -> Result<()> { diff --git a/quiche/src/path.rs b/quiche/src/path.rs index 08a4846cf3..e32eefad42 100644 --- a/quiche/src/path.rs +++ b/quiche/src/path.rs @@ -1120,23 +1120,39 @@ impl PathMap { self.multipath = v; } + /// Returns the number of active paths. + fn count_active_paths(&self) -> usize { + self.paths.iter().filter(|p| p.1.active()).count() + } + /// Changes the state of the path with the identifier `path_id` according to /// the provided `PathRequest`. /// /// This API is only usable when multipath extensions are enabled. /// Otherwise, it raises an [`InvalidState`]. /// + /// In case the request closes the last active path, returns a + /// [`NoMorePath`]. + /// /// In case the request is invalid, returns an [`InvalidState`]. /// /// [`InvalidState`]: enum.Error.html#variant.InvalidState + /// [`NoMorePath`]: enum.Error.html#variant.NoMorePath pub fn request( &mut self, path_id: usize, request: PathRequest, ) -> Result<()> { if !self.multipath { return Err(Error::InvalidState); } - let path = self.get_mut(path_id)?; let requested_state = request.requested_state(); + if let PathState::Closing(_) = requested_state { + if self.count_active_paths() == 1 && + path_id == self.get_active_path_id()? + { + return Err(Error::NoMorePath); + } + } + let path = self.get_mut(path_id)?; path.set_state(requested_state)?; if path.is_closing() { self.mark_path_abandon(path_id, true);