From 015be0d7b765962be73e8fff7edbf2da79d9762e Mon Sep 17 00:00:00 2001 From: flub Date: Thu, 18 Jun 2026 09:35:58 +0000 Subject: [PATCH] deploy: fd387aeaa232d8bb5fa76b046dfbb4d9497677d5 --- pr/707/docs/help.html | 2 +- pr/707/docs/noq/enum.ClosePathError.html | 6 +- pr/707/docs/noq/enum.ConnectionError.html | 6 +- pr/707/docs/noq/enum.PathError.html | 6 +- pr/707/docs/noq/enum.PathEvent.html | 6 +- pr/707/docs/noq/enum.PathStatus.html | 6 +- pr/707/docs/noq/enum.SetPathStatusError.html | 6 +- pr/707/docs/noq/struct.ClosedPath.html | 6 +- pr/707/docs/noq/trait.NetworkChangeHint.html | 4 +- .../docs/noq_proto/enum.ClosePathError.html | 6 +- .../docs/noq_proto/enum.ConnectionError.html | 6 +- pr/707/docs/noq_proto/enum.Event.html | 4 +- .../noq_proto/enum.PathAbandonReason.html | 6 +- pr/707/docs/noq_proto/enum.PathError.html | 6 +- pr/707/docs/noq_proto/enum.PathEvent.html | 6 +- pr/707/docs/noq_proto/enum.PathStatus.html | 6 +- .../noq_proto/enum.SetPathStatusError.html | 6 +- pr/707/docs/noq_proto/struct.ClosedPath.html | 6 +- pr/707/docs/noq_proto/struct.Connection.html | 102 +- .../struct.MultipathNotNegotiated.html | 4 +- .../docs/noq_proto/struct.RttEstimator.html | 10 +- .../docs/noq_proto/struct.TransportError.html | 2 +- .../noq_proto/trait.NetworkChangeHint.html | 4 +- .../{1ee427fe586f.js => 81eb57f53ae8.js} | 2 +- .../{dc6a9ddb2aa5.js => 769efb721a80.js} | 2 +- .../path/{a96c5934a8c0.js => 813227ed5be5.js} | 2 +- pr/707/docs/search.index/root.js | 2 +- .../type/{77cc909be087.js => ecaf0e68bc4f.js} | 2 +- pr/707/docs/settings.html | 2 +- .../docs/src/noq_proto/connection/mod.rs.html | 14761 ++++++++-------- .../src/noq_proto/connection/paths.rs.html | 1811 +- .../src/noq_proto/connection/spaces.rs.html | 2891 +-- 32 files changed, 9867 insertions(+), 9830 deletions(-) rename pr/707/docs/search.index/entry/{1ee427fe586f.js => 81eb57f53ae8.js} (94%) rename pr/707/docs/search.index/function/{dc6a9ddb2aa5.js => 769efb721a80.js} (98%) rename pr/707/docs/search.index/path/{a96c5934a8c0.js => 813227ed5be5.js} (96%) rename pr/707/docs/search.index/type/{77cc909be087.js => ecaf0e68bc4f.js} (99%) diff --git a/pr/707/docs/help.html b/pr/707/docs/help.html index 09d0ca8e7..cb140c09e 100644 --- a/pr/707/docs/help.html +++ b/pr/707/docs/help.html @@ -1 +1 @@ -Help

All

Rustdoc help

Back
\ No newline at end of file +Help

All

Rustdoc help

Back
\ No newline at end of file diff --git a/pr/707/docs/noq/enum.ClosePathError.html b/pr/707/docs/noq/enum.ClosePathError.html index d2489eb8f..3601f69b1 100644 --- a/pr/707/docs/noq/enum.ClosePathError.html +++ b/pr/707/docs/noq/enum.ClosePathError.html @@ -1,4 +1,4 @@ -ClosePathError in noq - Rust

ClosePathError

Enum ClosePathError 

Source
pub enum ClosePathError {
+ClosePathError in noq - Rust

ClosePathError

Enum ClosePathError 

Source
pub enum ClosePathError {
     MultipathNotNegotiated,
     ClosedPath,
     LastOpenPath,
@@ -7,8 +7,8 @@
 
§

ClosedPath

The path is already closed or was never opened

§

LastOpenPath

Cannot close the last remaining open path via the local API.

Use Connection::close to end the connection instead.

-

Trait Implementations§

Source§

impl Clone for ClosePathError

Source§

fn clone(&self) -> ClosePathError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosePathError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for ClosePathError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Error for ClosePathError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for ClosePathError

Source§

fn eq(&self, other: &ClosePathError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Eq for ClosePathError

Source§

impl StructuralPartialEq for ClosePathError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for ClosePathError

Source§

fn clone(&self) -> ClosePathError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosePathError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for ClosePathError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Error for ClosePathError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for ClosePathError

Source§

fn eq(&self, other: &ClosePathError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ClosePathError

Source§

impl StructuralPartialEq for ClosePathError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/707/docs/noq/enum.ConnectionError.html b/pr/707/docs/noq/enum.ConnectionError.html index a3241d9c6..7239e509a 100644 --- a/pr/707/docs/noq/enum.ConnectionError.html +++ b/pr/707/docs/noq/enum.ConnectionError.html @@ -1,4 +1,4 @@ -ConnectionError in noq - Rust

ConnectionError

Enum ConnectionError 

Source
pub enum ConnectionError {
+ConnectionError in noq - Rust

ConnectionError

Enum ConnectionError 

Source
pub enum ConnectionError {
     VersionMismatch,
     TransportError(Error),
     ConnectionClosed(ConnectionClose),
@@ -20,8 +20,8 @@ and §

LocallyClosed

The local application closed the connection

§

CidsExhausted

The connection could not be created because not enough of the CID space is available

Try using longer connection IDs.

-

Trait Implementations§

Source§

impl Clone for ConnectionError

Source§

fn clone(&self) -> ConnectionError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for ConnectionError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Error for ConnectionError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<Close> for ConnectionError

Source§

fn from(x: Close) -> ConnectionError

Converts to this type from the input type.
Source§

impl From<CloseReason> for ConnectionError

Source§

fn from(value: CloseReason) -> ConnectionError

Converts to this type from the input type.
Source§

impl From<ConnectionError> for ReadError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for ResetError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for SendDatagramError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for StoppedError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for WriteError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for ConnectionError

Source§

fn from(source: Error) -> ConnectionError

Converts to this type from the input type.
Source§

impl PartialEq for ConnectionError

Source§

fn eq(&self, other: &ConnectionError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Eq for ConnectionError

Source§

impl StructuralPartialEq for ConnectionError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for ConnectionError

Source§

fn clone(&self) -> ConnectionError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for ConnectionError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Error for ConnectionError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<Close> for ConnectionError

Source§

fn from(x: Close) -> ConnectionError

Converts to this type from the input type.
Source§

impl From<CloseReason> for ConnectionError

Source§

fn from(value: CloseReason) -> ConnectionError

Converts to this type from the input type.
Source§

impl From<ConnectionError> for ReadError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for ResetError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for SendDatagramError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for StoppedError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for WriteError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for ConnectionError

Source§

fn from(source: Error) -> ConnectionError

Converts to this type from the input type.
Source§

impl PartialEq for ConnectionError

Source§

fn eq(&self, other: &ConnectionError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ConnectionError

Source§

impl StructuralPartialEq for ConnectionError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/707/docs/noq/enum.PathError.html b/pr/707/docs/noq/enum.PathError.html index c98a850a1..abfcea53d 100644 --- a/pr/707/docs/noq/enum.PathError.html +++ b/pr/707/docs/noq/enum.PathError.html @@ -1,4 +1,4 @@ -PathError in noq - Rust

PathError

Enum PathError 

Source
pub enum PathError {
+PathError in noq - Rust

PathError

Enum PathError 

Source
pub enum PathError {
     MultipathNotNegotiated,
     ServerSideNotAllowed,
     MaxPathIdReached,
@@ -12,8 +12,8 @@
 
§

RemoteCidsExhausted

No remote CIDs available to open a new path

§

ValidationFailed

Path could not be validated and will be abandoned

§

InvalidRemoteAddress(SocketAddr)

The remote address for the path is not supported by the endpoint

-

Trait Implementations§

Source§

impl Clone for PathError

Source§

fn clone(&self) -> PathError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for PathError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Error for PathError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for PathError

Source§

fn eq(&self, other: &PathError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Copy for PathError

Source§

impl Eq for PathError

Source§

impl StructuralPartialEq for PathError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for PathError

Source§

fn clone(&self) -> PathError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for PathError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Error for PathError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for PathError

Source§

fn eq(&self, other: &PathError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for PathError

Source§

impl Eq for PathError

Source§

impl StructuralPartialEq for PathError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/707/docs/noq/enum.PathEvent.html b/pr/707/docs/noq/enum.PathEvent.html index 92f53e403..cd4eadd8b 100644 --- a/pr/707/docs/noq/enum.PathEvent.html +++ b/pr/707/docs/noq/enum.PathEvent.html @@ -1,4 +1,4 @@ -PathEvent in noq - Rust

PathEvent

Enum PathEvent 

Source
#[non_exhaustive]
pub enum PathEvent { +PathEvent in noq - Rust

PathEvent

Enum PathEvent 

Source
#[non_exhaustive]
pub enum PathEvent {
#[non_exhaustive]
Established { id: PathId, }, @@ -41,8 +41,8 @@ changes the status.

Fields

This variant is marked as non-exhaustive
Non-exhaustive enum variants could have additional fields added in future. Therefore, non-exhaustive enum variants cannot be constructed in external crates and cannot be matched against.
§id: PathId

Path over which the observed address was reported, PathId::ZERO when multipath is not negotiated

§addr: SocketAddr

The address observed by the remote over this path

-

Trait Implementations§

Source§

impl Clone for PathEvent

Source§

fn clone(&self) -> PathEvent

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathEvent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl PartialEq for PathEvent

Source§

fn eq(&self, other: &PathEvent) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Eq for PathEvent

Source§

impl StructuralPartialEq for PathEvent

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for PathEvent

Source§

fn clone(&self) -> PathEvent

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathEvent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl PartialEq for PathEvent

Source§

fn eq(&self, other: &PathEvent) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for PathEvent

Source§

impl StructuralPartialEq for PathEvent

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/707/docs/noq/enum.PathStatus.html b/pr/707/docs/noq/enum.PathStatus.html index 986bdf5fd..819a51b2f 100644 --- a/pr/707/docs/noq/enum.PathStatus.html +++ b/pr/707/docs/noq/enum.PathStatus.html @@ -1,4 +1,4 @@ -PathStatus in noq - Rust

PathStatus

Enum PathStatus 

Source
pub enum PathStatus {
+PathStatus in noq - Rust

PathStatus

Enum PathStatus 

Source
pub enum PathStatus {
     Available,
     Backup,
 }
Expand description

The QUIC-MULTIPATH path status

@@ -10,8 +10,8 @@ capacity.

§

Backup

Paths marked as backup will only be used if there are no available paths

If the max_idle_timeout is specified the path will be kept alive so that it does not expire.

-

Trait Implementations§

Source§

impl Clone for PathStatus

Source§

fn clone(&self) -> PathStatus

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathStatus

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for PathStatus

Source§

fn default() -> PathStatus

Returns the “default value” for a type. Read more
Source§

impl PartialEq for PathStatus

Source§

fn eq(&self, other: &PathStatus) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Copy for PathStatus

Source§

impl Eq for PathStatus

Source§

impl StructuralPartialEq for PathStatus

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for PathStatus

Source§

fn clone(&self) -> PathStatus

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathStatus

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for PathStatus

Source§

fn default() -> PathStatus

Returns the “default value” for a type. Read more
Source§

impl PartialEq for PathStatus

Source§

fn eq(&self, other: &PathStatus) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for PathStatus

Source§

impl Eq for PathStatus

Source§

impl StructuralPartialEq for PathStatus

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/707/docs/noq/enum.SetPathStatusError.html b/pr/707/docs/noq/enum.SetPathStatusError.html index feb5f8759..767305d43 100644 --- a/pr/707/docs/noq/enum.SetPathStatusError.html +++ b/pr/707/docs/noq/enum.SetPathStatusError.html @@ -1,11 +1,11 @@ -SetPathStatusError in noq - Rust

SetPathStatusError

Enum SetPathStatusError 

Source
pub enum SetPathStatusError {
+SetPathStatusError in noq - Rust

SetPathStatusError

Enum SetPathStatusError 

Source
pub enum SetPathStatusError {
     ClosedPath,
     MultipathNotNegotiated,
 }
Expand description

Error from setting path status

Variants§

§

ClosedPath

Error indicating that a path has not been opened or has already been abandoned

§

MultipathNotNegotiated

Error indicating that this operation requires multipath to be negotiated whereas it hasn’t been

-

Trait Implementations§

Source§

impl Clone for SetPathStatusError

Source§

fn clone(&self) -> SetPathStatusError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SetPathStatusError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for SetPathStatusError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Error for SetPathStatusError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for SetPathStatusError

Source§

fn eq(&self, other: &SetPathStatusError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Eq for SetPathStatusError

Source§

impl StructuralPartialEq for SetPathStatusError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for SetPathStatusError

Source§

fn clone(&self) -> SetPathStatusError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SetPathStatusError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for SetPathStatusError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Error for SetPathStatusError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for SetPathStatusError

Source§

fn eq(&self, other: &SetPathStatusError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for SetPathStatusError

Source§

impl StructuralPartialEq for SetPathStatusError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/707/docs/noq/struct.ClosedPath.html b/pr/707/docs/noq/struct.ClosedPath.html index c9dbbea89..d1e60aa63 100644 --- a/pr/707/docs/noq/struct.ClosedPath.html +++ b/pr/707/docs/noq/struct.ClosedPath.html @@ -1,6 +1,6 @@ -ClosedPath in noq - Rust

ClosedPath

Struct ClosedPath 

Source
pub struct ClosedPath { /* private fields */ }
Expand description

Error indicating that a path has not been opened or has already been abandoned

-

Trait Implementations§

Source§

impl Clone for ClosedPath

Source§

fn clone(&self) -> ClosedPath

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosedPath

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for ClosedPath

Source§

fn default() -> ClosedPath

Returns the “default value” for a type. Read more
Source§

impl Display for ClosedPath

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Error for ClosedPath

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for ClosedPath

Source§

fn eq(&self, other: &ClosedPath) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Eq for ClosedPath

Source§

impl StructuralPartialEq for ClosedPath

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +ClosedPath in noq - Rust

ClosedPath

Struct ClosedPath 

Source
pub struct ClosedPath { /* private fields */ }
Expand description

Error indicating that a path has not been opened or has already been abandoned

+

Trait Implementations§

Source§

impl Clone for ClosedPath

Source§

fn clone(&self) -> ClosedPath

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosedPath

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for ClosedPath

Source§

fn default() -> ClosedPath

Returns the “default value” for a type. Read more
Source§

impl Display for ClosedPath

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Error for ClosedPath

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for ClosedPath

Source§

fn eq(&self, other: &ClosedPath) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ClosedPath

Source§

impl StructuralPartialEq for ClosedPath

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/707/docs/noq/trait.NetworkChangeHint.html b/pr/707/docs/noq/trait.NetworkChangeHint.html index e8deda4fe..35c9ea7ff 100644 --- a/pr/707/docs/noq/trait.NetworkChangeHint.html +++ b/pr/707/docs/noq/trait.NetworkChangeHint.html @@ -1,4 +1,4 @@ -NetworkChangeHint in noq - Rust

NetworkChangeHint

Trait NetworkChangeHint 

Source
pub trait NetworkChangeHint: Debug + 'static {
+NetworkChangeHint in noq - Rust

NetworkChangeHint

Trait NetworkChangeHint 

Source
pub trait NetworkChangeHint: Debug + 'static {
     // Required method
     fn is_path_recoverable(
         &self,
@@ -6,7 +6,7 @@
         network_path: FourTuple,
     ) -> bool;
 }
Expand description

Hints when the caller identifies a network change.

-

Required Methods§

Source

fn is_path_recoverable(&self, path_id: PathId, network_path: FourTuple) -> bool

Inform the connection if a path may recover after a network change.

+

Required Methods§

Source

fn is_path_recoverable(&self, path_id: PathId, network_path: FourTuple) -> bool

Inform the connection if a path may recover after a network change.

After network changes, paths may not be recoverable. In this case, waiting for the path to become idle may take longer than what is desirable. If Self::is_path_recoverable returns false, a multipath-enabled, client-side connection will establish a new path to diff --git a/pr/707/docs/noq_proto/enum.ClosePathError.html b/pr/707/docs/noq_proto/enum.ClosePathError.html index 603d224dd..9d9201e26 100644 --- a/pr/707/docs/noq_proto/enum.ClosePathError.html +++ b/pr/707/docs/noq_proto/enum.ClosePathError.html @@ -1,4 +1,4 @@ -ClosePathError in noq_proto - Rust

ClosePathError

Enum ClosePathError 

Source
pub enum ClosePathError {
+ClosePathError in noq_proto - Rust

ClosePathError

Enum ClosePathError 

Source
pub enum ClosePathError {
     MultipathNotNegotiated,
     ClosedPath,
     LastOpenPath,
@@ -7,8 +7,8 @@
 
§

ClosedPath

The path is already closed or was never opened

§

LastOpenPath

Cannot close the last remaining open path via the local API.

Use Connection::close to end the connection instead.

-

Trait Implementations§

Source§

impl Clone for ClosePathError

Source§

fn clone(&self) -> ClosePathError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosePathError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ClosePathError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ClosePathError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for ClosePathError

Source§

fn eq(&self, other: &ClosePathError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Eq for ClosePathError

Source§

impl StructuralPartialEq for ClosePathError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for ClosePathError

Source§

fn clone(&self) -> ClosePathError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosePathError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ClosePathError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ClosePathError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for ClosePathError

Source§

fn eq(&self, other: &ClosePathError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ClosePathError

Source§

impl StructuralPartialEq for ClosePathError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/707/docs/noq_proto/enum.ConnectionError.html b/pr/707/docs/noq_proto/enum.ConnectionError.html index 8c1432156..7f2b59a12 100644 --- a/pr/707/docs/noq_proto/enum.ConnectionError.html +++ b/pr/707/docs/noq_proto/enum.ConnectionError.html @@ -1,4 +1,4 @@ -ConnectionError in noq_proto - Rust

ConnectionError

Enum ConnectionError 

Source
pub enum ConnectionError {
+ConnectionError in noq_proto - Rust

ConnectionError

Enum ConnectionError 

Source
pub enum ConnectionError {
     VersionMismatch,
     TransportError(TransportError),
     ConnectionClosed(ConnectionClose),
@@ -20,8 +20,8 @@ and §

LocallyClosed

The local application closed the connection

§

CidsExhausted

The connection could not be created because not enough of the CID space is available

Try using longer connection IDs.

-

Trait Implementations§

Source§

impl Clone for ConnectionError

Source§

fn clone(&self) -> ConnectionError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ConnectionError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ConnectionError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ConnectionError> for Error

Source§

fn from(x: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for ConnectionError

Source§

fn from(source: TransportError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ConnectionError

Source§

fn eq(&self, other: &ConnectionError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Eq for ConnectionError

Source§

impl StructuralPartialEq for ConnectionError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for ConnectionError

Source§

fn clone(&self) -> ConnectionError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ConnectionError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ConnectionError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ConnectionError> for Error

Source§

fn from(x: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for ConnectionError

Source§

fn from(source: TransportError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ConnectionError

Source§

fn eq(&self, other: &ConnectionError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ConnectionError

Source§

impl StructuralPartialEq for ConnectionError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/707/docs/noq_proto/enum.Event.html b/pr/707/docs/noq_proto/enum.Event.html index 7e4bed9cd..3c0fb70ed 100644 --- a/pr/707/docs/noq_proto/enum.Event.html +++ b/pr/707/docs/noq_proto/enum.Event.html @@ -1,4 +1,4 @@ -Event in noq_proto - Rust

Event

Enum Event 

Source
pub enum Event {
+Event in noq_proto - Rust

Event

Enum Event 

Source
pub enum Event {
     HandshakeDataReady,
     Connected,
     HandshakeConfirmed,
@@ -25,7 +25,7 @@ fail with §

DatagramsUnblocked

One or more application datagrams have been sent after blocking

§

Path(PathEvent)

(Multi)Path events

§

NatTraversal(Event)

n0’s nat traversal events

-

Trait Implementations§

Source§

impl Debug for Event

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<PathEvent> for Event

Source§

fn from(source: PathEvent) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

§

impl !Freeze for Event

§

impl !RefUnwindSafe for Event

§

impl Send for Event

§

impl Sync for Event

§

impl Unpin for Event

§

impl !UnwindSafe for Event

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Debug for Event

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<PathEvent> for Event

Source§

fn from(source: PathEvent) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

§

impl !Freeze for Event

§

impl !RefUnwindSafe for Event

§

impl Send for Event

§

impl Sync for Event

§

impl Unpin for Event

§

impl !UnwindSafe for Event

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

diff --git a/pr/707/docs/noq_proto/enum.PathAbandonReason.html b/pr/707/docs/noq_proto/enum.PathAbandonReason.html index c94ad0496..f561ecddd 100644 --- a/pr/707/docs/noq_proto/enum.PathAbandonReason.html +++ b/pr/707/docs/noq_proto/enum.PathAbandonReason.html @@ -1,4 +1,4 @@ -PathAbandonReason in noq_proto - Rust

PathAbandonReason

Enum PathAbandonReason 

Source
pub enum PathAbandonReason {
+PathAbandonReason in noq_proto - Rust

PathAbandonReason

Enum PathAbandonReason 

Source
pub enum PathAbandonReason {
     ApplicationClosed {
         error_code: VarInt,
     },
@@ -16,8 +16,8 @@
 
§

UnusableAfterNetworkChange

The path became unusable after a local network change.

§

RemoteAbandoned

The remote closed the path.

Fields

§error_code: VarInt

The error that was sent with the abandon frame.

-

Trait Implementations§

Source§

impl Clone for PathAbandonReason

Source§

fn clone(&self) -> PathAbandonReason

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathAbandonReason

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for PathAbandonReason

Source§

fn eq(&self, other: &PathAbandonReason) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Eq for PathAbandonReason

Source§

impl StructuralPartialEq for PathAbandonReason

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for PathAbandonReason

Source§

fn clone(&self) -> PathAbandonReason

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathAbandonReason

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for PathAbandonReason

Source§

fn eq(&self, other: &PathAbandonReason) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for PathAbandonReason

Source§

impl StructuralPartialEq for PathAbandonReason

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/707/docs/noq_proto/enum.PathError.html b/pr/707/docs/noq_proto/enum.PathError.html index ec9d4bd84..1ecb6cefd 100644 --- a/pr/707/docs/noq_proto/enum.PathError.html +++ b/pr/707/docs/noq_proto/enum.PathError.html @@ -1,4 +1,4 @@ -PathError in noq_proto - Rust

PathError

Enum PathError 

Source
pub enum PathError {
+PathError in noq_proto - Rust

PathError

Enum PathError 

Source
pub enum PathError {
     MultipathNotNegotiated,
     ServerSideNotAllowed,
     MaxPathIdReached,
@@ -12,8 +12,8 @@
 
§

RemoteCidsExhausted

No remote CIDs available to open a new path

§

ValidationFailed

Path could not be validated and will be abandoned

§

InvalidRemoteAddress(SocketAddr)

The remote address for the path is not supported by the endpoint

-

Trait Implementations§

Source§

impl Clone for PathError

Source§

fn clone(&self) -> PathError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for PathError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for PathError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for PathError

Source§

fn eq(&self, other: &PathError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Copy for PathError

Source§

impl Eq for PathError

Source§

impl StructuralPartialEq for PathError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for PathError

Source§

fn clone(&self) -> PathError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for PathError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for PathError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for PathError

Source§

fn eq(&self, other: &PathError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for PathError

Source§

impl Eq for PathError

Source§

impl StructuralPartialEq for PathError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/707/docs/noq_proto/enum.PathEvent.html b/pr/707/docs/noq_proto/enum.PathEvent.html index 139dc5da8..b9c10501d 100644 --- a/pr/707/docs/noq_proto/enum.PathEvent.html +++ b/pr/707/docs/noq_proto/enum.PathEvent.html @@ -1,4 +1,4 @@ -PathEvent in noq_proto - Rust

PathEvent

Enum PathEvent 

Source
#[non_exhaustive]
pub enum PathEvent { +PathEvent in noq_proto - Rust

PathEvent

Enum PathEvent 

Source
#[non_exhaustive]
pub enum PathEvent {
#[non_exhaustive]
Established { id: PathId, }, @@ -41,8 +41,8 @@ changes the status.

Fields

This variant is marked as non-exhaustive
Non-exhaustive enum variants could have additional fields added in future. Therefore, non-exhaustive enum variants cannot be constructed in external crates and cannot be matched against.
§id: PathId

Path over which the observed address was reported, PathId::ZERO when multipath is not negotiated

§addr: SocketAddr

The address observed by the remote over this path

-

Trait Implementations§

Source§

impl Clone for PathEvent

Source§

fn clone(&self) -> PathEvent

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathEvent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<PathEvent> for Event

Source§

fn from(source: PathEvent) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for PathEvent

Source§

fn eq(&self, other: &PathEvent) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Eq for PathEvent

Source§

impl StructuralPartialEq for PathEvent

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for PathEvent

Source§

fn clone(&self) -> PathEvent

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathEvent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<PathEvent> for Event

Source§

fn from(source: PathEvent) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for PathEvent

Source§

fn eq(&self, other: &PathEvent) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for PathEvent

Source§

impl StructuralPartialEq for PathEvent

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/707/docs/noq_proto/enum.PathStatus.html b/pr/707/docs/noq_proto/enum.PathStatus.html index 266984262..571f4c611 100644 --- a/pr/707/docs/noq_proto/enum.PathStatus.html +++ b/pr/707/docs/noq_proto/enum.PathStatus.html @@ -1,4 +1,4 @@ -PathStatus in noq_proto - Rust

PathStatus

Enum PathStatus 

Source
pub enum PathStatus {
+PathStatus in noq_proto - Rust

PathStatus

Enum PathStatus 

Source
pub enum PathStatus {
     Available,
     Backup,
 }
Expand description

The QUIC-MULTIPATH path status

@@ -10,8 +10,8 @@ capacity.

§

Backup

Paths marked as backup will only be used if there are no available paths

If the max_idle_timeout is specified the path will be kept alive so that it does not expire.

-

Trait Implementations§

Source§

impl Clone for PathStatus

Source§

fn clone(&self) -> PathStatus

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathStatus

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for PathStatus

Source§

fn default() -> PathStatus

Returns the “default value” for a type. Read more
Source§

impl PartialEq for PathStatus

Source§

fn eq(&self, other: &PathStatus) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Copy for PathStatus

Source§

impl Eq for PathStatus

Source§

impl StructuralPartialEq for PathStatus

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for PathStatus

Source§

fn clone(&self) -> PathStatus

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathStatus

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for PathStatus

Source§

fn default() -> PathStatus

Returns the “default value” for a type. Read more
Source§

impl PartialEq for PathStatus

Source§

fn eq(&self, other: &PathStatus) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for PathStatus

Source§

impl Eq for PathStatus

Source§

impl StructuralPartialEq for PathStatus

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/707/docs/noq_proto/enum.SetPathStatusError.html b/pr/707/docs/noq_proto/enum.SetPathStatusError.html index 4a2ec2d18..45a05e09f 100644 --- a/pr/707/docs/noq_proto/enum.SetPathStatusError.html +++ b/pr/707/docs/noq_proto/enum.SetPathStatusError.html @@ -1,11 +1,11 @@ -SetPathStatusError in noq_proto - Rust

SetPathStatusError

Enum SetPathStatusError 

Source
pub enum SetPathStatusError {
+SetPathStatusError in noq_proto - Rust

SetPathStatusError

Enum SetPathStatusError 

Source
pub enum SetPathStatusError {
     ClosedPath,
     MultipathNotNegotiated,
 }
Expand description

Error from setting path status

Variants§

§

ClosedPath

Error indicating that a path has not been opened or has already been abandoned

§

MultipathNotNegotiated

Error indicating that this operation requires multipath to be negotiated whereas it hasn’t been

-

Trait Implementations§

Source§

impl Clone for SetPathStatusError

Source§

fn clone(&self) -> SetPathStatusError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SetPathStatusError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for SetPathStatusError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for SetPathStatusError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for SetPathStatusError

Source§

fn eq(&self, other: &SetPathStatusError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Eq for SetPathStatusError

Source§

impl StructuralPartialEq for SetPathStatusError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for SetPathStatusError

Source§

fn clone(&self) -> SetPathStatusError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SetPathStatusError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for SetPathStatusError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for SetPathStatusError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for SetPathStatusError

Source§

fn eq(&self, other: &SetPathStatusError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for SetPathStatusError

Source§

impl StructuralPartialEq for SetPathStatusError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/707/docs/noq_proto/struct.ClosedPath.html b/pr/707/docs/noq_proto/struct.ClosedPath.html index 3190790d2..8616e34fc 100644 --- a/pr/707/docs/noq_proto/struct.ClosedPath.html +++ b/pr/707/docs/noq_proto/struct.ClosedPath.html @@ -1,6 +1,6 @@ -ClosedPath in noq_proto - Rust

ClosedPath

Struct ClosedPath 

Source
pub struct ClosedPath { /* private fields */ }
Expand description

Error indicating that a path has not been opened or has already been abandoned

-

Trait Implementations§

Source§

impl Clone for ClosedPath

Source§

fn clone(&self) -> ClosedPath

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosedPath

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ClosedPath

Source§

fn default() -> ClosedPath

Returns the “default value” for a type. Read more
Source§

impl Display for ClosedPath

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ClosedPath

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for ClosedPath

Source§

fn eq(&self, other: &ClosedPath) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, -and should not be overridden without very good reason.
Source§

impl Eq for ClosedPath

Source§

impl StructuralPartialEq for ClosedPath

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +ClosedPath in noq_proto - Rust

ClosedPath

Struct ClosedPath 

Source
pub struct ClosedPath { /* private fields */ }
Expand description

Error indicating that a path has not been opened or has already been abandoned

+

Trait Implementations§

Source§

impl Clone for ClosedPath

Source§

fn clone(&self) -> ClosedPath

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosedPath

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ClosedPath

Source§

fn default() -> ClosedPath

Returns the “default value” for a type. Read more
Source§

impl Display for ClosedPath

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ClosedPath

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for ClosedPath

Source§

fn eq(&self, other: &ClosedPath) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ClosedPath

Source§

impl StructuralPartialEq for ClosedPath

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/707/docs/noq_proto/struct.Connection.html b/pr/707/docs/noq_proto/struct.Connection.html index 597878e90..0c0d6c2f2 100644 --- a/pr/707/docs/noq_proto/struct.Connection.html +++ b/pr/707/docs/noq_proto/struct.Connection.html @@ -33,7 +33,7 @@ increasing time. Specifically, calling Instant may be interleaved in any order with a call to handle_event at that same instant; however events or timeouts with different instants must not be interleaved.

-

Implementations§

Source§

impl Connection

Source

pub fn poll_timeout(&mut self) -> Option<Instant>

Returns the next time at which handle_timeout should be called

+

Implementations§

Source§

impl Connection

Source

pub fn poll_timeout(&mut self) -> Option<Instant>

Returns the next time at which handle_timeout should be called

The value returned may change after:

  • the application performed some I/O on the connection
  • @@ -41,17 +41,17 @@ events or timeouts with different instants must not be interleaved.

  • a call to poll_transmit returned Some
  • a call was made to handle_timeout
-
Source

pub fn poll(&mut self) -> Option<Event>

Returns application-facing events

+
Source

pub fn poll(&mut self) -> Option<Event>

Returns application-facing events

Connections should be polled for events after:

  • a call was made to handle_event
  • a call was made to handle_timeout
-
Source

pub fn poll_endpoint_events(&mut self) -> Option<EndpointEvent>

Return endpoint-facing events

-
Source

pub fn streams(&mut self) -> Streams<'_>

Provide control over streams

-
Source

pub fn recv_stream(&mut self, id: StreamId) -> RecvStream<'_>

Provide control over streams

-
Source

pub fn send_stream(&mut self, id: StreamId) -> SendStream<'_>

Provide control over streams

-
Source

pub fn open_path_ensure( +

Source

pub fn poll_endpoint_events(&mut self) -> Option<EndpointEvent>

Return endpoint-facing events

+
Source

pub fn streams(&mut self) -> Streams<'_>

Provide control over streams

+
Source

pub fn recv_stream(&mut self, id: StreamId) -> RecvStream<'_>

Provide control over streams

+
Source

pub fn send_stream(&mut self, id: StreamId) -> SendStream<'_>

Provide control over streams

+
Source

pub fn open_path_ensure( &mut self, network_path: FourTuple, initial_status: PathStatus, @@ -67,7 +67,7 @@ guess which local interface will be used to communicate with the remote, should it not be known yet. We assume that if we already have a path to the remote, the OS is likely to use the same interface to talk to said remote.

See also open_path.

-

Source

pub fn open_path( +

Source

pub fn open_path( &mut self, network_path: FourTuple, initial_status: PathStatus, @@ -76,7 +76,7 @@ the remote, the OS is likely to use the same interface to talk to said remote.Further errors might occur and they will be emitted in PathEvent::Abandoned events with this path id. Once the path is opened and can carry application data it will be reported using a PathEvent::Established event.

-

Source

pub fn close_path( +

Source

pub fn close_path( &mut self, now: Instant, path_id: PathId, @@ -85,18 +85,18 @@ will be reported using a Returns ClosePathError::LastOpenPath if this is the last open path. It does allow closing paths which have not yet been opened, as e.g. is the case when receiving a PATH_ABANDON from the peer for a path that was never opened locally.

-

Source

pub fn paths(&self) -> Vec<PathId>

Returns all known paths.

+
Source

pub fn paths(&self) -> Vec<PathId>

Returns all known paths.

There is no guarantee any of these paths are open or usable.

-
Source

pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath>

Gets the local PathStatus for a known PathId

-
Source

pub fn network_path(&self, path_id: PathId) -> Result<FourTuple, ClosedPath>

Returns the path’s network path represented as a 4-tuple.

-
Source

pub fn set_path_status( +

Source

pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath>

Gets the local PathStatus for a known PathId

+
Source

pub fn network_path(&self, path_id: PathId) -> Result<FourTuple, ClosedPath>

Returns the path’s network path represented as a 4-tuple.

+
Source

pub fn set_path_status( &mut self, path_id: PathId, status: PathStatus, ) -> Result<PathStatus, SetPathStatusError>

Sets the PathStatus for a known PathId

Returns the previous path status on success.

-
Source

pub fn remote_path_status(&self, path_id: PathId) -> Option<PathStatus>

Returns the remote path status

-
Source

pub fn set_path_max_idle_timeout( +

Source

pub fn remote_path_status(&self, path_id: PathId) -> Option<PathStatus>

Returns the remote path status

+
Source

pub fn set_path_max_idle_timeout( &mut self, now: Instant, path_id: PathId, @@ -106,14 +106,14 @@ when receiving a PATH_ABANDON from the peer for a path that was never opened loc idle time. Setting None disables the timeout and stops the timer.

See TransportConfig::default_path_max_idle_timeout for details.

Returns the previous value of the setting.

-

Source

pub fn set_path_keep_alive_interval( +

Source

pub fn set_path_keep_alive_interval( &mut self, path_id: PathId, interval: Option<Duration>, ) -> Result<Option<Duration>, ClosedPath>

Sets the keep_alive_interval for a specific path

See TransportConfig::default_path_keep_alive_interval for details.

Returns the previous value of the setting.

-
Source

pub fn poll_transmit( +

Source

pub fn poll_transmit( &mut self, now: Instant, max_datagrams: NonZeroUsize, @@ -127,42 +127,42 @@ idle time. Setting None disables the timeout and stops the timer.

max_datagrams specifies how many datagrams can be returned inside a single Transmit using GSO. This must be at least 1.

-

Source

pub fn handle_event(&mut self, event: ConnectionEvent)

Process ConnectionEvents generated by the associated Endpoint

+
Source

pub fn handle_event(&mut self, event: ConnectionEvent)

Process ConnectionEvents generated by the associated Endpoint

Will execute protocol logic upon receipt of a connection event, in turn preparing signals (including application Events, EndpointEvents and outgoing datagrams) that should be extracted through the relevant methods.

-
Source

pub fn handle_timeout(&mut self, now: Instant)

Process timer expirations

+
Source

pub fn handle_timeout(&mut self, now: Instant)

Process timer expirations

Executes protocol logic, potentially preparing signals (including application Events, EndpointEvents and outgoing datagrams) that should be extracted through the relevant methods.

It is most efficient to call this immediately after the system clock reaches the latest Instant that was output by poll_timeout; however spurious extra calls will simply no-op and therefore are safe.

-
Source

pub fn close(&mut self, now: Instant, error_code: VarInt, reason: Bytes)

Close a connection immediately

+
Source

pub fn close(&mut self, now: Instant, error_code: VarInt, reason: Bytes)

Close a connection immediately

This does not ensure delivery of outstanding data. It is the application’s responsibility to call this only when all important communications have been completed, e.g. by calling SendStream::finish on outstanding streams and waiting for the corresponding StreamEvent::Finished event.

If Streams::send_streams returns 0, all outstanding stream data has been delivered. There may still be data from the peer that has not been received.

-
Source

pub fn datagrams(&mut self) -> Datagrams<'_>

Control datagrams

-
Source

pub fn stats(&mut self) -> ConnectionStats

Returns connection statistics

-
Source

pub fn path_stats(&mut self, path_id: PathId) -> Option<PathStats>

Returns path statistics

-
Source

pub fn ping(&mut self)

Ping the remote endpoint

+
Source

pub fn datagrams(&mut self) -> Datagrams<'_>

Control datagrams

+
Source

pub fn stats(&mut self) -> ConnectionStats

Returns connection statistics

+
Source

pub fn path_stats(&mut self, path_id: PathId) -> Option<PathStats>

Returns path statistics

+
Source

pub fn ping(&mut self)

Ping the remote endpoint

Causes an ACK-eliciting packet to be transmitted on the connection.

-
Source

pub fn ping_path(&mut self, path: PathId) -> Result<(), ClosedPath>

Ping the remote endpoint over a specific path

+
Source

pub fn ping_path(&mut self, path: PathId) -> Result<(), ClosedPath>

Ping the remote endpoint over a specific path

Causes an ACK-eliciting packet to be transmitted on the path.

-
Source

pub fn force_key_update(&mut self)

Update traffic keys spontaneously

+
Source

pub fn force_key_update(&mut self)

Update traffic keys spontaneously

This can be useful for testing key updates, as they otherwise only happen infrequently.

-
Source

pub fn crypto_session(&self) -> &dyn Session

Get a session reference

-
Source

pub fn is_handshaking(&self) -> bool

Whether the connection is in the process of being established

+
Source

pub fn crypto_session(&self) -> &dyn Session

Get a session reference

+
Source

pub fn is_handshaking(&self) -> bool

Whether the connection is in the process of being established

If this returns false, the connection may be either established or closed, signaled by the emission of a Connected or ConnectionLost event respectively. Note that locally-initiated closes via close() do not emit a ConnectionLost event.

For an established connection this essentially means the handshake is completed, but not necessarily yet confirmed.

-
Source

pub fn is_closed(&self) -> bool

Whether the connection is closed

+
Source

pub fn is_closed(&self) -> bool

Whether the connection is closed

Closed connections cannot transport any further data. A connection becomes closed when either peer application intentionally closes it, or when either transport layer detects an error such as a time-out or certificate validation failure.

@@ -170,24 +170,24 @@ error such as a time-out or certificate validation failure.

connection is closed by the peer or due to an error. When the local application closes the connection via close(), no ConnectionLost event is emitted; instead, pending operations fail with ConnectionError::LocallyClosed.

-
Source

pub fn is_drained(&self) -> bool

Whether there is no longer any need to keep the connection around

+
Source

pub fn is_drained(&self) -> bool

Whether there is no longer any need to keep the connection around

Closed connections become drained after a brief timeout to absorb any remaining in-flight packets from the peer. All drained connections have been closed.

-
Source

pub fn accepted_0rtt(&self) -> bool

For clients, if the peer accepted the 0-RTT data packets

+
Source

pub fn accepted_0rtt(&self) -> bool

For clients, if the peer accepted the 0-RTT data packets

The value is meaningless until after the handshake completes.

-
Source

pub fn has_0rtt(&self) -> bool

Whether 0-RTT is/was possible during the handshake

-
Source

pub fn has_pending_retransmits(&self) -> bool

Whether there are any pending retransmits

-
Source

pub fn side(&self) -> Side

Look up whether we’re the client or server of this Connection

-
Source

pub fn path_observed_address( +

Source

pub fn has_0rtt(&self) -> bool

Whether 0-RTT is/was possible during the handshake

+
Source

pub fn has_pending_retransmits(&self) -> bool

Whether there are any pending retransmits

+
Source

pub fn side(&self) -> Side

Look up whether we’re the client or server of this Connection

+
Source

pub fn path_observed_address( &self, path_id: PathId, ) -> Result<Option<SocketAddr>, ClosedPath>

Get the address observed by the remote over the given path

-
Source

pub fn rtt(&self, path_id: PathId) -> Option<Duration>

Current best estimate of this connection’s latency (round-trip-time)

-
Source

pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller>

Current state of this connection’s congestion controller, for debugging purposes

-
Source

pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt)

Modify the number of remotely initiated streams that may be concurrently open

+
Source

pub fn rtt(&self, path_id: PathId) -> Option<Duration>

Current best estimate of this connection’s latency (round-trip-time)

+
Source

pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller>

Current state of this connection’s congestion controller, for debugging purposes

+
Source

pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt)

Modify the number of remotely initiated streams that may be concurrently open

No streams may be opened by the peer unless fewer than count are already open. Large counts increase both minimum and worst-case memory consumption.

-
Source

pub fn set_max_concurrent_paths( +

Source

pub fn set_max_concurrent_paths( &mut self, now: Instant, count: NonZeroU32, @@ -198,16 +198,16 @@ actively reduce paths they must be closed using TransportConfig) this can not enable multipath and will fail.

-

Source

pub fn max_concurrent_streams(&self, dir: Dir) -> u64

Current number of remotely initiated streams that may be concurrently open

+
Source

pub fn max_concurrent_streams(&self, dir: Dir) -> u64

Current number of remotely initiated streams that may be concurrently open

If the target for this limit is reduced using set_max_concurrent_streams, it will not change immediately, even if fewer streams are open. Instead, it will decrement by one for each time a remotely initiated stream of matching directionality is closed.

-
Source

pub fn set_send_window(&mut self, send_window: u64)

Source

pub fn set_receive_window(&mut self, receive_window: VarInt)

Source

pub fn is_multipath_negotiated(&self) -> bool

Whether the Multipath for QUIC extension is enabled.

+
Source

pub fn set_send_window(&mut self, send_window: u64)

Source

pub fn set_receive_window(&mut self, receive_window: VarInt)

Source

pub fn is_multipath_negotiated(&self) -> bool

Whether the Multipath for QUIC extension is enabled.

Multipath is only enabled after the handshake is completed and if it was enabled by both peers.

-
Source

pub fn handle_network_change( +

Source

pub fn handle_network_change( &mut self, hint: Option<&dyn NetworkChangeHint>, now: Instant, @@ -222,26 +222,26 @@ paths as non recoverable when necessary accelerates connectivity re-establishmen allow it altogether.

The optional hint allows callers to indicate when paths are non-recoverable and should be migrated to new a PathId.

-

Source

pub fn current_mtu(&self) -> u16

Storage size required for the largest packet that can be transmitted on all currently +

Source

pub fn current_mtu(&self) -> u16

Storage size required for the largest packet that can be transmitted on all currently available paths

Buffers passed to Connection::poll_transmit should be at least this large.

When multipath is enabled, this value is the minimum MTU across all available paths.

-
Source

pub fn add_nat_traversal_address( +

Source

pub fn add_nat_traversal_address( &mut self, address: SocketAddr, ) -> Result<(), Error>

Add addresses the local endpoint considers are reachable for nat traversal.

-
Source

pub fn remove_nat_traversal_address( +

Source

pub fn remove_nat_traversal_address( &mut self, address: SocketAddr, ) -> Result<(), Error>

Removes an address the endpoing no longer considers reachable for nat traversal

Addresses not present in the set will be silently ignored.

-
Source

pub fn get_local_nat_traversal_addresses( +

Source

pub fn get_local_nat_traversal_addresses( &self, ) -> Result<Vec<SocketAddr>, Error>

Get the current local nat traversal addresses

-
Source

pub fn get_remote_nat_traversal_addresses( +

Source

pub fn get_remote_nat_traversal_addresses( &self, ) -> Result<Vec<SocketAddr>, Error>

Get the currently advertised nat traversal addresses by the server

-
Source

pub fn initiate_nat_traversal_round( +

Source

pub fn initiate_nat_traversal_round( &mut self, now: Instant, ) -> Result<Vec<SocketAddr>, Error>

Initiates a new nat traversal round

@@ -252,7 +252,7 @@ round is initiated, the previous one is cancelled.

4-tuple.

Returns the server addresses that are now being probed. If addresses fail due to spurious errors, these might succeed later and not be returned in this set.

-

Trait Implementations§

Source§

impl Debug for Connection

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Debug for Connection

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

diff --git a/pr/707/docs/noq_proto/struct.MultipathNotNegotiated.html b/pr/707/docs/noq_proto/struct.MultipathNotNegotiated.html index 7175ff682..605b68ff8 100644 --- a/pr/707/docs/noq_proto/struct.MultipathNotNegotiated.html +++ b/pr/707/docs/noq_proto/struct.MultipathNotNegotiated.html @@ -1,5 +1,5 @@ -MultipathNotNegotiated in noq_proto - Rust

MultipathNotNegotiated

Struct MultipathNotNegotiated 

Source
pub struct MultipathNotNegotiated { /* private fields */ }
Expand description

Error when the multipath extension was not negotiated, but attempted to be used.

-

Trait Implementations§

Source§

impl Clone for MultipathNotNegotiated

Source§

fn clone(&self) -> MultipathNotNegotiated

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for MultipathNotNegotiated

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for MultipathNotNegotiated

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for MultipathNotNegotiated

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl Copy for MultipathNotNegotiated

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +MultipathNotNegotiated in noq_proto - Rust

MultipathNotNegotiated

Struct MultipathNotNegotiated 

Source
pub struct MultipathNotNegotiated { /* private fields */ }
Expand description

Error when the multipath extension was not negotiated, but attempted to be used.

+

Trait Implementations§

Source§

impl Clone for MultipathNotNegotiated

Source§

fn clone(&self) -> MultipathNotNegotiated

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for MultipathNotNegotiated

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for MultipathNotNegotiated

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for MultipathNotNegotiated

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl Copy for MultipathNotNegotiated

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/707/docs/noq_proto/struct.RttEstimator.html b/pr/707/docs/noq_proto/struct.RttEstimator.html index f4bcff9fc..008d323eb 100644 --- a/pr/707/docs/noq_proto/struct.RttEstimator.html +++ b/pr/707/docs/noq_proto/struct.RttEstimator.html @@ -1,10 +1,10 @@ -RttEstimator in noq_proto - Rust

RttEstimator

Struct RttEstimator 

Source
pub struct RttEstimator { /* private fields */ }
Expand description

RTT estimation for a particular network path

-

Implementations§

Source§

impl RttEstimator

Source

pub fn get(&self) -> Duration

The current best RTT estimation.

-
Source

pub fn conservative(&self) -> Duration

Conservative estimate of RTT

+RttEstimator in noq_proto - Rust

RttEstimator

Struct RttEstimator 

Source
pub struct RttEstimator { /* private fields */ }
Expand description

RTT estimation for a particular network path

+

Implementations§

Source§

impl RttEstimator

Source

pub fn get(&self) -> Duration

The current best RTT estimation.

+
Source

pub fn conservative(&self) -> Duration

Conservative estimate of RTT

Takes the maximum of smoothed and latest RTT, as recommended in 6.1.2 of the recovery spec (draft 29).

-
Source

pub fn min(&self) -> Duration

Minimum RTT registered so far for this estimator.

-

Trait Implementations§

Source§

impl Clone for RttEstimator

Source§

fn clone(&self) -> RttEstimator

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for RttEstimator

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Copy for RttEstimator

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Source

pub fn min(&self) -> Duration

Minimum RTT registered so far for this estimator.

+

Trait Implementations§

Source§

impl Clone for RttEstimator

Source§

fn clone(&self) -> RttEstimator

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for RttEstimator

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Copy for RttEstimator

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/707/docs/noq_proto/struct.TransportError.html b/pr/707/docs/noq_proto/struct.TransportError.html index 1d1019d37..9d047cddf 100644 --- a/pr/707/docs/noq_proto/struct.TransportError.html +++ b/pr/707/docs/noq_proto/struct.TransportError.html @@ -11,7 +11,7 @@
§reason: String

Human-readable explanation of the reason

§crypto: Option<Arc<dyn Error + Send + Sync>>

An underlying crypto (e.g. TLS) layer error

Implementations§

Source§

impl Error

Source

pub fn new(code: Code, reason: String) -> Self

Construct an error with a code and a reason

-

Trait Implementations§

Source§

impl Clone for Error

Source§

fn clone(&self) -> Error

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Error

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for Error

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for Error

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<Error> for ConnectionClose

Source§

fn from(x: TransportError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for ConnectionError

Source§

fn from(source: TransportError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for TransportError

Source§

fn from(e: Error) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for Error

Source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +

Trait Implementations§

Source§

impl Clone for Error

Source§

fn clone(&self) -> Error

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Error

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for Error

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for Error

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<Error> for ConnectionClose

Source§

fn from(x: TransportError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for ConnectionError

Source§

fn from(source: TransportError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for TransportError

Source§

fn from(e: Error) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for Error

Source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Eq for Error

Auto Trait Implementations§

§

impl Freeze for Error

§

impl !RefUnwindSafe for Error

§

impl Send for Error

§

impl Sync for Error

§

impl Unpin for Error

§

impl !UnwindSafe for Error

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where diff --git a/pr/707/docs/noq_proto/trait.NetworkChangeHint.html b/pr/707/docs/noq_proto/trait.NetworkChangeHint.html index fbbdb4232..bbff42f61 100644 --- a/pr/707/docs/noq_proto/trait.NetworkChangeHint.html +++ b/pr/707/docs/noq_proto/trait.NetworkChangeHint.html @@ -1,4 +1,4 @@ -NetworkChangeHint in noq_proto - Rust

NetworkChangeHint

Trait NetworkChangeHint 

Source
pub trait NetworkChangeHint: Debug + 'static {
+NetworkChangeHint in noq_proto - Rust

NetworkChangeHint

Trait NetworkChangeHint 

Source
pub trait NetworkChangeHint: Debug + 'static {
     // Required method
     fn is_path_recoverable(
         &self,
@@ -6,7 +6,7 @@
         network_path: FourTuple,
     ) -> bool;
 }
Expand description

Hints when the caller identifies a network change.

-

Required Methods§

Source

fn is_path_recoverable(&self, path_id: PathId, network_path: FourTuple) -> bool

Inform the connection if a path may recover after a network change.

+

Required Methods§

Source

fn is_path_recoverable(&self, path_id: PathId, network_path: FourTuple) -> bool

Inform the connection if a path may recover after a network change.

After network changes, paths may not be recoverable. In this case, waiting for the path to become idle may take longer than what is desirable. If Self::is_path_recoverable returns false, a multipath-enabled, client-side connection will establish a new path to diff --git a/pr/707/docs/search.index/entry/1ee427fe586f.js b/pr/707/docs/search.index/entry/81eb57f53ae8.js similarity index 94% rename from pr/707/docs/search.index/entry/1ee427fe586f.js rename to pr/707/docs/search.index/entry/81eb57f53ae8.js index 44d15b70b..74c272289 100644 --- a/pr/707/docs/search.index/entry/1ee427fe586f.js +++ b/pr/707/docs/search.index/entry/81eb57f53ae8.js @@ -1 +1 @@ -rd_("Af[477,15,478,0,133,0,0]Ak[3173,15,3174,3174,134,0,0]Ag[477,15,478,0,4208,0,0]Al[3173,15,3174,1829,4209,0,0]Al[2029,15,2030,2030,4210,0,0]Al[3173,15,1731,1731,1470,0,0]Ao[1228,13,1229,1229,4047,3111,0]Am[477,13,478,3740,4898,3111,0]Am[477,13,478,4270,1458,3111,0]Al[477,13,478,1126,520,3111,0]Am[477,13,478,1126,4491,3111,0]Am[477,13,478,4157,4484,3111,0]Am[477,13,478,4157,3121,3111,0]Am[477,13,478,4157,3324,3111,0]Am[477,13,478,4157,4483,3111,0]Am[477,13,478,4162,3342,3111,0]Am[477,13,478,4162,4248,3111,0]Aj[477,13,478,0,1480,3111,0]Aj[477,13,478,0,5069,3111,0]Aj[477,13,478,0,1184,3111,0]Aj[477,13,478,0,1463,3111,0]Aj[477,13,478,0,3317,3111,0]Aj[477,13,478,0,3116,3111,0]Aj[477,13,478,0,4954,3111,0]Aj[477,13,478,0,3290,3111,0]Aj[477,13,478,0,2343,3111,0]Aj[477,13,478,0,3303,3111,0]Aj[477,13,478,0,3118,3111,0]Aj[477,13,478,0,4194,3111,0]Aj[477,13,478,0,4583,3111,0]Aj[477,13,478,0,3112,3111,0]Aj[477,13,478,0,4450,3111,0]Aj[477,13,478,0,4064,3111,0]Aj[477,13,478,0,4050,3111,0]Aj[477,13,478,0,3099,3111,0]Aj[477,13,478,0,4580,3111,0]Aj[477,13,478,0,4787,3111,0]Aj[477,13,478,0,4200,3111,0]Aj[477,13,478,0,4203,3111,0]Aj[477,13,478,0,4208,3111,0]Aj[477,13,478,0,4959,3111,0]Ai[477,13,478,0,526,3111,0]Ai[477,13,478,0,133,3111,0]Aj[477,13,478,0,2330,3111,0]Aj[477,13,478,0,3097,3111,0]Ak[477,13,1735,0,5223,3111,0]Ak[477,13,1818,0,1201,3111,0]Ao[3173,13,3174,1856,1481,3111,0]Ao[3173,13,3174,1856,5070,3111,0]Ao[3173,13,3174,3138,1185,3111,0]Ao[3173,13,3174,3147,4899,3111,0]Ao[3173,13,3174,1403,1464,3111,0]Ao[3173,13,3174,1403,3318,3111,0]Ao[3173,13,3174,1403,3117,3111,0]Ao[3173,13,3174,1403,4897,3111,0]Ao[3173,13,3174,1403,4955,3111,0]Ao[3173,13,3174,1403,3293,3111,0]Ao[3173,13,3174,1418,2344,3111,0]Ao[3173,13,3174,1418,3304,3111,0]Ao[3173,13,3174,1418,3119,3111,0]Ao[3173,13,3174,1152,3122,3111,0]Ao[3173,13,3174,1152,4374,3111,0]Ao[3173,13,3174,1157,3343,3111,0]Ao[3173,13,3174,1157,4063,3111,0]Ao[3173,13,3174,2044,4082,3111,0]Ao[3173,13,3174,2044,4485,3111,0]Ao[3173,13,3174,2044,4198,3111,0]Ao[3173,13,3174,3741,4584,3111,0]Ao[3173,13,3174,3741,3113,3111,0]Ao[3173,13,3174,3741,4451,3111,0]Ao[3173,13,3174,3276,4065,3111,0]Ao[3173,13,3174,1733,4051,3111,0]Ao[3173,13,3174,1393,3100,3111,0]Ao[3173,13,3174,1393,4466,3111,0]Ao[3173,13,3174,1393,3310,3111,0]Ao[3173,13,3174,1393,3332,3111,0]Ao[3173,13,3174,1393,4205,3111,0]Ao[3173,13,3174,1393,4582,3111,0]Ao[3173,13,3174,1393,4789,3111,0]Ao[3173,13,3174,2354,4793,3111,0]Ao[3173,13,3174,2354,4201,3111,0]Ao[3173,13,3174,1798,2316,3111,0]Ao[3173,13,3174,1798,4894,3111,0]Ao[3173,13,3174,1829,4204,3111,0]Ao[3173,13,3174,1829,4209,3111,0]Al[3173,13,3174,0,4489,3111,0]Al[3173,13,3174,0,4960,3111,0]An[3173,13,3174,3174,527,3111,0]An[3173,13,3174,3174,134,3111,0]Ao[3173,13,3174,3174,2331,3111,0]Ao[3173,13,3174,3174,3098,3111,0]Ao[3173,13,1731,1731,4380,3111,0]Ao[3173,13,5111,5111,5007,3111,0]Ao[3173,13,5111,5111,1203,3111,0]Ao[3173,13,1736,1736,5224,3111,0]Al[3173,13,1819,0,1202,3111,0]Ao[2029,13,2030,2030,4210,3111,0]Ai[477,13,478,1126,520,0,0]Aj[477,13,478,1126,4491,0,0]Aj[477,13,478,4157,3322,0,0]Aj[477,13,478,4157,5008,0,0]Aj[477,13,478,4162,3328,0,0]Ah[477,14,3114,0,4059,0,0]Ah[477,14,3114,0,3072,0,0]Ah[477,14,3114,0,3091,0,0]Ah[477,14,3114,0,4233,0,0]Ah[477,14,3114,0,4224,0,0]Ai[3173,14,3115,0,4060,0,0]Ai[3173,14,3115,0,3073,0,0]Ai[3173,14,3115,0,3092,0,0]Ai[3173,14,3115,0,4234,0,0]Ai[3173,14,3115,0,4225,0,0]Ai[3173,14,4081,0,2326,0,0]Ai[3173,14,4081,0,2346,0,0]Ai[3173,14,4081,0,2310,0,0]Ai[3173,14,4081,0,1901,0,0]Ae[1228,7,1229,0,0,0,0]Ah[3173,14,4595,0,516,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]Ae[2408,5,2409,0,0,0,0]Af[477,6,478,3174,0,0,0]Ae[3173,6,3174,0,0,0,0]Al[3173,15,1731,1731,1470,0,0]Ag[477,19,478,0,1480,0,0]Ag[477,19,478,0,1463,0,0]Al[3173,19,3174,1856,1481,0,0]Al[3173,19,3174,1403,1464,0,0]Ae[1228,5,1229,0,0,0,0]Ae[1127,5,1725,0,0,0,0]Ae[1127,5,1824,0,0,0,0]Af[477,15,478,0,133,0,0]Ak[3173,15,3174,3174,134,0,0]Ai[477,13,478,0,2343,127,0]Ai[477,13,478,0,3303,127,0]Ai[477,13,478,0,4587,127,0]An[3173,13,3174,1418,2344,127,0]An[3173,13,3174,1418,3304,127,0]An[3173,13,3174,1418,4588,127,0]Ai[477,13,478,0,1480,144,0]Ai[477,13,478,0,1463,144,0]Ai[477,13,478,0,4064,144,0]Ai[477,13,478,0,4203,144,0]Ah[477,13,478,0,526,144,0]Ah[477,13,478,0,133,144,0]Ai[477,13,478,0,2330,144,0]An[3173,13,3174,1856,1481,144,0]An[3173,13,3174,3147,4899,144,0]An[3173,13,3174,1403,1464,144,0]An[3173,13,3174,1152,3122,144,0]An[3173,13,3174,1152,4374,144,0]An[3173,13,3174,1157,3343,144,0]An[3173,13,3174,3276,4065,144,0]An[3173,13,3174,2354,4793,144,0]An[3173,13,3174,1798,4894,144,0]An[3173,13,3174,1829,4204,144,0]Am[3173,13,3174,3174,527,144,0]Am[3173,13,3174,3174,134,144,0]An[3173,13,3174,3174,2331,144,0]Ag[477,13,478,0,2330,0,0]Al[3173,13,3174,3174,2331,0,0]Ai[3173,14,4081,0,1460,0,0]Ai[3173,14,4081,0,3081,0,0]Ag[477,14,478,0,2340,0,0]Al[3173,14,3174,3174,2341,0,0]Al[2029,14,2030,2030,2328,0,0]Al[2029,14,2030,2030,2342,0,0]An[1228,13,1229,1229,141,1191,0]Ao[1228,13,1229,1229,4075,1191,0]Ao[1228,13,1229,1229,4047,1191,0]Ao[1228,13,1417,1417,4486,1191,0]Ao[4088,13,4089,4089,5161,1191,0]Am[477,13,478,4270,5065,1191,0]Am[477,13,478,3740,3295,1191,0]Am[477,13,478,3740,3297,1191,0]Am[477,13,478,3740,1450,1191,0]Am[477,13,478,3740,5072,1191,0]Db[477,13,478,3740,4898,1873,0,\"impl-Display-for-SendDatagramError\"]D`[477,13,478,3740,4898,1191,0,\"impl-Debug-for-SendDatagramError\"]Am[477,13,478,2353,2308,1191,0]Am[477,13,478,2353,4347,1191,0]Cg[477,13,478,4270,1458,1873,0,\"impl-Display-for-Lagged\"]Ce[477,13,478,4270,1458,1191,0,\"impl-Debug-for-Lagged\"]Am[477,13,478,4270,3316,1191,0]Am[477,13,478,4270,5000,1191,0]Am[477,13,478,2385,2312,1191,0]Ck[477,13,478,2385,3325,1873,0,\"impl-Display-for-RetryError\"]Ci[477,13,478,2385,3325,1191,0,\"impl-Debug-for-RetryError\"]Am[477,13,478,2385,4465,1191,0]Al[477,13,478,1126,520,1191,0]Am[477,13,478,1126,4491,1191,0]Am[477,13,478,4157,3322,1191,0]Am[477,13,478,4157,5008,1191,0]Cm[477,13,478,4157,4484,1191,0,\"impl-Debug-for-ReadToEndError\"]Co[477,13,478,4157,4484,1873,0,\"impl-Display-for-ReadToEndError\"]Cj[477,13,478,4157,3121,1873,0,\"impl-Display-for-ReadError\"]Ch[477,13,478,4157,3121,1191,0,\"impl-Debug-for-ReadError\"]Ci[477,13,478,4157,3324,1191,0,\"impl-Debug-for-ResetError\"]Ck[477,13,478,4157,3324,1873,0,\"impl-Display-for-ResetError\"]Cm[477,13,478,4157,4483,1191,0,\"impl-Debug-for-ReadExactError\"]Co[477,13,478,4157,4483,1873,0,\"impl-Display-for-ReadExactError\"]Am[477,13,478,1431,4249,1191,0]Am[477,13,478,1169,4078,1191,0]Am[477,13,478,4162,3328,1191,0]Ck[477,13,478,4162,3342,1873,0,\"impl-Display-for-WriteError\"]Ci[477,13,478,4162,3342,1191,0,\"impl-Debug-for-WriteError\"]Cm[477,13,478,4162,4248,1873,0,\"impl-Display-for-StoppedError\"]Ck[477,13,478,4162,4248,1191,0,\"impl-Debug-for-StoppedError\"]Am[477,13,478,4162,1903,1191,0]Cd[477,13,478,0,1480,1873,0,\"impl-Display-for-VarInt\"]Cb[477,13,478,0,1480,1191,0,\"impl-Debug-for-VarInt\"]Db[477,13,478,0,5069,1873,0,\"impl-Display-for-VarIntBoundsExceeded\"]D`[477,13,478,0,5069,1191,0,\"impl-Debug-for-VarIntBoundsExceeded\"]Aj[477,13,478,0,1184,1191,0]Cd[477,13,478,0,1463,1873,0,\"impl-Display-for-PathId\"]Cb[477,13,478,0,1463,1191,0,\"impl-Debug-for-PathId\"]Aj[477,13,478,0,3317,1191,0]Aj[477,13,478,0,3116,1191,0]Cn[477,13,478,0,4954,1191,0,\"impl-Debug-for-SetPathStatusError\"]D`[477,13,478,0,4954,1873,0,\"impl-Display-for-SetPathStatusError\"]Cf[477,13,478,0,3290,1191,0,\"impl-Debug-for-ClosedPath\"]Ch[477,13,478,0,3290,1873,0,\"impl-Display-for-ClosedPath\"]Aj[477,13,478,0,2343,1191,0]Aj[477,13,478,0,3303,1191,0]Aj[477,13,478,0,3118,1191,0]Aj[477,13,478,0,4587,1191,0]Ch[477,13,478,0,4194,1191,0,\"impl-Debug-for-ClosedStream\"]Cj[477,13,478,0,4194,1873,0,\"impl-Display-for-ClosedStream\"]Cm[477,13,478,0,4583,1873,0,\"impl-Display-for-ConnectionError\"]Ck[477,13,478,0,4583,1191,0,\"impl-Debug-for-ConnectionError\"]Cg[477,13,478,0,3112,1873,0,\"impl-Display-for-PathError\"]Ce[477,13,478,0,3112,1191,0,\"impl-Debug-for-PathError\"]Cl[477,13,478,0,4450,1873,0,\"impl-Display-for-ClosePathError\"]Cj[477,13,478,0,4450,1191,0,\"impl-Debug-for-ClosePathError\"]Aj[477,13,478,0,4597,1191,0]Aj[477,13,478,0,4606,1191,0]Aj[477,13,478,0,4924,1191,0]Aj[477,13,478,0,4939,1191,0]Aj[477,13,478,0,4064,1191,0]Aj[477,13,478,0,4460,1191,0]Aj[477,13,478,0,4242,1191,0]Aj[477,13,478,0,5126,1191,0]Aj[477,13,478,0,4188,1191,0]Cg[477,13,478,0,4050,1191,0,\"impl-Debug-for-ConfigError\"]Ci[477,13,478,0,4050,1873,0,\"impl-Display-for-ConfigError\"]Cg[477,13,478,0,3099,1873,0,\"impl-Display-for-FrameType\"]Ce[477,13,478,0,3099,1191,0,\"impl-Debug-for-FrameType\"]Cm[477,13,478,0,4580,1873,0,\"impl-Display-for-ConnectionClose\"]Ck[477,13,478,0,4580,1191,0,\"impl-Debug-for-ConnectionClose\"]Cn[477,13,478,0,4787,1873,0,\"impl-Display-for-ApplicationClose\"]Cl[477,13,478,0,4787,1191,0,\"impl-Debug-for-ApplicationClose\"]Cj[477,13,478,0,4200,1873,0,\"impl-Display-for-ConnectError\"]Ch[477,13,478,0,4200,1191,0,\"impl-Debug-for-ConnectError\"]Cj[477,13,478,0,4203,1873,0,\"impl-Display-for-ConnectionId\"]Ch[477,13,478,0,4203,1191,0,\"impl-Debug-for-ConnectionId\"]Aj[477,13,478,0,4208,1191,0]D`[477,13,478,0,4959,1873,0,\"impl-Display-for-TransportErrorCode\"]Cn[477,13,478,0,4959,1191,0,\"impl-Debug-for-TransportErrorCode\"]Aj[477,13,478,0,3306,1191,0]Aj[477,13,478,0,4821,1191,0]Ai[477,13,478,0,526,1191,0]C`[477,13,478,0,133,1873,0,\"impl-Display-for-Dir\"]Bn[477,13,478,0,133,1191,0,\"impl-Debug-for-Dir\"]Cf[477,13,478,0,2330,1873,0,\"impl-Display-for-StreamId\"]Cd[477,13,478,0,2330,1191,0,\"impl-Debug-for-StreamId\"]Aj[477,13,478,0,2340,1191,0]Cg[477,13,478,0,3097,1873,0,\"impl-Display-for-FourTuple\"]Ce[477,13,478,0,3097,1191,0,\"impl-Debug-for-FourTuple\"]Aj[477,13,3735,0,501,1191,0]Ak[477,13,3735,0,3286,1191,0]Ak[477,13,3735,0,1188,1191,0]Ak[477,13,3735,0,4055,1191,0]Ak[477,13,3735,0,1886,1191,0]Ak[477,13,3735,0,4366,1191,0]Ak[477,13,1735,0,5223,1191,0]Ak[477,13,1735,0,4053,1191,0]Ak[477,13,1735,0,4963,1191,0]Cd[477,13,1818,0,1201,1873,0,\"impl-Display-for-Error\"]Cb[477,13,1818,0,1201,1191,0,\"impl-Debug-for-Error\"]Dc[477,13,1818,0,5063,1873,0,\"impl-Display-for-NoInitialCipherSuite\"]Da[477,13,1818,0,5063,1191,0,\"impl-Debug-for-NoInitialCipherSuite\"]Ao[3173,13,3174,3741,3298,1191,0]Ao[3173,13,3174,2354,2309,1191,0]Cg[3173,13,3174,1856,1481,1191,0,\"impl-Debug-for-VarInt\"]Ci[3173,13,3174,1856,1481,1873,0,\"impl-Display-for-VarInt\"]Dg[3173,13,3174,1856,5070,1873,0,\"impl-Display-for-VarIntBoundsExceeded\"]De[3173,13,3174,1856,5070,1191,0,\"impl-Debug-for-VarIntBoundsExceeded\"]Ao[3173,13,3174,3138,1185,1191,0]Dd[3173,13,3174,3147,4899,1873,0,\"impl-Display-for-SendDatagramError\"]Db[3173,13,3174,3147,4899,1191,0,\"impl-Debug-for-SendDatagramError\"]Cg[3173,13,3174,1403,1464,1191,0,\"impl-Debug-for-PathId\"]Ci[3173,13,3174,1403,1464,1873,0,\"impl-Display-for-PathId\"]Ao[3173,13,3174,1403,4238,1191,0]Ao[3173,13,3174,1403,3318,1191,0]Ao[3173,13,3174,1403,3117,1191,0]Ao[3173,13,3174,1403,4897,1191,0]Dc[3173,13,3174,1403,4955,1191,0,\"impl-Debug-for-SetPathStatusError\"]De[3173,13,3174,1403,4955,1873,0,\"impl-Display-for-SetPathStatusError\"]Cm[3173,13,3174,1403,3293,1873,0,\"impl-Display-for-ClosedPath\"]Ck[3173,13,3174,1403,3293,1191,0,\"impl-Debug-for-ClosedPath\"]Ao[3173,13,3174,1418,2344,1191,0]Ao[3173,13,3174,1418,3304,1191,0]Ao[3173,13,3174,1418,3119,1191,0]Ao[3173,13,3174,1418,4588,1191,0]Cj[3173,13,3174,1152,3122,1191,0,\"impl-Debug-for-ReadError\"]Cl[3173,13,3174,1152,3122,1873,0,\"impl-Display-for-ReadError\"]Cn[3173,13,3174,1152,4374,1191,0,\"impl-Debug-for-ReadableError\"]D`[3173,13,3174,1152,4374,1873,0,\"impl-Display-for-ReadableError\"]Cm[3173,13,3174,1157,3343,1873,0,\"impl-Display-for-WriteError\"]Ck[3173,13,3174,1157,3343,1191,0,\"impl-Debug-for-WriteError\"]Cn[3173,13,3174,1157,4063,1873,0,\"impl-Display-for-FinishError\"]Cl[3173,13,3174,1157,4063,1191,0,\"impl-Debug-for-FinishError\"]Ao[3173,13,3174,2044,4082,1191,0]Ao[3173,13,3174,2044,4485,1191,0]Cm[3173,13,3174,2044,4198,1191,0,\"impl-Debug-for-ClosedStream\"]Co[3173,13,3174,2044,4198,1873,0,\"impl-Display-for-ClosedStream\"]Db[3173,13,3174,3741,4584,1873,0,\"impl-Display-for-ConnectionError\"]D`[3173,13,3174,3741,4584,1191,0,\"impl-Debug-for-ConnectionError\"]Cl[3173,13,3174,3741,3113,1873,0,\"impl-Display-for-PathError\"]Cj[3173,13,3174,3741,3113,1191,0,\"impl-Debug-for-PathError\"]Da[3173,13,3174,3741,4451,1873,0,\"impl-Display-for-ClosePathError\"]Co[3173,13,3174,3741,4451,1191,0,\"impl-Debug-for-ClosePathError\"]Di[3173,13,3174,3741,5157,1873,0,\"impl-Display-for-MultipathNotNegotiated\"]Dg[3173,13,3174,3741,5157,1191,0,\"impl-Debug-for-MultipathNotNegotiated\"]Ao[3173,13,3174,3741,1206,1191,0]Ao[3173,13,3174,1148,4598,1191,0]Ao[3173,13,3174,3276,4607,1191,0]Ao[3173,13,3174,3276,4925,1191,0]Ao[3173,13,3174,3276,4940,1191,0]Ao[3173,13,3174,3276,4065,1191,0]Ao[3173,13,3174,1733,4461,1191,0]Ao[3173,13,3174,1733,4244,1191,0]Ao[3173,13,3174,1733,5127,1191,0]Ao[3173,13,3174,1733,4190,1191,0]Cl[3173,13,3174,1733,4051,1191,0,\"impl-Debug-for-ConfigError\"]Cn[3173,13,3174,1733,4051,1873,0,\"impl-Display-for-ConfigError\"]Cj[3173,13,3174,1393,3100,1191,0,\"impl-Debug-for-FrameType\"]Cl[3173,13,3174,1393,3100,1873,0,\"impl-Display-for-FrameType\"]Co[3173,13,3174,1393,4466,1191,0,\"impl-Debug-for-InvalidFrameId\"]Da[3173,13,3174,1393,4466,1873,0,\"impl-Display-for-InvalidFrameId\"]Cm[3173,13,3174,1393,3310,1873,0,\"impl-Display-for-MaybeFrame\"]Ck[3173,13,3174,1393,3310,1191,0,\"impl-Debug-for-MaybeFrame\"]Cm[3173,13,3174,1393,3332,1873,0,\"impl-Display-for-StreamInfo\"]Ck[3173,13,3174,1393,3332,1191,0,\"impl-Debug-for-StreamInfo\"]Co[3173,13,3174,1393,4205,1873,0,\"impl-Display-for-DatagramInfo\"]Cm[3173,13,3174,1393,4205,1191,0,\"impl-Debug-for-DatagramInfo\"]D`[3173,13,3174,1393,4582,1191,0,\"impl-Debug-for-ConnectionClose\"]Db[3173,13,3174,1393,4582,1873,0,\"impl-Display-for-ConnectionClose\"]Dc[3173,13,3174,1393,4789,1873,0,\"impl-Display-for-ApplicationClose\"]Da[3173,13,3174,1393,4789,1191,0,\"impl-Debug-for-ApplicationClose\"]Ck[3173,13,3174,1393,2302,1873,0,\"impl-Display-for-Datagram\"]Ci[3173,13,3174,1393,2302,1191,0,\"impl-Debug-for-Datagram\"]Ao[3173,13,3174,2354,4793,1191,0]Ao[3173,13,3174,2354,2313,1191,0]Cm[3173,13,3174,2354,4201,1191,0,\"impl-Debug-for-ConnectError\"]Co[3173,13,3174,2354,4201,1873,0,\"impl-Display-for-ConnectError\"]Ao[3173,13,3174,2354,4045,1191,0]Cm[3173,13,3174,2354,3326,1873,0,\"impl-Display-for-RetryError\"]Ck[3173,13,3174,2354,3326,1191,0,\"impl-Debug-for-RetryError\"]Ao[3173,13,3174,1798,4369,1191,0]Ao[3173,13,3174,1798,4596,1191,0]Ao[3173,13,3174,1798,5160,1191,0]Ao[3173,13,3174,1798,2316,1191,0]Dd[3173,13,3174,1798,4894,1873,0,\"impl-Display-for-PacketDecodeError\"]Db[3173,13,3174,1798,4894,1191,0,\"impl-Debug-for-PacketDecodeError\"]Ao[3173,13,3174,1829,4586,1191,0]Ao[3173,13,3174,1829,4346,1191,0]Cm[3173,13,3174,1829,4204,1191,0,\"impl-Debug-for-ConnectionId\"]Co[3173,13,3174,1829,4204,1873,0,\"impl-Display-for-ConnectionId\"]Ao[3173,13,3174,1829,4209,1191,0]Ce[3173,13,3174,0,4489,1873,0,\"impl-Display-for-Error\"]Cc[3173,13,3174,0,4489,1191,0,\"impl-Debug-for-Error\"]Cd[3173,13,3174,0,4960,1873,0,\"impl-Display-for-Code\"]Cb[3173,13,3174,0,4960,1191,0,\"impl-Debug-for-Code\"]Ao[3173,13,3174,4387,3307,1191,0]Ao[3173,13,3174,4387,5262,1191,0]Ao[3173,13,3174,4995,4822,1191,0]An[3173,13,3174,3174,527,1191,0]Cc[3173,13,3174,3174,134,1191,0,\"impl-Debug-for-Dir\"]Ce[3173,13,3174,3174,134,1873,0,\"impl-Display-for-Dir\"]Ci[3173,13,3174,3174,2331,1191,0,\"impl-Debug-for-StreamId\"]Ck[3173,13,3174,3174,2331,1873,0,\"impl-Display-for-StreamId\"]Ao[3173,13,3174,3174,2341,1191,0]Cl[3173,13,3174,3174,3098,1873,0,\"impl-Display-for-FourTuple\"]Cj[3173,13,3174,3174,3098,1191,0,\"impl-Debug-for-FourTuple\"]D`[3173,13,1731,1731,4380,1873,0,\"impl-Display-for-UnexpectedEnd\"]Cn[3173,13,1731,1731,4380,1191,0,\"impl-Debug-for-UnexpectedEnd\"]Ao[3173,13,5111,5111,5007,1191,0]Cf[3173,13,5111,5111,1203,1191,0,\"impl-Debug-for-Error\"]Ch[3173,13,5111,5111,1203,1873,0,\"impl-Display-for-Error\"]Ao[3173,13,1736,1736,5224,1191,0]Ao[3173,13,1736,1736,4054,1191,0]Ao[3173,13,1736,1736,4966,1191,0]Ce[3173,13,1819,0,1202,1873,0,\"impl-Display-for-Error\"]Cc[3173,13,1819,0,1202,1191,0,\"impl-Debug-for-Error\"]De[3173,13,1819,1819,5064,1191,0,\"impl-Debug-for-NoInitialCipherSuite\"]Dg[3173,13,1819,1819,5064,1873,0,\"impl-Display-for-NoInitialCipherSuite\"]Am[3173,13,3736,535,502,1191,0]An[3173,13,3736,535,3287,1191,0]Ao[3173,13,3736,1389,1189,1191,0]Ao[3173,13,3736,1389,4056,1191,0]Ao[3173,13,3736,2408,1887,1191,0]Ao[3173,13,3736,2408,4367,1191,0]Cf[3173,13,4853,4853,1204,1191,0,\"impl-Debug-for-Error\"]Ch[3173,13,4853,4853,1204,1873,0,\"impl-Display-for-Error\"]Ao[3173,13,4853,4853,1207,1191,0]An[2029,13,2030,431,4490,1191,0]Ao[2029,13,2030,2030,2328,1191,0]Ao[2029,13,2030,2030,2342,1191,0]Ao[2029,13,2030,2030,4210,1191,0]Aj[477,13,478,4270,5065,0,0]Al[3173,13,3174,1403,4238,0,0]Ag[477,14,478,0,2343,0,0]Al[3173,14,3174,1418,2344,0,0]Al[3173,13,3174,1798,4369,0,0]Al[3173,14,3174,1798,5160,0,0]Ah[3173,14,4595,0,516,0,0]Al[2029,14,2030,2030,2328,0,0]Ag[477,13,478,0,5126,0,0]Al[3173,13,3174,1733,5127,0,0]8Al[1228,13,1417,1417,4486,0,0]Al[4088,13,4089,4089,5161,0,0]Ag[477,13,478,0,4338,0,0]Aj[477,13,478,2353,2308,0,0]Ag[477,13,478,0,3319,0,0]Ag[477,13,478,0,4597,0,0]Ag[477,13,478,0,4460,0,0]Ag[477,13,478,0,4242,0,0]Ag[477,13,478,0,4188,0,0]Ag[477,13,478,0,4203,0,0]Ag[477,13,478,0,4821,0,0]Ag[477,13,478,0,2330,0,0]Ag[477,13,478,0,3097,0,0]Ah[477,13,3735,0,1188,0,0]Ah[477,13,3735,0,1886,0,0]Al[3173,13,3174,4616,4339,0,0]Al[3173,13,3174,1148,3320,0,0]Al[3173,13,3174,2354,2309,0,0]Al[3173,13,3174,1798,5288,0,0]Al[3173,13,3174,4387,5257,0,0]Al[3173,13,3174,1148,4598,0,0]Al[3173,13,3174,1733,4461,0,0]Al[3173,13,3174,1733,4244,0,0]Al[3173,13,3174,1733,4190,0,0]Al[3173,13,3174,1798,4369,0,0]Al[3173,13,3174,1829,4204,0,0]Ai[3173,13,3174,0,4489,0,0]Al[3173,13,3174,4387,5262,0,0]Al[3173,13,3174,4995,4822,0,0]Al[3173,13,3174,3174,2331,0,0]Al[3173,13,3174,3174,3098,0,0]Al[3173,13,3736,1389,1189,0,0]Al[3173,13,3736,2408,1887,0,0]Ak[2029,13,2030,431,4490,0,0]Al[1127,13,4305,4305,5209,0,0]Al[1127,13,4305,4305,5210,0,0]Aa[477,3,0,0,0,0,0]Ah[477,13,478,0,526,140,0]Am[3173,13,3174,3174,527,140,0]Ag[477,12,478,0,3335,0,0]Aj[477,13,478,2036,1898,0,0]0Am[477,13,478,1431,4249,1898,0]Aj[477,13,478,0,4378,3335,0]Al[3173,12,3174,1733,3336,0,0]Ao[3173,13,3174,1733,4379,3336,0]Aj[477,13,478,3740,3297,0,0]Ag[477,14,478,0,3118,0,0]Al[3173,13,3174,3741,3298,0,0]Al[3173,14,3174,1418,3119,0,0]Ae[1127,7,1725,0,0,0,0]Ae[1127,7,1824,0,0,0,0]Ac[477,4,478,0,0,0,0]Af[477,5,3735,535,0,0,0]Ag[3173,5,3736,535,0,0,0]Al[1127,15,1128,1128,4999,0,0]Ag[477,15,478,0,4208,0,0]Al[3173,15,3174,1829,4209,0,0]Al[2029,15,2030,2030,4210,0,0]210Ag[477,5,1735,1736,0,0,0]Ae[3173,5,1736,0,0,0,0]Al[3173,15,3174,1798,4596,0,0]Al[3173,15,3174,1393,3310,0,0]Af[477,5,478,1126,0,0,0]Al[3173,15,3174,3741,1206,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]Aj[477,15,478,4157,4484,0,0]Af[477,6,478,3174,0,0,0]Ae[3173,6,3174,0,0,0,0]Ag[477,19,478,0,1463,0,0]Al[3173,19,3174,1403,1464,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1418,3304,0,0]Ah[477,14,3114,0,4224,0,0]Ai[3173,14,3115,0,4225,0,0]Al[2029,14,2030,2030,2328,0,0]Aa[536,3,0,0,0,0,0]Ai[3173,14,3174,0,4489,0,0]Ag[477,14,478,0,3118,0,0]Al[3173,14,3174,1418,3119,0,0]Al[3173,14,3174,1393,2302,0,0]Al[477,13,478,3740,2323,504,0]Al[477,13,478,2385,2312,504,0]Ak[477,13,478,1126,520,504,0]Al[477,13,478,1126,4491,504,0]Al[477,13,478,4157,3322,504,0]Al[477,13,478,4162,3328,504,0]An[3173,13,3174,1152,1446,504,0]Am[1228,13,1229,1229,141,511,0]An[1228,13,1229,1229,4075,511,0]An[1228,13,1229,1229,4047,511,0]An[1228,13,1417,1417,4083,511,0]An[1228,13,1417,1417,1221,511,0]An[1228,13,1417,1417,4486,511,0]Ak[536,13,537,537,4048,511,0]An[4088,13,4089,4089,5161,511,0]Al[477,13,478,3740,4611,511,0]Al[477,13,478,3740,2323,511,0]Ai[477,13,478,0,4338,511,0]Al[477,13,478,4270,5065,511,0]Al[477,13,478,1126,2324,511,0]Al[477,13,478,1126,4785,511,0]Al[477,13,478,3740,3295,511,0]Al[477,13,478,3740,3297,511,0]Al[477,13,478,3740,1889,511,0]Al[477,13,478,3740,1459,511,0]Al[477,13,478,3740,3076,511,0]Al[477,13,478,3740,2296,511,0]Al[477,13,478,3740,4232,511,0]Al[477,13,478,3740,4239,511,0]Al[477,13,478,3740,1450,511,0]Al[477,13,478,3740,5072,511,0]Al[477,13,478,3740,4898,511,0]0Al[477,13,478,2353,2308,511,0]Al[477,13,478,2353,4347,511,0]Al[477,13,478,2353,1437,511,0]Al[477,13,478,4270,1458,511,0]Al[477,13,478,4270,3316,511,0]Al[477,13,478,4270,5000,511,0]Al[477,13,478,2385,2312,511,0]Al[477,13,478,2385,3325,511,0]Al[477,13,478,2385,4465,511,0]Ak[477,13,478,1126,520,511,0]Al[477,13,478,1126,4491,511,0]Al[477,13,478,4157,3322,511,0]Al[477,13,478,4157,5008,511,0]Al[477,13,478,4157,4484,511,0]0Df[477,13,478,4157,3121,511,0,\"impl-From%3CResetError%3E-for-ReadError\"]Al[477,13,478,4157,3121,511,0]Dk[477,13,478,4157,3121,511,0,\"impl-From%3CConnectionError%3E-for-ReadError\"]Al[477,13,478,4157,3324,511,0]0Al[477,13,478,4157,4483,511,0]0Al[477,13,478,1431,4249,511,0]Al[477,13,478,1169,4078,511,0]Al[477,13,478,4162,3328,511,0]Di[477,13,478,4162,3342,511,0,\"impl-From%3CClosedStream%3E-for-WriteError\"]Di[477,13,478,4162,3342,511,0,\"impl-From%3CStoppedError%3E-for-WriteError\"]Dl[477,13,478,4162,3342,511,0,\"impl-From%3CConnectionError%3E-for-WriteError\"]Al[477,13,478,4162,3342,511,0]Al[477,13,478,4162,4248,511,0]0Al[477,13,478,4162,1903,511,0]Ai[477,13,478,0,3319,511,0]Ai[477,13,478,0,4378,511,0]Ai[477,13,478,0,4795,511,0]Ai[477,13,478,0,4603,511,0]Ai[477,13,478,0,4222,511,0]Ai[477,13,478,0,4474,511,0]Cn[477,13,478,0,1480,511,0,\"impl-From%3CStreamId%3E-for-VarInt\"]Dh[477,13,478,0,1480,511,0,\"impl-From%3CTransportErrorCode%3E-for-VarInt\"]Ci[477,13,478,0,1480,511,0,\"impl-From%3Cu32%3E-for-VarInt\"]Ci[477,13,478,0,1480,511,0,\"impl-From%3Cu16%3E-for-VarInt\"]Ch[477,13,478,0,1480,511,0,\"impl-From%3Cu8%3E-for-VarInt\"]Ai[477,13,478,0,1480,511,0]Ai[477,13,478,0,5069,511,0]Ai[477,13,478,0,1184,511,0]Ai[477,13,478,0,1463,511,0]0Ai[477,13,478,0,3317,511,0]Ai[477,13,478,0,3116,511,0]Ai[477,13,478,0,4954,511,0]Ai[477,13,478,0,3290,511,0]Ai[477,13,478,0,2343,511,0]Ai[477,13,478,0,3303,511,0]Ai[477,13,478,0,3118,511,0]Ai[477,13,478,0,4587,511,0]Ai[477,13,478,0,4194,511,0]Dj[477,13,478,0,4583,511,0,\"impl-From%3CCloseReason%3E-for-ConnectionError\"]Ai[477,13,478,0,4583,511,0]Dd[477,13,478,0,4583,511,0,\"impl-From%3CClose%3E-for-ConnectionError\"]Dm[477,13,478,0,4583,511,0,\"impl-From%3CTransportError%3E-for-ConnectionError\"]Ai[477,13,478,0,3112,511,0]Ai[477,13,478,0,4450,511,0]Ai[477,13,478,0,4597,511,0]Ai[477,13,478,0,4606,511,0]Ai[477,13,478,0,4924,511,0]Ai[477,13,478,0,4939,511,0]Ai[477,13,478,0,4064,511,0]0Ai[477,13,478,0,4460,511,0]Ai[477,13,478,0,4242,511,0]Ai[477,13,478,0,5126,511,0]Ai[477,13,478,0,4188,511,0]Ai[477,13,478,0,4050,511,0]Dj[477,13,478,0,4050,511,0,\"impl-From%3CTryFromIntError%3E-for-ConfigError\"]Do[477,13,478,0,4050,511,0,\"impl-From%3CVarIntBoundsExceeded%3E-for-ConfigError\"]Ai[477,13,478,0,3099,511,0]Ai[477,13,478,0,4580,511,0]0Ai[477,13,478,0,4787,511,0]Ai[477,13,478,0,4200,511,0]0Ai[477,13,478,0,4203,511,0]Ai[477,13,478,0,4208,511,0]Ai[477,13,478,0,4959,511,0]0Ai[477,13,478,0,3306,511,0]Ai[477,13,478,0,4821,511,0]Ah[477,13,478,0,526,511,0]Ah[477,13,478,0,133,511,0]Ai[477,13,478,0,2330,511,0]0Ai[477,13,478,0,2340,511,0]Ai[477,13,478,0,3097,511,0]0Ai[477,13,3735,0,501,511,0]Aj[477,13,3735,0,3286,511,0]Aj[477,13,3735,0,1188,511,0]Aj[477,13,3735,0,4055,511,0]Aj[477,13,3735,0,1886,511,0]Aj[477,13,3735,0,4366,511,0]Aj[477,13,3735,0,4885,511,0]Aj[477,13,1735,0,1881,511,0]Ai[477,13,1735,0,514,511,0]Aj[477,13,1735,0,5223,511,0]Aj[477,13,1735,0,4053,511,0]Aj[477,13,1735,0,4963,511,0]Aj[477,13,1818,0,3337,511,0]Aj[477,13,1818,0,4349,511,0]Aj[477,13,1818,0,1201,511,0]Dd[477,13,1818,0,1201,511,0,\"impl-From%3CPeerMisbehaved%3E-for-Error\"]D`[477,13,1818,0,1201,511,0,\"impl-From%3COtherError%3E-for-Error\"]De[477,13,1818,0,1201,511,0,\"impl-From%3CGetRandomFailed%3E-for-Error\"]De[477,13,1818,0,1201,511,0,\"impl-From%3CSystemTimeError%3E-for-Error\"]Do[477,13,1818,0,1201,511,0,\"impl-From%3CEncryptedClientHelloError%3E-for-Error\"]Dm[477,13,1818,0,1201,511,0,\"impl-From%3CCertRevocationListError%3E-for-Error\"]Df[477,13,1818,0,1201,511,0,\"impl-From%3CCertificateError%3E-for-Error\"]Do[477,13,1818,0,1201,511,0,\"impl-From%3CUnsupportedOperationError%3E-for-Error\"]Df[477,13,1818,0,1201,511,0,\"impl-From%3CInconsistentKeys%3E-for-Error\"]Df[477,13,1818,0,1201,511,0,\"impl-From%3CPeerIncompatible%3E-for-Error\"]Dd[477,13,1818,0,1201,511,0,\"impl-From%3CInvalidMessage%3E-for-Error\"]Aj[477,13,1818,0,4817,511,0]Aj[477,13,1818,0,5063,511,0]Aj[477,13,1818,0,4819,511,0]Am[2408,13,2409,2409,132,511,0]An[2408,13,2409,2409,2299,511,0]An[3173,13,3174,4616,4339,511,0]An[3173,13,3174,3147,3089,511,0]An[3173,13,3174,1152,1446,511,0]An[3173,13,3174,2044,1907,511,0]An[3173,13,3174,2044,3323,511,0]An[3173,13,3174,2044,3329,511,0]An[3173,13,3174,3741,3298,511,0]An[3173,13,3174,1148,3320,511,0]An[3173,13,3174,1733,4379,511,0]An[3173,13,3174,2354,2309,511,0]An[3173,13,3174,2354,4345,511,0]An[3173,13,3174,2354,4796,511,0]An[3173,13,3174,2354,4357,511,0]An[3173,13,3174,1798,5288,511,0]An[3173,13,3174,4387,5257,511,0]An[3173,13,3174,1430,4604,511,0]An[3173,13,3174,1430,4223,511,0]An[3173,13,3174,1430,4475,511,0]Co[3173,13,3174,1856,1481,511,0,\"impl-From%3CCode%3E-for-VarInt\"]Cm[3173,13,3174,1856,1481,511,0,\"impl-From%3Cu8%3E-for-VarInt\"]Cn[3173,13,3174,1856,1481,511,0,\"impl-From%3Cu16%3E-for-VarInt\"]Cn[3173,13,3174,1856,1481,511,0,\"impl-From%3Cu32%3E-for-VarInt\"]Dc[3173,13,3174,1856,1481,511,0,\"impl-From%3CStreamId%3E-for-VarInt\"]An[3173,13,3174,1856,1481,511,0]An[3173,13,3174,1856,5070,511,0]An[3173,13,3174,3138,1185,511,0]An[3173,13,3174,3147,4899,511,0]An[3173,13,3174,1403,1464,511,0]0An[3173,13,3174,1403,4238,511,0]An[3173,13,3174,1403,3318,511,0]An[3173,13,3174,1403,3117,511,0]An[3173,13,3174,1403,4897,511,0]An[3173,13,3174,1403,4955,511,0]An[3173,13,3174,1403,3293,511,0]An[3173,13,3174,1418,2344,511,0]An[3173,13,3174,1418,3304,511,0]An[3173,13,3174,1418,3119,511,0]An[3173,13,3174,1418,4588,511,0]An[3173,13,3174,1152,3122,511,0]An[3173,13,3174,1152,4374,511,0]An[3173,13,3174,1157,3343,511,0]An[3173,13,3174,1157,4063,511,0]An[3173,13,3174,2044,4082,511,0]An[3173,13,3174,2044,4485,511,0]An[3173,13,3174,2044,4198,511,0]An[3173,13,3174,3741,4584,511,0]0An[3173,13,3174,3741,3113,511,0]An[3173,13,3174,3741,4451,511,0]An[3173,13,3174,3741,5157,511,0]An[3173,13,3174,3741,1206,511,0]0An[3173,13,3174,1148,4598,511,0]An[3173,13,3174,3276,4607,511,0]An[3173,13,3174,3276,4925,511,0]An[3173,13,3174,3276,4940,511,0]An[3173,13,3174,3276,4065,511,0]0An[3173,13,3174,1733,4461,511,0]An[3173,13,3174,1733,4244,511,0]An[3173,13,3174,1733,5127,511,0]An[3173,13,3174,1733,4190,511,0]Do[3173,13,3174,1733,4051,511,0,\"impl-From%3CTryFromIntError%3E-for-ConfigError\"]An[3173,13,3174,1733,4051,511,0]Ed[3173,13,3174,1733,4051,511,0,\"impl-From%3CVarIntBoundsExceeded%3E-for-ConfigError\"]An[3173,13,3174,1393,3100,511,0]An[3173,13,3174,1393,4466,511,0]An[3173,13,3174,1393,3310,511,0]An[3173,13,3174,1393,3332,511,0]An[3173,13,3174,1393,4205,511,0]An[3173,13,3174,1393,4582,511,0]0An[3173,13,3174,1393,4789,511,0]An[3173,13,3174,1393,2302,511,0]An[3173,13,3174,2354,4793,511,0]An[3173,13,3174,2354,2313,511,0]An[3173,13,3174,2354,4201,511,0]0An[3173,13,3174,2354,4045,511,0]An[3173,13,3174,2354,3326,511,0]An[3173,13,3174,1798,4369,511,0]An[3173,13,3174,1798,4596,511,0]An[3173,13,3174,1798,5160,511,0]An[3173,13,3174,1798,2316,511,0]An[3173,13,3174,1798,4894,511,0]0An[3173,13,3174,1829,4586,511,0]An[3173,13,3174,1829,4346,511,0]An[3173,13,3174,1829,4204,511,0]An[3173,13,3174,1829,4209,511,0]Ak[3173,13,3174,0,4489,511,0]0Ak[3173,13,3174,0,4960,511,0]0An[3173,13,3174,4387,3307,511,0]An[3173,13,3174,4387,5262,511,0]An[3173,13,3174,4995,4822,511,0]Am[3173,13,3174,3174,527,511,0]Am[3173,13,3174,3174,134,511,0]An[3173,13,3174,3174,2331,511,0]0An[3173,13,3174,3174,2341,511,0]An[3173,13,3174,3174,3098,511,0]0An[3173,13,1731,1731,4380,511,0]An[3173,13,5111,5111,5007,511,0]An[3173,13,5111,5111,1203,511,0]0An[3173,13,1736,1736,1882,511,0]Am[3173,13,1736,1736,515,511,0]An[3173,13,1736,1736,5224,511,0]An[3173,13,1736,1736,4054,511,0]An[3173,13,1736,1736,4966,511,0]An[3173,13,1819,1819,3338,511,0]An[3173,13,1819,1819,4350,511,0]E`[3173,13,1819,0,1202,511,0,\"impl-From%3CUnsupportedOperationError%3E-for-Error\"]Dg[3173,13,1819,0,1202,511,0,\"impl-From%3CCertificateError%3E-for-Error\"]Ak[3173,13,1819,0,1202,511,0]Dg[3173,13,1819,0,1202,511,0,\"impl-From%3CInconsistentKeys%3E-for-Error\"]De[3173,13,1819,0,1202,511,0,\"impl-From%3CInvalidMessage%3E-for-Error\"]De[3173,13,1819,0,1202,511,0,\"impl-From%3CPeerMisbehaved%3E-for-Error\"]Df[3173,13,1819,0,1202,511,0,\"impl-From%3CGetRandomFailed%3E-for-Error\"]Df[3173,13,1819,0,1202,511,0,\"impl-From%3CSystemTimeError%3E-for-Error\"]E`[3173,13,1819,0,1202,511,0,\"impl-From%3CEncryptedClientHelloError%3E-for-Error\"]Dn[3173,13,1819,0,1202,511,0,\"impl-From%3CCertRevocationListError%3E-for-Error\"]Dg[3173,13,1819,0,1202,511,0,\"impl-From%3CPeerIncompatible%3E-for-Error\"]Da[3173,13,1819,0,1202,511,0,\"impl-From%3COtherError%3E-for-Error\"]An[3173,13,1819,1819,4818,511,0]An[3173,13,1819,1819,5064,511,0]An[3173,13,1819,1819,4820,511,0]Al[3173,13,3736,535,502,511,0]Am[3173,13,3736,535,3287,511,0]An[3173,13,3736,1389,1189,511,0]An[3173,13,3736,1389,4056,511,0]An[3173,13,3736,2408,1887,511,0]An[3173,13,3736,2408,4367,511,0]An[3173,13,3736,3736,4886,511,0]An[3173,13,4853,4853,1204,511,0]An[3173,13,4853,4853,1207,511,0]An[2029,13,2030,2030,3341,511,0]0Am[2029,13,2030,431,4490,511,0]An[2029,13,2030,2030,2328,511,0]An[2029,13,2030,2030,2342,511,0]An[2029,13,2030,2030,4210,511,0]An[1127,13,1128,1128,3086,511,0]An[1127,13,1128,1128,4999,511,0]Am[1127,13,1725,1725,142,511,0]An[1127,13,1419,1419,1222,511,0]An[1127,13,1419,1419,4084,511,0]An[1127,13,1419,1419,4592,511,0]An[1127,13,4305,4305,5209,511,0]An[1127,13,4305,4305,5210,511,0]Am[1127,13,1824,1824,143,511,0]Ai[477,13,478,0,1480,512,0]Ai[477,13,478,0,1463,512,0]Ai[477,13,478,0,4064,512,0]Ai[477,13,478,0,4203,512,0]Ah[477,13,478,0,526,512,0]Ah[477,13,478,0,133,512,0]Ai[477,13,478,0,2330,512,0]Ai[477,13,478,0,3097,512,0]An[3173,13,3174,1856,1481,512,0]An[3173,13,3174,3147,4899,512,0]An[3173,13,3174,1403,1464,512,0]An[3173,13,3174,1152,3122,512,0]An[3173,13,3174,1152,4374,512,0]An[3173,13,3174,1157,3343,512,0]An[3173,13,3174,3276,4065,512,0]An[3173,13,3174,2354,4793,512,0]An[3173,13,3174,1798,4894,512,0]An[3173,13,3174,1829,4204,512,0]Am[3173,13,3174,3174,527,512,0]Am[3173,13,3174,3174,134,512,0]An[3173,13,3174,3174,2331,512,0]An[3173,13,3174,3174,3098,512,0]Am[1228,13,1229,1229,141,513,0]An[1228,13,1229,1229,4075,513,0]An[1228,13,1229,1229,4047,513,0]An[1228,13,1417,1417,4083,513,0]An[1228,13,1417,1417,1221,513,0]An[1228,13,1417,1417,4486,513,0]Ak[536,13,537,537,4048,513,0]An[4088,13,4089,4089,5161,513,0]Al[477,13,478,3740,4611,513,0]Al[477,13,478,3740,2323,513,0]Ai[477,13,478,0,4338,513,0]Al[477,13,478,4270,5065,513,0]Al[477,13,478,1126,2324,513,0]Al[477,13,478,1126,4785,513,0]Al[477,13,478,3740,3295,513,0]Al[477,13,478,3740,3297,513,0]Al[477,13,478,3740,1889,513,0]Al[477,13,478,3740,1459,513,0]Al[477,13,478,3740,3076,513,0]Al[477,13,478,3740,2296,513,0]Al[477,13,478,3740,4232,513,0]Al[477,13,478,3740,4239,513,0]Al[477,13,478,3740,1450,513,0]Al[477,13,478,3740,5072,513,0]Al[477,13,478,3740,4898,513,0]Al[477,13,478,2353,2308,513,0]Al[477,13,478,2353,4347,513,0]Al[477,13,478,2353,1437,513,0]Al[477,13,478,4270,1458,513,0]Al[477,13,478,4270,3316,513,0]Al[477,13,478,4270,5000,513,0]Al[477,13,478,2385,2312,513,0]Al[477,13,478,2385,3325,513,0]Al[477,13,478,2385,4465,513,0]Ak[477,13,478,1126,520,513,0]Al[477,13,478,1126,4491,513,0]Al[477,13,478,4157,3322,513,0]Al[477,13,478,4157,5008,513,0]Al[477,13,478,4157,4484,513,0]Al[477,13,478,4157,3121,513,0]Al[477,13,478,4157,3324,513,0]Al[477,13,478,4157,4483,513,0]Al[477,13,478,1431,4249,513,0]Al[477,13,478,1169,4078,513,0]Al[477,13,478,4162,3328,513,0]Al[477,13,478,4162,3342,513,0]Al[477,13,478,4162,4248,513,0]Al[477,13,478,4162,1903,513,0]Ai[477,13,478,0,3319,513,0]Ai[477,13,478,0,4378,513,0]Ai[477,13,478,0,4795,513,0]Ai[477,13,478,0,4603,513,0]Ai[477,13,478,0,4222,513,0]Ai[477,13,478,0,4474,513,0]Ai[477,13,478,0,1480,513,0]Ai[477,13,478,0,5069,513,0]Ai[477,13,478,0,1184,513,0]Ai[477,13,478,0,1463,513,0]Ai[477,13,478,0,3317,513,0]Ai[477,13,478,0,3116,513,0]Ai[477,13,478,0,4954,513,0]Ai[477,13,478,0,3290,513,0]Ai[477,13,478,0,2343,513,0]Ai[477,13,478,0,3303,513,0]Ai[477,13,478,0,3118,513,0]Ai[477,13,478,0,4587,513,0]Ai[477,13,478,0,4194,513,0]Ai[477,13,478,0,4583,513,0]Ai[477,13,478,0,3112,513,0]Ai[477,13,478,0,4450,513,0]Ai[477,13,478,0,4597,513,0]Ai[477,13,478,0,4606,513,0]Ai[477,13,478,0,4924,513,0]Ai[477,13,478,0,4939,513,0]Ai[477,13,478,0,4064,513,0]Ai[477,13,478,0,4460,513,0]Ai[477,13,478,0,4242,513,0]Ai[477,13,478,0,5126,513,0]Ai[477,13,478,0,4188,513,0]Ai[477,13,478,0,4050,513,0]Ai[477,13,478,0,3099,513,0]Ai[477,13,478,0,4580,513,0]Ai[477,13,478,0,4787,513,0]Ai[477,13,478,0,4200,513,0]Ai[477,13,478,0,4203,513,0]Ai[477,13,478,0,4208,513,0]Ai[477,13,478,0,4959,513,0]Ai[477,13,478,0,3306,513,0]Ai[477,13,478,0,4821,513,0]Ah[477,13,478,0,526,513,0]Ah[477,13,478,0,133,513,0]Ai[477,13,478,0,2330,513,0]Ai[477,13,478,0,2340,513,0]Ai[477,13,478,0,3097,513,0]Ai[477,13,3735,0,501,513,0]Aj[477,13,3735,0,3286,513,0]Aj[477,13,3735,0,1188,513,0]Aj[477,13,3735,0,4055,513,0]Aj[477,13,3735,0,1886,513,0]Aj[477,13,3735,0,4366,513,0]Aj[477,13,3735,0,4885,513,0]Aj[477,13,1735,0,1881,513,0]Ai[477,13,1735,0,514,513,0]Aj[477,13,1735,0,5223,513,0]Aj[477,13,1735,0,4053,513,0]Aj[477,13,1735,0,4963,513,0]Aj[477,13,1818,0,3337,513,0]Aj[477,13,1818,0,4349,513,0]Aj[477,13,1818,0,1201,513,0]Aj[477,13,1818,0,4817,513,0]Aj[477,13,1818,0,5063,513,0]Aj[477,13,1818,0,4819,513,0]Am[2408,13,2409,2409,132,513,0]An[2408,13,2409,2409,2299,513,0]An[3173,13,3174,4616,4339,513,0]An[3173,13,3174,3147,3089,513,0]An[3173,13,3174,1152,1446,513,0]An[3173,13,3174,2044,1907,513,0]An[3173,13,3174,2044,3323,513,0]An[3173,13,3174,2044,3329,513,0]An[3173,13,3174,3741,3298,513,0]An[3173,13,3174,1148,3320,513,0]An[3173,13,3174,1733,4379,513,0]An[3173,13,3174,2354,2309,513,0]An[3173,13,3174,2354,4345,513,0]An[3173,13,3174,2354,4796,513,0]An[3173,13,3174,2354,4357,513,0]An[3173,13,3174,1798,5288,513,0]An[3173,13,3174,4387,5257,513,0]An[3173,13,3174,1430,4604,513,0]An[3173,13,3174,1430,4223,513,0]An[3173,13,3174,1430,4475,513,0]An[3173,13,3174,1856,1481,513,0]An[3173,13,3174,1856,5070,513,0]An[3173,13,3174,3138,1185,513,0]An[3173,13,3174,3147,4899,513,0]An[3173,13,3174,1403,1464,513,0]An[3173,13,3174,1403,4238,513,0]An[3173,13,3174,1403,3318,513,0]An[3173,13,3174,1403,3117,513,0]An[3173,13,3174,1403,4897,513,0]An[3173,13,3174,1403,4955,513,0]An[3173,13,3174,1403,3293,513,0]An[3173,13,3174,1418,2344,513,0]An[3173,13,3174,1418,3304,513,0]An[3173,13,3174,1418,3119,513,0]An[3173,13,3174,1418,4588,513,0]An[3173,13,3174,1152,3122,513,0]An[3173,13,3174,1152,4374,513,0]An[3173,13,3174,1157,3343,513,0]An[3173,13,3174,1157,4063,513,0]An[3173,13,3174,2044,4082,513,0]An[3173,13,3174,2044,4485,513,0]An[3173,13,3174,2044,4198,513,0]An[3173,13,3174,3741,4584,513,0]An[3173,13,3174,3741,3113,513,0]An[3173,13,3174,3741,4451,513,0]An[3173,13,3174,3741,5157,513,0]An[3173,13,3174,3741,1206,513,0]An[3173,13,3174,1148,4598,513,0]An[3173,13,3174,3276,4607,513,0]An[3173,13,3174,3276,4925,513,0]An[3173,13,3174,3276,4940,513,0]An[3173,13,3174,3276,4065,513,0]An[3173,13,3174,1733,4461,513,0]An[3173,13,3174,1733,4244,513,0]An[3173,13,3174,1733,5127,513,0]An[3173,13,3174,1733,4190,513,0]An[3173,13,3174,1733,4051,513,0]An[3173,13,3174,1393,3100,513,0]An[3173,13,3174,1393,4466,513,0]An[3173,13,3174,1393,3310,513,0]An[3173,13,3174,1393,3332,513,0]An[3173,13,3174,1393,4205,513,0]An[3173,13,3174,1393,4582,513,0]An[3173,13,3174,1393,4789,513,0]An[3173,13,3174,1393,2302,513,0]An[3173,13,3174,2354,4793,513,0]An[3173,13,3174,2354,2313,513,0]An[3173,13,3174,2354,4201,513,0]An[3173,13,3174,2354,4045,513,0]An[3173,13,3174,2354,3326,513,0]An[3173,13,3174,1798,4369,513,0]An[3173,13,3174,1798,4596,513,0]An[3173,13,3174,1798,5160,513,0]An[3173,13,3174,1798,2316,513,0]An[3173,13,3174,1798,4894,513,0]An[3173,13,3174,1829,4586,513,0]An[3173,13,3174,1829,4346,513,0]An[3173,13,3174,1829,4204,513,0]An[3173,13,3174,1829,4209,513,0]Ak[3173,13,3174,0,4489,513,0]Ak[3173,13,3174,0,4960,513,0]An[3173,13,3174,4387,3307,513,0]An[3173,13,3174,4387,5262,513,0]An[3173,13,3174,4995,4822,513,0]Am[3173,13,3174,3174,527,513,0]Am[3173,13,3174,3174,134,513,0]An[3173,13,3174,3174,2331,513,0]An[3173,13,3174,3174,2341,513,0]An[3173,13,3174,3174,3098,513,0]An[3173,13,1731,1731,4380,513,0]An[3173,13,5111,5111,5007,513,0]An[3173,13,5111,5111,1203,513,0]An[3173,13,1736,1736,1882,513,0]Am[3173,13,1736,1736,515,513,0]An[3173,13,1736,1736,5224,513,0]An[3173,13,1736,1736,4054,513,0]An[3173,13,1736,1736,4966,513,0]An[3173,13,1819,1819,3338,513,0]An[3173,13,1819,1819,4350,513,0]Ak[3173,13,1819,0,1202,513,0]An[3173,13,1819,1819,4818,513,0]An[3173,13,1819,1819,5064,513,0]An[3173,13,1819,1819,4820,513,0]Al[3173,13,3736,535,502,513,0]Am[3173,13,3736,535,3287,513,0]An[3173,13,3736,1389,1189,513,0]An[3173,13,3736,1389,4056,513,0]An[3173,13,3736,2408,1887,513,0]An[3173,13,3736,2408,4367,513,0]An[3173,13,3736,3736,4886,513,0]An[3173,13,4853,4853,1204,513,0]An[3173,13,4853,4853,1207,513,0]An[2029,13,2030,2030,3341,513,0]Am[2029,13,2030,431,4490,513,0]An[2029,13,2030,2030,2328,513,0]An[2029,13,2030,2030,2342,513,0]An[2029,13,2030,2030,4210,513,0]An[1127,13,1128,1128,3086,513,0]An[1127,13,1128,1128,4999,513,0]Am[1127,13,1725,1725,142,513,0]An[1127,13,1419,1419,1222,513,0]An[1127,13,1419,1419,4084,513,0]An[1127,13,1419,1419,4592,513,0]An[1127,13,4305,4305,5209,513,0]An[1127,13,4305,4305,5210,513,0]Am[1127,13,1824,1824,143,513,0]Ac[536,7,537,0,0,0,0]Ae[4088,7,4089,0,0,0,0]Ae[4390,7,4391,0,0,0,0]Ae[2408,7,2409,0,0,0,0]Ae[4917,7,4918,0,0,0,0]Al[3173,13,3174,1152,1446,0,0]Ao[3173,13,3174,2354,4357,2314,0]Ah[477,12,1735,0,4887,0,0]Al[3173,13,3174,2044,1907,0,0]Al[3173,12,1736,1736,4888,0,0]Aj[477,13,478,3740,3297,0,0]Ab[1127,3,0,0,0,0,0]Ai[477,13,478,1126,520,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,13,3174,3741,3298,0,0]Al[3173,14,3174,1418,3304,0,0]Aj[477,12,478,2036,3282,0,0]Am[477,13,478,3740,4611,1455,0]Am[477,13,478,3740,2323,1455,0]Am[477,13,478,1126,2324,1455,0]Am[477,13,478,3740,3295,1455,0]Am[477,13,478,3740,1889,1455,0]Am[477,13,478,3740,1459,1455,0]Am[477,13,478,3740,3076,1455,0]Am[477,13,478,3740,2296,1455,0]Am[477,13,478,3740,4232,1455,0]Am[477,13,478,3740,4239,1455,0]Am[477,13,478,2353,1437,1455,0]Am[477,13,478,2385,4465,1455,0]Am[477,13,478,4162,1903,1455,0]?Aj[477,13,478,4157,3322,0,0]Al[3173,13,3174,2044,3323,0,0]Al[3173,13,5111,5111,5007,0,0]Al[3173,13,3174,3147,3089,0,0]Ak[2029,13,2030,431,4490,0,0]Ah[477,12,1735,0,4887,0,0]Al[3173,12,1736,1736,4888,0,0]32Ag[477,13,478,0,5126,0,0]Al[3173,13,3174,1733,5127,0,0]Aj[477,13,478,3740,3297,0,0]Al[3173,13,3174,3741,3298,0,0]Ah[477,12,1735,0,1877,0,0]Al[3173,12,1736,1736,1878,0,0]Al[1228,14,1417,1417,4486,0,0]Ag[477,14,478,0,2340,0,0]Al[3173,14,3174,3174,2341,0,0]Ai[3173,14,4595,0,1218,0,0]Aj[477,13,478,4157,3322,0,0]Aj[477,13,478,4157,5008,0,0]Al[3173,13,3174,2044,3323,0,0]Ag[477,12,478,0,3339,0,0]Aj[477,13,478,0,4474,3339,0]Aj[477,13,478,0,4821,3339,0]Al[3173,12,3174,1430,3340,0,0]Ao[3173,13,3174,1430,4475,3340,0]Ao[3173,13,3174,4995,4822,3340,0]Af[477,5,478,3138,0,0,0]Ah[3173,5,3174,3138,0,0,0]Ag[477,5,3735,1389,0,0,0]Ah[3173,5,3736,1389,0,0,0]Al[1127,15,1128,1128,4999,0,0]Ag[477,6,1818,1392,0,0,0]Ah[3173,6,1819,1392,0,0,0]Ae[3173,6,5111,0,0,0,0]Ae[3173,6,4853,0,0,0,0]Ah[3173,6,3174,3741,0,0,0]1Al[3173,15,3174,1393,3310,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]Aj[477,15,478,4157,3121,0,0]Ag[477,15,478,0,4583,0,0]Al[3173,15,3174,1152,3122,0,0]Al[3173,15,3174,3741,4584,0,0]Al[3173,15,3174,1798,4596,0,0]0Ae[1228,5,1417,0,0,0,0]Ae[1127,5,1419,0,0,0,0]Al[1228,15,1229,1229,4075,0,0]Ag[477,13,478,0,4795,0,0]Al[3173,13,3174,2354,4796,0,0]Ab[1228,3,0,0,0,0,0]Ah[477,12,3735,0,4883,0,0]Ak[477,13,3735,0,3286,4883,0]Ak[477,13,3735,0,4055,4883,0]Ak[477,13,3735,0,4366,4883,0]Al[3173,12,3736,3736,4884,0,0]An[3173,13,3736,535,3287,4884,0]Ao[3173,13,3736,1389,4056,4884,0]Ao[3173,13,3736,2408,4367,4884,0]Al[1127,13,1128,1128,4999,0,0]Ag[477,14,478,0,1184,0,0]Ag[477,14,478,0,2343,0,0]Al[3173,14,3174,3138,1185,0,0]Al[3173,14,3174,1418,2344,0,0]Al[3173,14,3174,2354,4045,0,0]An[1228,13,1229,1229,141,1186,0]Ao[1228,13,1229,1229,4075,1186,0]Ao[1228,13,1229,1229,4047,1186,0]Am[477,13,478,3740,3297,1186,0]Am[477,13,478,3740,1450,1186,0]Am[477,13,478,3740,5072,1186,0]Am[477,13,478,3740,4898,1186,0]Am[477,13,478,2353,2308,1186,0]Am[477,13,478,2353,4347,1186,0]Am[477,13,478,4270,1458,1186,0]Al[477,13,478,1126,520,1186,0]Am[477,13,478,1126,4491,1186,0]Am[477,13,478,4157,4484,1186,0]Am[477,13,478,4157,3121,1186,0]Am[477,13,478,4157,3324,1186,0]Am[477,13,478,4157,4483,1186,0]Am[477,13,478,4162,3342,1186,0]Am[477,13,478,4162,4248,1186,0]Aj[477,13,478,0,1480,1186,0]Aj[477,13,478,0,5069,1186,0]Aj[477,13,478,0,1463,1186,0]Aj[477,13,478,0,3317,1186,0]Aj[477,13,478,0,3116,1186,0]Aj[477,13,478,0,4954,1186,0]Aj[477,13,478,0,3290,1186,0]Aj[477,13,478,0,2343,1186,0]Aj[477,13,478,0,3303,1186,0]Aj[477,13,478,0,3118,1186,0]Aj[477,13,478,0,4587,1186,0]Aj[477,13,478,0,4194,1186,0]Aj[477,13,478,0,4583,1186,0]Aj[477,13,478,0,3112,1186,0]Aj[477,13,478,0,4450,1186,0]Aj[477,13,478,0,4606,1186,0]Aj[477,13,478,0,4924,1186,0]Aj[477,13,478,0,4939,1186,0]Aj[477,13,478,0,4064,1186,0]Aj[477,13,478,0,4460,1186,0]Aj[477,13,478,0,4242,1186,0]Aj[477,13,478,0,5126,1186,0]Aj[477,13,478,0,4188,1186,0]Aj[477,13,478,0,4050,1186,0]Aj[477,13,478,0,3099,1186,0]Aj[477,13,478,0,4580,1186,0]Aj[477,13,478,0,4787,1186,0]Aj[477,13,478,0,4200,1186,0]Aj[477,13,478,0,4203,1186,0]Aj[477,13,478,0,4208,1186,0]Aj[477,13,478,0,4959,1186,0]Aj[477,13,478,0,3306,1186,0]Ai[477,13,478,0,526,1186,0]Ai[477,13,478,0,133,1186,0]Aj[477,13,478,0,2330,1186,0]Aj[477,13,478,0,3097,1186,0]Aj[477,13,3735,0,501,1186,0]Ak[477,13,3735,0,3286,1186,0]Ak[477,13,3735,0,1188,1186,0]Ak[477,13,3735,0,4055,1186,0]Ak[477,13,3735,0,1886,1186,0]Ak[477,13,3735,0,4366,1186,0]Ak[477,13,1818,0,1201,1186,0]Ak[477,13,1818,0,4817,1186,0]Ak[477,13,1818,0,5063,1186,0]Ak[477,13,1818,0,4819,1186,0]Ao[3173,13,3174,1856,1481,1186,0]Ao[3173,13,3174,1856,5070,1186,0]Ao[3173,13,3174,3147,4899,1186,0]Ao[3173,13,3174,1403,1464,1186,0]Ao[3173,13,3174,1403,4238,1186,0]Ao[3173,13,3174,1403,3318,1186,0]Ao[3173,13,3174,1403,3117,1186,0]Ao[3173,13,3174,1403,4897,1186,0]Ao[3173,13,3174,1403,4955,1186,0]Ao[3173,13,3174,1403,3293,1186,0]Ao[3173,13,3174,1418,2344,1186,0]Ao[3173,13,3174,1418,3304,1186,0]Ao[3173,13,3174,1418,3119,1186,0]Ao[3173,13,3174,1418,4588,1186,0]Ao[3173,13,3174,1152,3122,1186,0]Ao[3173,13,3174,1152,4374,1186,0]Ao[3173,13,3174,1157,3343,1186,0]Ao[3173,13,3174,1157,4063,1186,0]Ao[3173,13,3174,2044,4485,1186,0]Ao[3173,13,3174,2044,4198,1186,0]Ao[3173,13,3174,3741,4584,1186,0]Ao[3173,13,3174,3741,3113,1186,0]Ao[3173,13,3174,3741,4451,1186,0]Ao[3173,13,3174,3741,5157,1186,0]Ao[3173,13,3174,3276,4607,1186,0]Ao[3173,13,3174,3276,4925,1186,0]Ao[3173,13,3174,3276,4940,1186,0]Ao[3173,13,3174,3276,4065,1186,0]Ao[3173,13,3174,1733,4461,1186,0]Ao[3173,13,3174,1733,4244,1186,0]Ao[3173,13,3174,1733,5127,1186,0]Ao[3173,13,3174,1733,4190,1186,0]Ao[3173,13,3174,1733,4051,1186,0]Ao[3173,13,3174,1393,3100,1186,0]Ao[3173,13,3174,1393,3310,1186,0]Ao[3173,13,3174,1393,3332,1186,0]Ao[3173,13,3174,1393,4205,1186,0]Ao[3173,13,3174,1393,4582,1186,0]Ao[3173,13,3174,1393,4789,1186,0]Ao[3173,13,3174,1393,2302,1186,0]Ao[3173,13,3174,2354,4793,1186,0]Ao[3173,13,3174,2354,4201,1186,0]Ao[3173,13,3174,1798,4596,1186,0]Ao[3173,13,3174,1798,5160,1186,0]Ao[3173,13,3174,1798,2316,1186,0]Ao[3173,13,3174,1798,4894,1186,0]Ao[3173,13,3174,1829,4204,1186,0]Ao[3173,13,3174,1829,4209,1186,0]Al[3173,13,3174,0,4489,1186,0]Al[3173,13,3174,0,4960,1186,0]Ao[3173,13,3174,4387,3307,1186,0]Ao[3173,13,3174,4387,5262,1186,0]An[3173,13,3174,3174,527,1186,0]An[3173,13,3174,3174,134,1186,0]Ao[3173,13,3174,3174,2331,1186,0]Ao[3173,13,3174,3174,3098,1186,0]Ao[3173,13,1731,1731,4380,1186,0]Ao[3173,13,5111,5111,5007,1186,0]Ao[3173,13,5111,5111,1203,1186,0]Al[3173,13,1819,0,1202,1186,0]Ao[3173,13,1819,1819,4818,1186,0]Ao[3173,13,1819,1819,5064,1186,0]Ao[3173,13,1819,1819,4820,1186,0]Am[3173,13,3736,535,502,1186,0]An[3173,13,3736,535,3287,1186,0]Ao[3173,13,3736,1389,1189,1186,0]Ao[3173,13,3736,1389,4056,1186,0]Ao[3173,13,3736,2408,1887,1186,0]Ao[3173,13,3736,2408,4367,1186,0]Ao[3173,13,4853,4853,1207,1186,0]Ao[2029,13,2030,2030,2328,1186,0]Ao[2029,13,2030,2030,2342,1186,0]Ao[2029,13,2030,2030,4210,1186,0]Ao[1127,13,1128,1128,4999,1186,0]Ao[1127,13,1419,1419,4592,1186,0]Ao[1127,13,4305,4305,5209,1186,0]Ao[1127,13,4305,4305,5210,1186,0]Aj[477,13,478,3740,3297,0,0]Aj[477,13,478,2353,2308,0,0]Ai[477,13,478,1126,520,0,0]Al[3173,13,3174,3741,3298,0,0]Aj[477,13,478,0,4203,1192,0]Ao[3173,13,3174,1829,4204,1192,0]Ai[3173,14,3174,0,4489,0,0]Ag[477,13,478,0,2330,0,0]Al[3173,13,3174,3174,2331,0,0]Ag[477,13,478,0,4208,0,0]Al[3173,13,3174,1829,4209,0,0]Ah[477,14,1735,0,1881,0,0]Al[3173,14,1736,1736,1882,0,0]Al[3173,12,3174,1798,4930,0,0]Ao[3173,13,3174,1798,5288,4930,0];Al[1228,13,1417,1417,1221,0,0]Ai[536,13,537,537,4048,0,0]Al[1127,13,1419,1419,1222,0,0]Aj[477,12,478,2036,3282,0,0]Aj[477,13,478,4162,3328,0,0]Al[3173,13,3174,2044,3329,0,0]Aj[477,13,478,2385,2312,0,0]Al[3173,13,3174,2354,2309,0,0]Aj[477,12,478,2036,1898,0,0]Am[477,13,478,1431,4249,1898,0]Am[477,13,478,1169,4078,1898,0]Ae[1228,2,1229,0,0,0,0]Ak[1228,14,1229,1229,141,0,0]Aj[477,13,478,3740,3297,0,0]Aj[477,13,478,2353,2308,0,0]Ai[477,13,478,1126,520,0,0]Aj[477,14,478,3740,1450,0,0]Al[3173,13,3174,3741,3298,0,0]Ae[1127,2,1128,0,0,0,0]Ag[477,13,478,0,3319,0,0]Al[3173,13,3174,1148,3320,0,0]Aj[477,13,478,4162,3328,0,0]Al[3173,13,3174,2044,3329,0,0]Al[3173,13,5111,5111,5007,0,0]Af[477,5,478,2353,0,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]Al[1228,15,1229,1229,4047,0,0]0Ag[477,15,478,0,3317,0,0]Al[3173,15,3174,1403,3318,0,0]Ah[3173,5,3174,1152,0,0,0]Af[477,15,478,0,526,0,0]Al[2408,15,2409,2409,2299,0,0]Ak[3173,15,3174,3174,527,0,0]Af[477,5,478,3740,0,0,0]Al[3173,15,4853,4853,1204,0,0]:9Af[477,5,478,4270,0,0,0]2Al[3173,15,3174,2044,4082,0,0]Af[477,5,478,1403,0,0,0]Ah[3173,5,3174,1403,0,0,0]Ae[3173,8,1731,0,0,0,0]987?Al[3173,15,3174,3741,1206,0,0]?Af[477,5,478,1856,0,0,0]Ah[3173,5,3174,1856,0,0,0]Aj[477,13,478,2353,2308,0,0]Aj[477,13,478,2385,2312,0,0]Al[3173,13,3174,2044,1907,0,0]Al[3173,13,3174,2354,2309,0,0]An[1228,13,1229,1229,141,1444,0]Ao[1228,13,1229,1229,4075,1444,0]Ao[1228,13,1229,1229,4047,1444,0]Ao[1228,13,1417,1417,4083,1444,0]Ao[1228,13,1417,1417,1221,1444,0]Ao[1228,13,1417,1417,4486,1444,0]Al[536,13,537,537,4048,1444,0]Ao[4088,13,4089,4089,5161,1444,0]Am[477,13,478,3740,4611,1444,0]Am[477,13,478,3740,2323,1444,0]Aj[477,13,478,0,4338,1444,0]Am[477,13,478,4270,5065,1444,0]Am[477,13,478,1126,2324,1444,0]Am[477,13,478,1126,4785,1444,0]Am[477,13,478,3740,3295,1444,0]Am[477,13,478,3740,3297,1444,0]Am[477,13,478,3740,1889,1444,0]Am[477,13,478,3740,1459,1444,0]Am[477,13,478,3740,3076,1444,0]Am[477,13,478,3740,2296,1444,0]Am[477,13,478,3740,4232,1444,0]Am[477,13,478,3740,4239,1444,0]Am[477,13,478,3740,1450,1444,0]Am[477,13,478,3740,5072,1444,0]Am[477,13,478,3740,4898,1444,0]Am[477,13,478,2353,2308,1444,0]Am[477,13,478,2353,4347,1444,0]Am[477,13,478,2353,1437,1444,0]Am[477,13,478,4270,1458,1444,0]Am[477,13,478,4270,3316,1444,0]Am[477,13,478,4270,5000,1444,0]Am[477,13,478,2385,2312,1444,0]Am[477,13,478,2385,3325,1444,0]Am[477,13,478,2385,4465,1444,0]Al[477,13,478,1126,520,1444,0]Am[477,13,478,1126,4491,1444,0]Am[477,13,478,4157,3322,1444,0]Am[477,13,478,4157,5008,1444,0]Am[477,13,478,4157,4484,1444,0]Am[477,13,478,4157,3121,1444,0]Am[477,13,478,4157,3324,1444,0]Am[477,13,478,4157,4483,1444,0]Am[477,13,478,1431,4249,1444,0]Am[477,13,478,1169,4078,1444,0]Am[477,13,478,4162,3328,1444,0]Am[477,13,478,4162,3342,1444,0]Am[477,13,478,4162,4248,1444,0]Am[477,13,478,4162,1903,1444,0]Aj[477,13,478,0,3319,1444,0]Aj[477,13,478,0,4378,1444,0]Aj[477,13,478,0,4795,1444,0]Aj[477,13,478,0,4603,1444,0]Aj[477,13,478,0,4222,1444,0]Aj[477,13,478,0,4474,1444,0]Aj[477,13,478,0,1480,1444,0]Aj[477,13,478,0,5069,1444,0]Aj[477,13,478,0,1184,1444,0]Aj[477,13,478,0,1463,1444,0]Aj[477,13,478,0,3317,1444,0]Aj[477,13,478,0,3116,1444,0]Aj[477,13,478,0,4954,1444,0]Aj[477,13,478,0,3290,1444,0]Aj[477,13,478,0,2343,1444,0]Aj[477,13,478,0,3303,1444,0]Aj[477,13,478,0,3118,1444,0]Aj[477,13,478,0,4587,1444,0]Aj[477,13,478,0,4194,1444,0]Aj[477,13,478,0,4583,1444,0]Aj[477,13,478,0,3112,1444,0]Aj[477,13,478,0,4450,1444,0]Aj[477,13,478,0,4597,1444,0]Aj[477,13,478,0,4606,1444,0]Aj[477,13,478,0,4924,1444,0]Aj[477,13,478,0,4939,1444,0]Aj[477,13,478,0,4064,1444,0]Aj[477,13,478,0,4460,1444,0]Aj[477,13,478,0,4242,1444,0]Aj[477,13,478,0,5126,1444,0]Aj[477,13,478,0,4188,1444,0]Aj[477,13,478,0,4050,1444,0]Aj[477,13,478,0,3099,1444,0]Aj[477,13,478,0,4580,1444,0]Aj[477,13,478,0,4787,1444,0]Aj[477,13,478,0,4200,1444,0]Aj[477,13,478,0,4203,1444,0]Aj[477,13,478,0,4208,1444,0]Aj[477,13,478,0,4959,1444,0]Aj[477,13,478,0,3306,1444,0]Aj[477,13,478,0,4821,1444,0]Ai[477,13,478,0,526,1444,0]Ai[477,13,478,0,133,1444,0]Aj[477,13,478,0,2330,1444,0]Aj[477,13,478,0,2340,1444,0]Aj[477,13,478,0,3097,1444,0]Aj[477,13,3735,0,501,1444,0]Ak[477,13,3735,0,3286,1444,0]Ak[477,13,3735,0,1188,1444,0]Ak[477,13,3735,0,4055,1444,0]Ak[477,13,3735,0,1886,1444,0]Ak[477,13,3735,0,4366,1444,0]Ak[477,13,3735,0,4885,1444,0]Ak[477,13,1735,0,1881,1444,0]Aj[477,13,1735,0,514,1444,0]Ak[477,13,1735,0,5223,1444,0]Ak[477,13,1735,0,4053,1444,0]Ak[477,13,1735,0,4963,1444,0]Ak[477,13,1818,0,3337,1444,0]Ak[477,13,1818,0,4349,1444,0]Ak[477,13,1818,0,1201,1444,0]Ak[477,13,1818,0,4817,1444,0]Ak[477,13,1818,0,5063,1444,0]Ak[477,13,1818,0,4819,1444,0]An[2408,13,2409,2409,132,1444,0]Ao[2408,13,2409,2409,2299,1444,0]Ao[3173,13,3174,4616,4339,1444,0]Ao[3173,13,3174,3147,3089,1444,0]Ao[3173,13,3174,1152,1446,1444,0]Ao[3173,13,3174,2044,1907,1444,0]Ao[3173,13,3174,2044,3323,1444,0]Ao[3173,13,3174,2044,3329,1444,0]Ao[3173,13,3174,3741,3298,1444,0]Ao[3173,13,3174,1148,3320,1444,0]Ao[3173,13,3174,1733,4379,1444,0]Ao[3173,13,3174,2354,2309,1444,0]Ao[3173,13,3174,2354,4345,1444,0]Ao[3173,13,3174,2354,4796,1444,0]Ao[3173,13,3174,2354,4357,1444,0]Ao[3173,13,3174,1798,5288,1444,0]Ao[3173,13,3174,4387,5257,1444,0]Ao[3173,13,3174,1430,4604,1444,0]Ao[3173,13,3174,1430,4223,1444,0]Ao[3173,13,3174,1430,4475,1444,0]Ao[3173,13,3174,1856,1481,1444,0]Ao[3173,13,3174,1856,5070,1444,0]Ao[3173,13,3174,3138,1185,1444,0]Ao[3173,13,3174,3147,4899,1444,0]Ao[3173,13,3174,1403,1464,1444,0]Ao[3173,13,3174,1403,4238,1444,0]Ao[3173,13,3174,1403,3318,1444,0]Ao[3173,13,3174,1403,3117,1444,0]Ao[3173,13,3174,1403,4897,1444,0]Ao[3173,13,3174,1403,4955,1444,0]Ao[3173,13,3174,1403,3293,1444,0]Ao[3173,13,3174,1418,2344,1444,0]Ao[3173,13,3174,1418,3304,1444,0]Ao[3173,13,3174,1418,3119,1444,0]Ao[3173,13,3174,1418,4588,1444,0]Ao[3173,13,3174,1152,3122,1444,0]Ao[3173,13,3174,1152,4374,1444,0]Ao[3173,13,3174,1157,3343,1444,0]Ao[3173,13,3174,1157,4063,1444,0]Ao[3173,13,3174,2044,4082,1444,0]Ao[3173,13,3174,2044,4485,1444,0]Ao[3173,13,3174,2044,4198,1444,0]Ao[3173,13,3174,3741,4584,1444,0]Ao[3173,13,3174,3741,3113,1444,0]Ao[3173,13,3174,3741,4451,1444,0]Ao[3173,13,3174,3741,5157,1444,0]Ao[3173,13,3174,3741,1206,1444,0]Ao[3173,13,3174,1148,4598,1444,0]Ao[3173,13,3174,3276,4607,1444,0]Ao[3173,13,3174,3276,4925,1444,0]Ao[3173,13,3174,3276,4940,1444,0]Ao[3173,13,3174,3276,4065,1444,0]Ao[3173,13,3174,1733,4461,1444,0]Ao[3173,13,3174,1733,4244,1444,0]Ao[3173,13,3174,1733,5127,1444,0]Ao[3173,13,3174,1733,4190,1444,0]Ao[3173,13,3174,1733,4051,1444,0]Ao[3173,13,3174,1393,3100,1444,0]Ao[3173,13,3174,1393,4466,1444,0]Ao[3173,13,3174,1393,3310,1444,0]Ao[3173,13,3174,1393,3332,1444,0]Ao[3173,13,3174,1393,4205,1444,0]Ao[3173,13,3174,1393,4582,1444,0]Ao[3173,13,3174,1393,4789,1444,0]Ao[3173,13,3174,1393,2302,1444,0]Ao[3173,13,3174,2354,4793,1444,0]Ao[3173,13,3174,2354,2313,1444,0]Ao[3173,13,3174,2354,4201,1444,0]Ao[3173,13,3174,2354,4045,1444,0]Ao[3173,13,3174,2354,3326,1444,0]Ao[3173,13,3174,1798,4369,1444,0]Ao[3173,13,3174,1798,4596,1444,0]Ao[3173,13,3174,1798,5160,1444,0]Ao[3173,13,3174,1798,2316,1444,0]Ao[3173,13,3174,1798,4894,1444,0]Ao[3173,13,3174,1829,4586,1444,0]Ao[3173,13,3174,1829,4346,1444,0]Ao[3173,13,3174,1829,4204,1444,0]Ao[3173,13,3174,1829,4209,1444,0]Al[3173,13,3174,0,4489,1444,0]Al[3173,13,3174,0,4960,1444,0]Ao[3173,13,3174,4387,3307,1444,0]Ao[3173,13,3174,4387,5262,1444,0]Ao[3173,13,3174,4995,4822,1444,0]An[3173,13,3174,3174,527,1444,0]An[3173,13,3174,3174,134,1444,0]Ao[3173,13,3174,3174,2331,1444,0]Ao[3173,13,3174,3174,2341,1444,0]Ao[3173,13,3174,3174,3098,1444,0]Ao[3173,13,1731,1731,4380,1444,0]Ao[3173,13,5111,5111,5007,1444,0]Ao[3173,13,5111,5111,1203,1444,0]Ao[3173,13,1736,1736,1882,1444,0]An[3173,13,1736,1736,515,1444,0]Ao[3173,13,1736,1736,5224,1444,0]Ao[3173,13,1736,1736,4054,1444,0]Ao[3173,13,1736,1736,4966,1444,0]Ao[3173,13,1819,1819,3338,1444,0]Ao[3173,13,1819,1819,4350,1444,0]Al[3173,13,1819,0,1202,1444,0]Ao[3173,13,1819,1819,4818,1444,0]Ao[3173,13,1819,1819,5064,1444,0]Ao[3173,13,1819,1819,4820,1444,0]Am[3173,13,3736,535,502,1444,0]An[3173,13,3736,535,3287,1444,0]Ao[3173,13,3736,1389,1189,1444,0]Ao[3173,13,3736,1389,4056,1444,0]Ao[3173,13,3736,2408,1887,1444,0]Ao[3173,13,3736,2408,4367,1444,0]Ao[3173,13,3736,3736,4886,1444,0]Ao[3173,13,4853,4853,1204,1444,0]Ao[3173,13,4853,4853,1207,1444,0]Ao[2029,13,2030,2030,3341,1444,0]An[2029,13,2030,431,4490,1444,0]Ao[2029,13,2030,2030,2328,1444,0]Ao[2029,13,2030,2030,2342,1444,0]Ao[2029,13,2030,2030,4210,1444,0]Ao[1127,13,1128,1128,3086,1444,0]Ao[1127,13,1128,1128,4999,1444,0]An[1127,13,1725,1725,142,1444,0]Ao[1127,13,1419,1419,1222,1444,0]Ao[1127,13,1419,1419,4084,1444,0]Ao[1127,13,1419,1419,4592,1444,0]Ao[1127,13,4305,4305,5209,1444,0]Ao[1127,13,4305,4305,5210,1444,0]An[1127,13,1824,1824,143,1444,0]Ak[1228,14,1229,1229,141,0,0]Ac[536,7,537,0,0,0,0]Aj[477,13,478,2353,2308,0,0]Ae[1127,2,1128,0,0,0,0]Ae[4917,7,4918,0,0,0,0]Aj[477,13,478,3740,3297,0,0]Ae[3173,2,3174,0,0,0,0]Al[3173,13,3174,2354,2309,0,0]Af[477,2,478,3174,0,0,0]Ag[477,13,478,0,4959,0,0]Ag[477,14,478,0,3303,0,0]Ag[477,14,478,0,4242,0,0]5Ai[3173,13,3174,0,4960,0,0]Al[3173,14,3174,1418,3304,0,0]Al[3173,14,3174,1733,4244,0,0]Ai[3173,14,3174,0,4489,0,0]Aj[477,13,478,0,1480,3090,0]Aj[477,13,478,0,1463,3090,0]Aj[477,13,478,0,3099,3090,0]Aj[477,13,478,0,4959,3090,0]Aj[477,13,478,0,2330,3090,0]Ao[3173,13,3174,1856,1481,3090,0]Ao[3173,13,3174,1403,1464,3090,0]Ao[3173,13,3174,1393,3100,3090,0]Ao[3173,13,3174,1393,3310,3090,0]Al[3173,13,3174,1798,4596,0,0]Al[3173,13,3174,0,4960,3090,0]Ao[3173,13,3174,3174,2331,3090,0]Al[3173,12,1731,1731,3090,0,0]Al[2029,14,2030,2030,2328,0,0]Aj[477,13,478,0,1480,3095,0]Aj[477,13,478,0,1463,3095,0]Aj[477,13,478,0,3099,3095,0]Aj[477,13,478,0,4959,3095,0]Aj[477,13,478,0,2330,3095,0]Ao[3173,13,3174,1856,1481,3095,0]Ao[3173,13,3174,1403,1464,3095,0]Ao[3173,13,3174,1393,3100,3095,0]Ao[3173,13,3174,1393,3310,3095,0]Ao[3173,13,3174,1393,2302,3095,0]Al[3173,13,3174,0,4960,3095,0]Ao[3173,13,3174,3174,2331,3095,0]Al[3173,12,1731,1731,3095,0,0]Aj[477,13,478,4162,3328,0,0]Al[3173,13,3174,2044,3329,0,0]Al[1127,13,1419,1419,4084,0,0]Al[3173,13,3174,2354,2309,0,0]Ag[477,14,1735,0,514,0,0]Ak[3173,14,1736,1736,515,0,0]Aj[477,13,478,2385,2312,0,0]3Ag[477,12,478,0,3339,0,0]Aj[477,13,478,0,4474,3339,0]Aj[477,13,478,0,4821,3339,0]Al[3173,12,3174,1430,3340,0,0]Ao[3173,13,3174,1430,4475,3340,0]Ao[3173,13,3174,4995,4822,3340,0]Al[1127,14,1128,1128,3086,0,0]Ag[477,14,478,0,1184,0,0]Al[3173,14,3174,3138,1185,0,0]Ah[477,13,3735,0,3299,0,0]Aj[477,13,3735,0,501,3299,0]Ak[477,13,3735,0,1188,3299,0]Ak[477,13,3735,0,1886,3299,0]Al[3173,13,3736,3736,3300,0,0]Am[3173,13,3736,535,502,3300,0]Ao[3173,13,3736,1389,1189,3300,0]Ao[3173,13,3736,2408,1887,3300,0]Ag[477,14,1735,0,514,0,0]Ak[3173,14,1736,1736,515,0,0]Ai[3173,14,4595,0,4827,0,0]Aj[477,14,478,3740,1450,0,0]Ag[477,14,478,0,4580,0,0]Ag[477,14,478,0,4787,0,0]Ah[477,14,3114,0,3072,0,0]Al[3173,14,3174,1393,4582,0,0]Al[3173,14,3174,1393,4789,0,0]Ai[3173,14,3174,0,4489,0,0]Ai[3173,14,1205,0,4453,0,0]Ai[3173,14,3115,0,3073,0,0]Aj[477,13,478,2353,2308,0,0]Aj[477,13,478,2385,2312,0,0]Al[3173,13,3174,2354,2309,0,0]Ag[477,13,478,0,3097,0,0]Ah[477,14,1735,0,1881,0,0]Al[3173,13,3174,3174,3098,0,0]Al[3173,14,1736,1736,1882,0,0]Ac[477,4,478,0,0,0,0]Ag[477,2,1735,1736,0,0,0]Ae[3173,4,3174,0,0,0,0]Ae[3173,2,1736,0,0,0,0]Ac[536,7,537,0,0,0,0];Ae[1127,2,1128,0,0,0,0]Ae[4917,7,4918,0,0,0,0]Am[477,13,478,3740,4898,1194,0]Am[477,13,478,4157,4484,1194,0]Am[477,13,478,4157,3121,1194,0]Am[477,13,478,4157,3324,1194,0]Am[477,13,478,4157,4483,1194,0]Am[477,13,478,4162,3342,1194,0]Am[477,13,478,4162,4248,1194,0]Aj[477,13,478,0,4583,1194,0]Ao[3173,13,3174,3741,4584,1194,0]Ag[477,14,478,0,2340,0,0]Al[3173,14,3174,3174,2341,0,0]Al[2029,14,2030,2030,2342,0,0]Ai[477,13,478,1126,520,0,0]Ah[477,14,3114,0,4233,0,0]Ai[3173,14,3115,0,4234,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1418,3304,0,0]Al[2029,14,2030,2030,2328,0,0]Ag[477,14,478,0,3118,0,0]Ag[477,14,478,0,4587,0,0]Al[3173,14,3174,1418,3119,0,0]Al[3173,14,3174,1418,4588,0,0]3210Ah[477,12,1735,0,1877,0,0]Al[3173,12,1736,1736,1878,0,0]Ah[477,12,3735,0,3299,0,0]Aj[477,13,3735,0,501,3299,0]Ak[477,13,3735,0,1188,3299,0]Ak[477,13,3735,0,1886,3299,0]Al[3173,12,3736,3736,3300,0,0]Am[3173,13,3736,535,502,3300,0]Ao[3173,13,3736,1389,1189,3300,0]Ao[3173,13,3736,2408,1887,3300,0]Al[3173,15,3174,3147,4899,0,0]Al[3173,15,3174,1152,3122,0,0]Al[3173,15,3174,1157,3343,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]Ah[477,10,1735,1736,0,0,0]Af[3173,10,1736,0,0,0,0]Al[3173,15,3174,1798,4596,0,0]Ag[477,5,1735,1736,0,0,0]Ae[3173,5,1736,0,0,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]Ag[477,5,3735,2408,0,0,0]Ah[3173,5,3736,2408,0,0,0]Al[1127,15,1128,1128,4999,0,0]Af[477,5,478,3740,0,0,0]5454Ag[477,10,478,2036,0,0,0];:Aj[477,15,478,4162,3342,0,0]Af[477,5,478,4162,0,0,0]Al[3173,15,3174,1157,3343,0,0]Al[3173,15,3174,1157,4063,0,0]Al[3173,15,3174,2044,4082,0,0]Ah[3173,5,3174,2044,0,0,0]Aj[477,15,478,4157,4484,0,0]Al[3173,15,3174,1393,3310,0,0]Al[3173,15,3174,1798,2316,0,0]Ag[477,12,478,0,5117,0,0]Al[3173,12,3174,4387,5118,0,0]Ao[3173,13,3174,4387,5257,5118,0]Ao[3173,13,3174,4387,5262,5118,0]Ak[1228,14,1229,1229,141,0,0]An[1228,13,1229,1229,141,4452,0]An[2408,13,2409,2409,132,4452,0]Ak[2408,14,2409,2409,132,0,0]Ao[1127,13,1128,1128,3086,4452,0]An[1127,13,1725,1725,142,4452,0]An[1127,13,1824,1824,143,4452,0]Aj[477,13,478,0,1480,3294,0]Aj[477,13,478,0,1463,3294,0]Aj[477,13,478,0,4064,3294,0]Aj[477,13,478,0,4203,3294,0]Ai[477,13,478,0,526,3294,0]Ai[477,13,478,0,133,3294,0]Aj[477,13,478,0,2330,3294,0]Ao[3173,13,3174,1856,1481,3294,0]Ao[3173,13,3174,3147,4899,3294,0]Ao[3173,13,3174,1403,1464,3294,0]Ao[3173,13,3174,1152,3122,3294,0]Ao[3173,13,3174,1152,4374,3294,0]Ao[3173,13,3174,1157,3343,3294,0]Ao[3173,13,3174,3276,4065,3294,0]Ao[3173,13,3174,2354,4793,3294,0]Ao[3173,13,3174,1798,4894,3294,0]Ao[3173,13,3174,1829,4204,3294,0]An[3173,13,3174,3174,527,3294,0]An[3173,13,3174,3174,134,3294,0]Ao[3173,13,3174,3174,2331,3294,0]Aj[477,13,478,2353,2308,0,0]Al[3173,13,3174,2354,2309,0,0]Aj[477,13,478,2385,2312,0,0]Ah[477,12,1735,0,3109,0,0]Ah[477,12,1735,0,3102,0,0]Al[3173,13,3174,2354,2313,0,0]Al[3173,12,1736,1736,3110,0,0]Al[3173,12,1736,1736,3103,0,0]Ao[1228,13,1417,1417,4083,1872,0]Ao[1228,13,1417,1417,1221,1872,0]Al[536,13,537,537,4048,1872,0]Aj[477,13,478,0,4338,1872,0]Am[477,13,478,2353,4347,1872,0]Aj[477,13,478,0,1480,1872,0]Aj[477,13,478,0,1463,1872,0]Aj[477,13,478,0,3317,1872,0]Aj[477,13,478,0,3290,1872,0]Aj[477,13,478,0,2343,1872,0]Aj[477,13,478,0,3303,1872,0]Aj[477,13,478,0,3118,1872,0]Aj[477,13,478,0,4587,1872,0]Aj[477,13,478,0,4194,1872,0]Aj[477,13,478,0,4606,1872,0]Aj[477,13,478,0,4924,1872,0]Aj[477,13,478,0,4939,1872,0]Aj[477,13,478,0,4064,1872,0]Aj[477,13,478,0,4460,1872,0]Aj[477,13,478,0,5126,1872,0]Aj[477,13,478,0,4821,1872,0]Ak[477,13,3735,0,3286,1872,0]Ak[477,13,3735,0,4055,1872,0]Ak[477,13,3735,0,4366,1872,0]Ak[477,13,3735,0,4885,1872,0]Ao[3173,13,3174,4616,4339,1872,0]Ao[3173,13,3174,4387,5257,1872,0]Ao[3173,13,3174,1856,1481,1872,0]Ao[3173,13,3174,1403,1464,1872,0]Ao[3173,13,3174,1403,3318,1872,0]Ao[3173,13,3174,1403,3293,1872,0]Ao[3173,13,3174,1418,2344,1872,0]Ao[3173,13,3174,1418,3304,1872,0]Ao[3173,13,3174,1418,3119,1872,0]Ao[3173,13,3174,1418,4588,1872,0]Ao[3173,13,3174,2044,4485,1872,0]Ao[3173,13,3174,2044,4198,1872,0]Ao[3173,13,3174,3276,4607,1872,0]Ao[3173,13,3174,3276,4925,1872,0]Ao[3173,13,3174,3276,4940,1872,0]Ao[3173,13,3174,3276,4065,1872,0]Ao[3173,13,3174,1733,4461,1872,0]Ao[3173,13,3174,1733,5127,1872,0]Ao[3173,13,3174,4387,5262,1872,0]Ao[3173,13,3174,4995,4822,1872,0]An[3173,13,3736,535,3287,1872,0]Ao[3173,13,3736,1389,4056,1872,0]Ao[3173,13,3736,2408,4367,1872,0]Ao[3173,13,3736,3736,4886,1872,0]Ao[2029,13,2030,2030,2328,1872,0]Ao[1127,13,1419,1419,1222,1872,0]Ao[1127,13,1419,1419,4592,1872,0]Al[3173,13,3174,1829,4346,0,0]Al[3173,13,3174,1798,4369,0,0]Al[3173,13,3174,1798,4596,0,0]Al[3173,14,3174,1798,5160,0,0]Ai[3173,14,4893,0,4961,0,0]Ah[3173,14,4595,0,516,0,0]Ai[3173,14,4595,0,1216,0,0]Ai[3173,14,4595,0,1218,0,0]Ai[3173,14,4595,0,4827,0,0]Ah[477,12,1735,0,3109,0,0]Ah[477,12,1735,0,3102,0,0]Al[3173,12,1736,1736,3110,0,0]Al[3173,12,1736,1736,3103,0,0]Aj[477,13,478,4157,3322,0,0]Aj[477,13,478,4157,5008,0,0]Ah[477,13,3735,0,3299,0,0]Aj[477,13,3735,0,501,3299,0]Ak[477,13,3735,0,1188,3299,0]Ak[477,13,3735,0,1886,3299,0]Al[3173,13,3736,3736,3300,0,0]Am[3173,13,3736,535,502,3300,0]Ao[3173,13,3736,1389,1189,3300,0]Ao[3173,13,3736,2408,1887,3300,0]Ag[477,13,478,0,4606,0,0]Al[3173,13,3174,3276,4607,0,0]Ab[2029,3,0,0,0,0,0]:6Aj[477,13,478,3740,3297,0,0]Aj[477,13,478,1126,2324,0,0]Al[3173,14,3174,1798,5160,0,0]Ai[3173,14,4893,0,4961,0,0]Ah[3173,14,4595,0,516,0,0]Ai[3173,14,4595,0,1216,0,0]Ai[3173,14,4595,0,4827,0,0]Aj[477,13,478,4162,3328,0,0]Al[3173,13,3174,2044,3329,0,0]Ak[1228,14,1229,1229,141,0,0]Al[1228,14,1417,1417,1221,0,0]Al[3173,13,3174,3741,3298,0,0]Ah[477,12,1735,0,3109,0,0]Al[3173,12,1736,1736,3110,0,0]Am[1228,13,1229,1229,141,128,0]An[1228,13,1229,1229,4075,128,0]An[1228,13,1229,1229,4047,128,0]An[1228,13,1417,1417,4083,128,0]An[1228,13,1417,1417,1221,128,0]An[1228,13,1417,1417,4486,128,0]Ak[536,13,537,537,4048,128,0]An[4088,13,4089,4089,5161,128,0]Al[477,13,478,3740,4611,128,0]Al[477,13,478,3740,2323,128,0]Ai[477,13,478,0,4338,128,0]Al[477,13,478,4270,5065,128,0]Al[477,13,478,1126,2324,128,0]Al[477,13,478,1126,4785,128,0]Al[477,13,478,3740,3295,128,0]Al[477,13,478,3740,3297,128,0]Al[477,13,478,3740,1889,128,0]Al[477,13,478,3740,1459,128,0]Al[477,13,478,3740,3076,128,0]Al[477,13,478,3740,2296,128,0]Al[477,13,478,3740,4232,128,0]Al[477,13,478,3740,4239,128,0]Al[477,13,478,3740,1450,128,0]Al[477,13,478,3740,5072,128,0]Al[477,13,478,3740,4898,128,0]Al[477,13,478,2353,2308,128,0]Al[477,13,478,2353,4347,128,0]Al[477,13,478,2353,1437,128,0]Al[477,13,478,4270,1458,128,0]Al[477,13,478,4270,3316,128,0]Al[477,13,478,4270,5000,128,0]Al[477,13,478,2385,2312,128,0]Al[477,13,478,2385,3325,128,0]Al[477,13,478,2385,4465,128,0]Ak[477,13,478,1126,520,128,0]Al[477,13,478,1126,4491,128,0]Al[477,13,478,4157,3322,128,0]Al[477,13,478,4157,5008,128,0]Al[477,13,478,4157,4484,128,0]Al[477,13,478,4157,3121,128,0]Al[477,13,478,4157,3324,128,0]Al[477,13,478,4157,4483,128,0]Al[477,13,478,1431,4249,128,0]Al[477,13,478,1169,4078,128,0]Al[477,13,478,4162,3328,128,0]Al[477,13,478,4162,3342,128,0]Al[477,13,478,4162,4248,128,0]Al[477,13,478,4162,1903,128,0]Ai[477,13,478,0,3319,128,0]Ai[477,13,478,0,4378,128,0]Ai[477,13,478,0,4795,128,0]Ai[477,13,478,0,4603,128,0]Ai[477,13,478,0,4222,128,0]Ai[477,13,478,0,4474,128,0]Ai[477,13,478,0,1480,128,0]Ai[477,13,478,0,5069,128,0]Ai[477,13,478,0,1184,128,0]Ai[477,13,478,0,1463,128,0]Ai[477,13,478,0,3317,128,0]Ai[477,13,478,0,3116,128,0]Ai[477,13,478,0,4954,128,0]Ai[477,13,478,0,3290,128,0]Ai[477,13,478,0,2343,128,0]Ai[477,13,478,0,3303,128,0]Ai[477,13,478,0,3118,128,0]Ai[477,13,478,0,4587,128,0]Ai[477,13,478,0,4194,128,0]Ai[477,13,478,0,4583,128,0]Ai[477,13,478,0,3112,128,0]Ai[477,13,478,0,4450,128,0]Ai[477,13,478,0,4597,128,0]Ai[477,13,478,0,4606,128,0]Ai[477,13,478,0,4924,128,0]Ai[477,13,478,0,4939,128,0]Ai[477,13,478,0,4064,128,0]Ai[477,13,478,0,4460,128,0]Ai[477,13,478,0,4242,128,0]Ai[477,13,478,0,5126,128,0]Ai[477,13,478,0,4188,128,0]Ai[477,13,478,0,4050,128,0]Ai[477,13,478,0,3099,128,0]Ai[477,13,478,0,4580,128,0]Ai[477,13,478,0,4787,128,0]Ai[477,13,478,0,4200,128,0]Ai[477,13,478,0,4203,128,0]Ai[477,13,478,0,4208,128,0]Ai[477,13,478,0,4959,128,0]Ai[477,13,478,0,3306,128,0]Ai[477,13,478,0,4821,128,0]Ah[477,13,478,0,526,128,0]Ah[477,13,478,0,133,128,0]Ai[477,13,478,0,2330,128,0]Ai[477,13,478,0,2340,128,0]Ai[477,13,478,0,3097,128,0]Ai[477,13,3735,0,501,128,0]Aj[477,13,3735,0,3286,128,0]Aj[477,13,3735,0,1188,128,0]Aj[477,13,3735,0,4055,128,0]Aj[477,13,3735,0,1886,128,0]Aj[477,13,3735,0,4366,128,0]Aj[477,13,3735,0,4885,128,0]Aj[477,13,1735,0,1881,128,0]Ai[477,13,1735,0,514,128,0]Aj[477,13,1735,0,5223,128,0]Aj[477,13,1735,0,4053,128,0]Aj[477,13,1735,0,4963,128,0]Aj[477,13,1818,0,3337,128,0]Aj[477,13,1818,0,4349,128,0]Aj[477,13,1818,0,1201,128,0]Aj[477,13,1818,0,4817,128,0]Aj[477,13,1818,0,5063,128,0]Aj[477,13,1818,0,4819,128,0]Am[2408,13,2409,2409,132,128,0]An[2408,13,2409,2409,2299,128,0]An[3173,13,3174,4616,4339,128,0]An[3173,13,3174,3147,3089,128,0]An[3173,13,3174,1152,1446,128,0]An[3173,13,3174,2044,1907,128,0]An[3173,13,3174,2044,3323,128,0]An[3173,13,3174,2044,3329,128,0]An[3173,13,3174,3741,3298,128,0]An[3173,13,3174,1148,3320,128,0]An[3173,13,3174,1733,4379,128,0]An[3173,13,3174,2354,2309,128,0]An[3173,13,3174,2354,4345,128,0]An[3173,13,3174,2354,4796,128,0]An[3173,13,3174,2354,4357,128,0]An[3173,13,3174,1798,5288,128,0]An[3173,13,3174,4387,5257,128,0]An[3173,13,3174,1430,4604,128,0]An[3173,13,3174,1430,4223,128,0]An[3173,13,3174,1430,4475,128,0]An[3173,13,3174,1856,1481,128,0]An[3173,13,3174,1856,5070,128,0]An[3173,13,3174,3138,1185,128,0]An[3173,13,3174,3147,4899,128,0]An[3173,13,3174,1403,1464,128,0]An[3173,13,3174,1403,4238,128,0]An[3173,13,3174,1403,3318,128,0]An[3173,13,3174,1403,3117,128,0]An[3173,13,3174,1403,4897,128,0]An[3173,13,3174,1403,4955,128,0]An[3173,13,3174,1403,3293,128,0]An[3173,13,3174,1418,2344,128,0]An[3173,13,3174,1418,3304,128,0]An[3173,13,3174,1418,3119,128,0]An[3173,13,3174,1418,4588,128,0]An[3173,13,3174,1152,3122,128,0]An[3173,13,3174,1152,4374,128,0]An[3173,13,3174,1157,3343,128,0]An[3173,13,3174,1157,4063,128,0]An[3173,13,3174,2044,4082,128,0]An[3173,13,3174,2044,4485,128,0]An[3173,13,3174,2044,4198,128,0]An[3173,13,3174,3741,4584,128,0]An[3173,13,3174,3741,3113,128,0]An[3173,13,3174,3741,4451,128,0]An[3173,13,3174,3741,5157,128,0]An[3173,13,3174,3741,1206,128,0]An[3173,13,3174,1148,4598,128,0]An[3173,13,3174,3276,4607,128,0]An[3173,13,3174,3276,4925,128,0]An[3173,13,3174,3276,4940,128,0]An[3173,13,3174,3276,4065,128,0]An[3173,13,3174,1733,4461,128,0]An[3173,13,3174,1733,4244,128,0]An[3173,13,3174,1733,5127,128,0]An[3173,13,3174,1733,4190,128,0]An[3173,13,3174,1733,4051,128,0]An[3173,13,3174,1393,3100,128,0]An[3173,13,3174,1393,4466,128,0]An[3173,13,3174,1393,3310,128,0]An[3173,13,3174,1393,3332,128,0]An[3173,13,3174,1393,4205,128,0]An[3173,13,3174,1393,4582,128,0]An[3173,13,3174,1393,4789,128,0]An[3173,13,3174,1393,2302,128,0]An[3173,13,3174,2354,4793,128,0]An[3173,13,3174,2354,2313,128,0]An[3173,13,3174,2354,4201,128,0]An[3173,13,3174,2354,4045,128,0]An[3173,13,3174,2354,3326,128,0]An[3173,13,3174,1798,4369,128,0]An[3173,13,3174,1798,4596,128,0]An[3173,13,3174,1798,5160,128,0]An[3173,13,3174,1798,2316,128,0]An[3173,13,3174,1798,4894,128,0]An[3173,13,3174,1829,4586,128,0]An[3173,13,3174,1829,4346,128,0]An[3173,13,3174,1829,4204,128,0]An[3173,13,3174,1829,4209,128,0]Ak[3173,13,3174,0,4489,128,0]Ak[3173,13,3174,0,4960,128,0]An[3173,13,3174,4387,3307,128,0]An[3173,13,3174,4387,5262,128,0]An[3173,13,3174,4995,4822,128,0]Am[3173,13,3174,3174,527,128,0]Am[3173,13,3174,3174,134,128,0]An[3173,13,3174,3174,2331,128,0]An[3173,13,3174,3174,2341,128,0]An[3173,13,3174,3174,3098,128,0]An[3173,13,1731,1731,4380,128,0]An[3173,13,5111,5111,5007,128,0]An[3173,13,5111,5111,1203,128,0]An[3173,13,1736,1736,1882,128,0]Am[3173,13,1736,1736,515,128,0]An[3173,13,1736,1736,5224,128,0]An[3173,13,1736,1736,4054,128,0]An[3173,13,1736,1736,4966,128,0]An[3173,13,1819,1819,3338,128,0]An[3173,13,1819,1819,4350,128,0]Ak[3173,13,1819,0,1202,128,0]An[3173,13,1819,1819,4818,128,0]An[3173,13,1819,1819,5064,128,0]An[3173,13,1819,1819,4820,128,0]Al[3173,13,3736,535,502,128,0]Am[3173,13,3736,535,3287,128,0]An[3173,13,3736,1389,1189,128,0]An[3173,13,3736,1389,4056,128,0]An[3173,13,3736,2408,1887,128,0]An[3173,13,3736,2408,4367,128,0]An[3173,13,3736,3736,4886,128,0]An[3173,13,4853,4853,1204,128,0]An[3173,13,4853,4853,1207,128,0]An[2029,13,2030,2030,3341,128,0]Am[2029,13,2030,431,4490,128,0]An[2029,13,2030,2030,2328,128,0]An[2029,13,2030,2030,2342,128,0]An[2029,13,2030,2030,4210,128,0]An[1127,13,1128,1128,3086,128,0]An[1127,13,1128,1128,4999,128,0]Am[1127,13,1725,1725,142,128,0]An[1127,13,1419,1419,1222,128,0]An[1127,13,1419,1419,4084,128,0]An[1127,13,1419,1419,4592,128,0]An[1127,13,4305,4305,5209,128,0]An[1127,13,4305,4305,5210,128,0]Am[1127,13,1824,1824,143,128,0]Aj[477,13,478,3740,5072,0,0]Aj[477,13,478,1126,4491,0,0]Ag[477,13,478,0,4188,0,0]Al[3173,13,3174,1733,4190,0,0]Al[3173,14,3174,1798,5160,0,0]Ai[3173,14,4893,0,4961,0,0]Ah[3173,14,4595,0,516,0,0]Ai[3173,14,4595,0,1216,0,0]Af[477,5,478,3740,0,0,0]Al[1228,15,1229,1229,4047,0,0]Ae[2408,6,2409,0,0,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]Ah[3173,5,3174,1393,0,0,0]Aj[477,15,478,3740,4898,0,0]Al[3173,15,3174,3147,4899,0,0]Af[477,5,478,2353,0,0,0]Ah[3173,5,3174,2354,0,0,0]Al[3173,15,3174,2044,4082,0,0]Af[477,5,478,2385,0,0,0]2Ah[3173,6,3174,1798,0,0,0]Ag[477,19,478,0,1480,0,0]Al[3173,19,3174,1856,1481,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]=Ah[477,12,1735,0,4243,0,0]>Ak[477,13,1818,0,4819,4243,0]>Al[3173,12,1736,1736,4245,0,0]>Ao[3173,13,1819,1819,4820,4245,0]Ao[1127,13,4305,4305,5210,4245,0]An[2408,13,2409,2409,132,4066,0]Ao[2408,13,2409,2409,2299,4066,0]Ag[477,14,478,0,3118,0,0]Ag[477,14,478,0,4587,0,0]Al[3173,14,3174,1418,3119,0,0]Al[3173,14,3174,1418,4588,0,0]Ag[477,13,478,0,4242,0,0]Al[3173,13,3174,1733,4244,0,0]Aj[477,13,478,2036,4448,0,0]0Ak[2029,13,2030,431,4490,0,0]Ai[477,13,478,1126,520,0,0]Al[3173,13,3174,3741,3298,0,0]Al[1127,13,1419,1419,4592,0,0]Ae[1127,2,1128,0,0,0,0]Aj[477,13,478,2385,2312,0,0]Al[3173,13,3174,2354,2313,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1418,3304,0,0]6Ag[477,13,478,0,4606,0,0]Al[3173,13,3174,3276,4607,0,0]32Ak[1228,14,1229,1229,141,0,0]Ag[477,14,478,0,2340,0,0]Al[3173,14,3174,3174,2341,0,0]Al[2029,14,2030,2030,2342,0,0]Ah[477,14,3735,0,4885,0,0]Al[3173,14,3736,3736,4886,0,0]Al[3173,13,3174,2044,1907,0,0]Al[3173,13,3174,4387,5257,0,0]Al[3173,13,3174,4387,5262,0,0]Aj[477,13,478,4162,3328,0,0]Al[3173,13,3174,2044,3329,0,0]>=Al[1228,14,1417,1417,1221,0,0]Ai[536,14,537,537,4048,0,0]Ah[477,13,1818,0,4817,0,0]Ah[477,13,1818,0,4819,0,0]Al[3173,13,1819,1819,4818,0,0]Al[3173,13,1819,1819,4820,0,0]6Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]Af[477,5,478,4616,0,0,0]Ah[3173,5,3174,4616,0,0,0]Ag[477,15,478,0,4583,0,0]Ag[477,15,478,0,4200,0,0]Al[3173,15,3174,3741,4584,0,0]Al[3173,15,3174,2354,4201,0,0]Ah[3173,6,3174,2354,0,0,0]Ah[3173,5,3174,1829,0,0,0]Af[477,5,478,2353,0,0,0]Aj[477,15,478,4157,4483,0,0]Ag[477,5,1818,1819,0,0,0]Ae[3173,5,1819,0,0,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Ah[3173,5,3174,2354,0,0,0]Al[3173,15,3174,1798,4894,0,0]?=5454Al[3173,15,3174,2354,4345,0,0]Ag[477,5,3735,2408,0,0,0]Ah[3173,5,3736,2408,0,0,0]Ah[3173,5,3174,1798,0,0,0]98Ah[3173,6,3174,1152,0,0,0]:9Af[477,5,478,1733,0,0,0]Ah[3173,5,3174,1733,0,0,0]Ae[3173,5,1731,0,0,0,0]Al[3173,13,3174,3741,3298,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1418,3304,0,0]Al[1127,14,1128,1128,3086,0,0]Ae[3173,2,3174,0,0,0,0]Ag[477,13,478,0,4460,0,0]Al[3173,13,3174,1733,4461,0,0]Aj[477,12,478,2036,4448,0,0]Ab[4390,3,0,0,0,0,0]Ak[1228,14,1229,1229,141,0,0]Al[1228,14,1417,1417,4083,0,0]98Aj[477,13,478,2385,3325,0,0]Al[3173,13,3174,2354,3326,0,0]Ag[477,13,478,0,4924,0,0]Al[3173,13,3174,3276,4925,0,0];==;:Ak[1228,14,1229,1229,141,0,0]21Al[1127,14,1128,1128,3086,0,0]Aj[477,13,478,4157,3322,0,0]Aj[477,13,478,4157,5008,0,0]Al[3173,13,3174,2044,3323,0,0]Aj[477,13,478,3740,3295,0,0]Aj[477,13,478,2385,2312,0,0]Ai[477,13,478,1126,520,0,0]Al[3173,13,3174,2354,2313,0,0]<;Ag[477,13,478,0,1480,0,0]Ag[477,13,478,0,1463,0,0]Al[3173,13,3174,1856,1481,0,0]Al[3173,13,3174,1403,1464,0,0]20Ae[1228,7,1417,0,0,0,0]?>Al[1228,14,1417,1417,1221,0,0]Ao[1127,13,1128,1128,4999,3127,0]Ag[477,15,478,0,3099,0,0]Af[477,5,478,1393,0,0,0]Al[3173,15,3174,1393,3100,0,0]Ah[3173,5,3174,1393,0,0,0]Af[477,6,478,3741,0,0,0]Ah[3173,6,3174,3741,0,0,0]Al[3173,15,3174,2354,4345,0,0]Ah[3173,5,3174,1829,0,0,0]Af[477,5,478,1418,0,0,0]Ah[3173,5,3174,1418,0,0,0]97Ae[1127,5,1419,0,0,0,0]:8Ah[3173,6,3174,1798,0,0,0]Af[477,5,478,1148,0,0,0]Ah[3173,5,3174,1148,0,0,0]Al[3173,15,3174,1403,4897,0,0]Af[477,5,478,1430,0,0,0]Ah[3173,5,3174,1430,0,0,0]Af[477,5,478,3276,0,0,0]Ah[3173,5,3174,3276,0,0,0]Ag[477,15,478,0,4583,0,0]Al[3173,15,3174,3741,4584,0,0]Af[477,5,478,3740,0,0,0]Aj[477,15,478,4157,3121,0,0]Aj[477,15,478,4157,3324,0,0]Aj[477,15,478,4162,3342,0,0]Aj[477,15,478,4162,4248,0,0]An[1228,13,1229,1229,141,4344,0]Ao[1228,13,1229,1229,4075,4344,0]Ao[1228,13,1229,1229,4047,4344,0]Am[477,13,478,3740,3297,4344,0]Am[477,13,478,3740,1450,4344,0]Am[477,13,478,3740,5072,4344,0]Am[477,13,478,3740,4898,4344,0]Am[477,13,478,2353,2308,4344,0]Am[477,13,478,2353,4347,4344,0]Am[477,13,478,4270,1458,4344,0]Al[477,13,478,1126,520,4344,0]Am[477,13,478,1126,4491,4344,0]Am[477,13,478,4157,4484,4344,0]Am[477,13,478,4157,3121,4344,0]Am[477,13,478,4157,3324,4344,0]Am[477,13,478,4157,4483,4344,0]Am[477,13,478,4162,3342,4344,0]Am[477,13,478,4162,4248,4344,0]Aj[477,13,478,0,1480,4344,0]Aj[477,13,478,0,5069,4344,0]Aj[477,13,478,0,1463,4344,0]Aj[477,13,478,0,3317,4344,0]Aj[477,13,478,0,3116,4344,0]Aj[477,13,478,0,4954,4344,0]Aj[477,13,478,0,3290,4344,0]Aj[477,13,478,0,2343,4344,0]Aj[477,13,478,0,3303,4344,0]Aj[477,13,478,0,3118,4344,0]Aj[477,13,478,0,4587,4344,0]Aj[477,13,478,0,4194,4344,0]Aj[477,13,478,0,4583,4344,0]Aj[477,13,478,0,3112,4344,0]Aj[477,13,478,0,4450,4344,0]Aj[477,13,478,0,4606,4344,0]Aj[477,13,478,0,4924,4344,0]Aj[477,13,478,0,4939,4344,0]Aj[477,13,478,0,4064,4344,0]Aj[477,13,478,0,4460,4344,0]Aj[477,13,478,0,4242,4344,0]Aj[477,13,478,0,5126,4344,0]Aj[477,13,478,0,4188,4344,0]Aj[477,13,478,0,4050,4344,0]Aj[477,13,478,0,3099,4344,0]Aj[477,13,478,0,4580,4344,0]Aj[477,13,478,0,4787,4344,0]Aj[477,13,478,0,4200,4344,0]Aj[477,13,478,0,4203,4344,0]Aj[477,13,478,0,4208,4344,0]Aj[477,13,478,0,4959,4344,0]Aj[477,13,478,0,3306,4344,0]Ai[477,13,478,0,526,4344,0]Ai[477,13,478,0,133,4344,0]Aj[477,13,478,0,2330,4344,0]Aj[477,13,478,0,3097,4344,0]Aj[477,13,3735,0,501,4344,0]Ak[477,13,3735,0,3286,4344,0]Ak[477,13,3735,0,1188,4344,0]Ak[477,13,3735,0,4055,4344,0]Ak[477,13,3735,0,1886,4344,0]Ak[477,13,3735,0,4366,4344,0]Ak[477,13,1818,0,1201,4344,0]Ak[477,13,1818,0,4817,4344,0]Ak[477,13,1818,0,5063,4344,0]Ak[477,13,1818,0,4819,4344,0]Ao[3173,13,3174,1856,1481,4344,0]Ao[3173,13,3174,1856,5070,4344,0]Ao[3173,13,3174,3147,4899,4344,0]Ao[3173,13,3174,1403,1464,4344,0]Ao[3173,13,3174,1403,4238,4344,0]Ao[3173,13,3174,1403,3318,4344,0]Ao[3173,13,3174,1403,3117,4344,0]Ao[3173,13,3174,1403,4897,4344,0]Ao[3173,13,3174,1403,4955,4344,0]Ao[3173,13,3174,1403,3293,4344,0]Ao[3173,13,3174,1418,2344,4344,0]Ao[3173,13,3174,1418,3304,4344,0]Ao[3173,13,3174,1418,3119,4344,0]Ao[3173,13,3174,1418,4588,4344,0]Ao[3173,13,3174,1152,3122,4344,0]Ao[3173,13,3174,1152,4374,4344,0]Ao[3173,13,3174,1157,3343,4344,0]Ao[3173,13,3174,1157,4063,4344,0]Ao[3173,13,3174,2044,4485,4344,0]Ao[3173,13,3174,2044,4198,4344,0]Ao[3173,13,3174,3741,4584,4344,0]Ao[3173,13,3174,3741,3113,4344,0]Ao[3173,13,3174,3741,4451,4344,0]Ao[3173,13,3174,3741,5157,4344,0]Ao[3173,13,3174,3276,4607,4344,0]Ao[3173,13,3174,3276,4925,4344,0]Ao[3173,13,3174,3276,4940,4344,0]Ao[3173,13,3174,3276,4065,4344,0]Ao[3173,13,3174,1733,4461,4344,0]Ao[3173,13,3174,1733,4244,4344,0]Ao[3173,13,3174,1733,5127,4344,0]Ao[3173,13,3174,1733,4190,4344,0]Ao[3173,13,3174,1733,4051,4344,0]Ao[3173,13,3174,1393,3100,4344,0]Ao[3173,13,3174,1393,3310,4344,0]Ao[3173,13,3174,1393,3332,4344,0]Ao[3173,13,3174,1393,4205,4344,0]Ao[3173,13,3174,1393,4582,4344,0]Ao[3173,13,3174,1393,4789,4344,0]Ao[3173,13,3174,1393,2302,4344,0]Ao[3173,13,3174,2354,4793,4344,0]Ao[3173,13,3174,2354,4201,4344,0]Ao[3173,13,3174,1798,4596,4344,0]Ao[3173,13,3174,1798,5160,4344,0]Ao[3173,13,3174,1798,2316,4344,0]Ao[3173,13,3174,1798,4894,4344,0]Ao[3173,13,3174,1829,4204,4344,0]Ao[3173,13,3174,1829,4209,4344,0]Al[3173,13,3174,0,4489,4344,0]Al[3173,13,3174,0,4960,4344,0]Ao[3173,13,3174,4387,3307,4344,0]Ao[3173,13,3174,4387,5262,4344,0]An[3173,13,3174,3174,527,4344,0]An[3173,13,3174,3174,134,4344,0]Ao[3173,13,3174,3174,2331,4344,0]Ao[3173,13,3174,3174,3098,4344,0]Ao[3173,13,1731,1731,4380,4344,0]Ao[3173,13,5111,5111,5007,4344,0]Ao[3173,13,5111,5111,1203,4344,0]Al[3173,13,1819,0,1202,4344,0]Ao[3173,13,1819,1819,4818,4344,0]Ao[3173,13,1819,1819,5064,4344,0]Ao[3173,13,1819,1819,4820,4344,0]Am[3173,13,3736,535,502,4344,0]An[3173,13,3736,535,3287,4344,0]Ao[3173,13,3736,1389,1189,4344,0]Ao[3173,13,3736,1389,4056,4344,0]Ao[3173,13,3736,2408,1887,4344,0]Ao[3173,13,3736,2408,4367,4344,0]Ao[3173,13,4853,4853,1207,4344,0]Ao[2029,13,2030,2030,2328,4344,0]Ao[2029,13,2030,2030,2342,4344,0]Ao[2029,13,2030,2030,4210,4344,0]Ao[1127,13,1128,1128,4999,4344,0]Ao[1127,13,1419,1419,4592,4344,0]Ao[1127,13,4305,4305,5209,4344,0]Ao[1127,13,4305,4305,5210,4344,0]Af[477,7,478,2036,0,0,0]Ag[477,13,478,0,4460,0,0]Al[3173,13,3174,1733,4461,0,0]Ah[477,12,1735,0,3109,0,0]Al[3173,12,1736,1736,3110,0,0]Al[2029,14,2030,2030,2328,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1418,3304,0,0]10Ae[1127,7,1128,0,0,0,0]Aj[477,13,478,2353,2308,0,0]Ae[4390,7,4391,0,0,0,0]Ae[1228,7,1229,0,0,0,0]Al[3173,13,3174,3741,3298,0,0]Aj[477,13,478,3740,3297,0,0]1Al[3173,13,3174,2044,4485,0,0]Al[1228,13,1417,1417,1221,0,0]Al[1228,14,1417,1417,4083,0,0]Aj[477,12,478,2036,1898,0,0]Am[477,13,478,1431,4249,1898,0]Am[477,13,478,1169,4078,1898,0]Ah[477,12,1735,0,1899,0,0]Ak[477,13,1818,0,3337,1899,0]Al[3173,12,1736,1736,1900,0,0]Ao[3173,13,1819,1819,3338,1900,0]Af[477,5,478,1126,0,0,0]Ag[477,15,478,0,3099,0,0]Af[477,5,478,1393,0,0,0]Al[3173,15,3174,1393,3100,0,0]Ah[3173,5,3174,1393,0,0,0]Ag[477,15,478,0,4583,0,0]Al[3173,15,3174,3741,4584,0,0]Ah[3173,5,3174,2354,0,0,0]Al[3173,15,3174,3741,1206,0,0]Af[477,5,478,2354,0,0,0]2Ag[477,15,478,0,4200,0,0]Al[3173,15,3174,2354,4201,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]32Ag[477,15,478,0,3112,0,0]Al[3173,15,3174,3741,3113,0,0]Ag[477,15,478,0,3099,0,0]?0?0?43Ag[477,5,1818,1819,0,0,0]Ae[3173,5,1819,0,0,0,0]10Af[477,5,478,4995,0,0,0]Ah[3173,5,3174,4995,0,0,0]Al[3173,15,4853,4853,1204,0,0]7Al[3173,15,3174,1403,4897,0,0]7Al[3173,15,3174,1798,4596,0,0]Ag[477,12,478,0,2336,0,0]Aj[477,13,478,0,4338,2336,0]Aj[477,13,478,0,4222,2336,0]Al[3173,12,3174,1430,2337,0,0]Ao[3173,13,3174,4616,4339,2337,0]Ao[3173,13,3174,1430,4223,2337,0]Ae[4088,7,4089,0,0,0,0]Aj[477,13,478,3740,3297,0,0]Al[3173,13,3174,3741,3298,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1418,3304,0,0]32An[1228,13,1229,1229,141,4462,0]An[2408,13,2409,2409,132,4462,0]Ao[2408,13,2409,2409,2299,4462,0]Ao[1127,13,1128,1128,3086,4462,0]An[1127,13,1725,1725,142,4462,0]An[1127,13,1824,1824,143,4462,0]Ak[2029,13,2030,431,4490,0,0]Ag[477,13,478,0,4606,0,0]Al[3173,13,3174,3276,4607,0,0]:9Ae[3173,2,3174,0,0,0,0]Aj[477,13,478,2353,2308,0,0]Al[3173,13,3174,2354,2309,0,0]?>43Aj[477,13,478,4157,3322,0,0]Ae[4390,7,4391,0,0,0,0]7Al[1127,14,1128,1128,3086,0,0]80Ae[1228,7,1229,0,0,0,0]Ag[477,13,478,0,4242,0,0]Ag[477,13,478,0,4188,0,0]Al[3173,13,3174,1733,4244,0,0]Al[3173,13,3174,1733,4190,0,0]Ag[477,14,478,0,4242,0,0]Al[3173,14,3174,1733,4244,0,0]53Aj[477,13,478,4162,3328,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Ag[477,15,478,0,4583,0,0]Al[3173,15,3174,1403,4897,0,0]Al[3173,15,3174,3741,4584,0,0]Ah[477,10,3735,3736,0,0,0]Af[3173,10,3736,0,0,0,0]Ag[477,5,3735,3736,0,0,0]Ae[3173,5,3736,0,0,0,0]Ah[477,10,1735,1736,0,0,0]Af[3173,10,1736,0,0,0,0]Ag[477,15,478,0,4200,0,0]Al[3173,15,3174,2354,4201,0,0]Ag[477,10,478,3741,0,0,0]Ai[3173,10,3174,3741,0,0,0]Ah[3173,6,3174,1798,0,0,0]Ah[3173,6,3174,1403,0,0,0]Af[477,6,478,3740,0,0,0]Ah[3173,6,3174,3147,0,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]10Aj[477,15,478,3740,4898,0,0]Al[3173,15,3174,3147,4899,0,0]Ag[477,14,478,0,3118,0,0]Al[3173,14,3174,1418,3119,0,0]Ah[477,14,3735,0,4885,0,0]Al[3173,14,3736,3736,4886,0,0]Aj[477,13,478,3740,3297,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1418,3304,0,0]10Al[3173,13,3174,3147,3089,0,0]Aj[477,13,478,2353,2308,0,0]Al[3173,13,3174,2354,2309,0,0]Ab[4917,3,0,0,0,0,0]Ao[1127,13,1128,1128,4999,3127,0]3Aj[477,13,478,4162,3328,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Af[477,5,478,3276,0,0,0]Ah[3173,5,3174,3276,0,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]54Ai[3173,10,3174,1798,0,0,0]Al[3173,15,3174,3741,1206,0,0]7600Al[3173,15,3174,1152,4374,0,0]4365Al[3173,15,4853,4853,1204,0,0]Ae[1127,9,1128,0,0,0,0]:9Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]<;<;Af[477,6,478,1403,0,0,0]Ah[3173,6,3174,1403,0,0,0]32Al[1228,15,1229,1229,4075,0,0]Ac[477,5,478,0,0,0,0]Ae[3173,5,3174,0,0,0,0]Ag[477,15,478,0,4200,0,0]Ag[477,5,1735,1736,0,0,0]Al[3173,15,3174,2354,4201,0,0]Al[3173,15,3174,1798,4894,0,0]Ae[3173,5,1736,0,0,0,0]An[1228,13,1229,1229,141,4452,0]An[2408,13,2409,2409,132,4452,0]Ao[1127,13,1128,1128,3086,4452,0]An[1127,13,1725,1725,142,4452,0]An[1127,13,1824,1824,143,4452,0]Ag[477,13,478,0,4606,0,0]Al[3173,13,3174,3276,4607,0,0]Ag[477,13,478,0,1480,0,0]Al[3173,13,3174,1856,1481,0,0]Aj[477,14,478,2353,4347,0,0]Aj[477,13,478,3740,5072,0,0]Ag[477,13,478,0,4460,0,0]Al[3173,13,3174,1733,4461,0,0]Ag[477,13,478,0,4338,0,0]Al[3173,13,3174,4616,4339,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1418,3304,0,0]7Al[3173,13,3174,3741,3298,0,0]Aj[477,13,478,3740,3297,0,0]Ah[477,13,1818,0,4817,0,0]Ah[477,13,1818,0,4819,0,0]Al[3173,13,1819,1819,4818,0,0]Al[3173,13,1819,1819,4820,0,0]45;:Ao[4088,13,4089,4089,5161,4953,0]Ag[477,13,478,0,4597,0,0]Al[3173,13,3174,1148,4598,0,0]Ae[1127,6,1128,0,0,0,0]Af[477,5,478,4270,0,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]10Ag[477,15,478,0,3112,0,0]Al[3173,15,3174,3741,3113,0,0]Ae[3173,5,5111,0,0,0,0]Af[477,5,478,4157,0,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]Al[3173,15,4853,4853,1204,0,0]Aj[477,14,478,2353,4347,0,0]Ai[477,13,478,0,526,3078,0]Ai[477,13,478,0,133,3078,0]An[3173,13,3174,3174,527,3078,0]An[3173,13,3174,3174,134,3078,0]Ao[2408,13,2409,2409,2299,3333,0]Ag[477,13,478,0,4939,0,0]Al[3173,13,3174,3276,4940,0,0]Ah[477,12,1735,0,1899,0,0]Ak[477,13,1818,0,3337,1899,0]Al[3173,12,1736,1736,1900,0,0]Ao[3173,13,1819,1819,3338,1900,0]Aj[477,13,478,3740,3297,0,0]Ag[477,12,478,0,4891,0,0]Al[3173,12,3174,3741,4892,0,0]Ag[477,13,478,0,4606,0,0]Al[3173,13,3174,3276,4607,0,0]Ag[477,14,478,0,3118,0,0]Al[3173,14,3174,1418,3119,0,0]Ah[477,12,3735,0,3299,0,0]Aj[477,13,3735,0,501,3299,0]Ak[477,13,3735,0,1188,3299,0]Ak[477,13,3735,0,1886,3299,0]Al[3173,12,3736,3736,3300,0,0]Am[3173,13,3736,535,502,3300,0]Ao[3173,13,3736,1389,1189,3300,0]Ao[3173,13,3736,2408,1887,3300,0]Aj[477,14,478,2353,4347,0,0]Al[3173,13,3174,2044,1907,0,0]Ah[3173,2,4385,4161,0,0,0]Ae[1228,7,1229,0,0,0,0]==Ai[477,13,478,1126,520,0,0]Ak[2029,13,2030,431,4490,0,0]032Ah[477,12,1735,0,1899,0,0]Ak[477,13,1818,0,3337,1899,0]Ae[3173,2,3174,0,0,0,0]Al[3173,12,1736,1736,1900,0,0]Ao[3173,13,1819,1819,3338,1900,0]Ag[477,10,478,4387,0,0,0]Ai[3173,10,3174,4387,0,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]Ag[477,15,478,0,4200,0,0]Al[3173,15,3174,2354,4201,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Af[477,5,478,1733,0,0,0]Ah[3173,5,3174,1733,0,0,0]Ah[477,12,1735,0,3109,0,0]Al[3173,12,1736,1736,3110,0,0]Aj[477,13,478,2353,2308,0,0]Al[3173,13,3174,3741,3298,0,0]Al[3173,13,3174,2354,2309,0,0]Ah[477,13,3735,0,4366,0,0]Al[3173,13,3736,2408,4367,0,0]Aj[477,13,478,2036,3125,0,0]0Aj[477,13,478,3740,3297,0,0]5Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1418,3304,0,0]Ag[477,13,478,0,4606,0,0]Al[3173,13,3174,3276,4607,0,0]Al[1127,14,1128,1128,3086,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Al[3173,15,4853,4853,1204,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]10Ag[477,15,478,0,4954,0,0]Ag[477,15,478,0,3112,0,0]Ag[477,15,478,0,4450,0,0]Al[3173,15,3174,1403,4955,0,0]Al[3173,15,3174,3741,3113,0,0]Al[3173,15,3174,3741,4451,0,0]Ah[3173,5,3174,3741,0,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]Ah[3173,5,3174,1798,0,0,0]Ae[4088,5,4089,0,0,0,0]Al[1127,13,1128,1128,3086,0,0]Aj[477,13,478,3740,3297,0,0]Ah[477,12,1735,0,1899,0,0]Ak[477,13,1818,0,3337,1899,0]Al[3173,12,1736,1736,1900,0,0]Ao[3173,13,1819,1819,3338,1900,0]Ah[3173,7,5042,5041,0,0,0]0Al[3173,13,3174,3741,3298,0,0]6Ai[477,13,478,1126,520,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1418,3304,0,0]Ao[4088,13,4089,4089,5161,4953,0]0Ag[477,13,478,0,4188,0,0]Al[3173,13,3174,1733,4190,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]10Ag[477,13,478,0,4924,0,0]Al[3173,13,3174,3276,4925,0,0]Am[1228,13,1229,1229,141,499,0]Am[2408,13,2409,2409,132,499,0]An[1127,13,1128,1128,3086,499,0]Am[1127,13,1725,1725,142,499,0]Am[1127,13,1824,1824,143,499,0]??Ah[477,13,3735,0,3299,0,0]Aj[477,13,3735,0,501,3299,0]Al[3173,13,3736,3736,3300,0,0]Am[3173,13,3736,535,502,3300,0]Ai[477,13,478,1126,520,0,0]An[1228,13,1229,1229,141,4462,0]An[2408,13,2409,2409,132,4462,0]Ao[2408,13,2409,2409,2299,4462,0]Ao[1127,13,1128,1128,3086,4462,0]An[1127,13,1725,1725,142,4462,0]An[1127,13,1824,1824,143,4462,0]Ag[477,13,478,0,4242,0,0]Al[3173,13,3174,1733,4244,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Ae[1127,5,4305,0,0,0,0]0Ag[477,13,478,0,4460,0,0]Al[3173,13,3174,1733,4461,0,0]Ag[477,13,478,0,4188,0,0]Al[3173,13,3174,1733,4190,0,0]Aj[477,13,478,2353,2308,0,0]Aj[477,13,478,2385,2312,0,0]Al[3173,13,3174,2354,2313,0,0]Al[3173,13,3174,3741,3298,0,0]Ao[4088,13,4089,4089,5161,4953,0];:Ag[477,5,1735,1736,0,0,0]Ae[3173,5,1736,0,0,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]?>?>Aj[477,13,478,3740,3297,0,0]6Ag[477,13,478,0,4606,0,0]Al[3173,13,3174,3276,4607,0,0]Ae[4088,7,4089,0,0,0,0]Ae[4390,7,4391,0,0,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1418,3304,0,0]?Ah[477,12,1735,0,4243,0,0]>Ak[477,13,1818,0,4819,4243,0]>Al[3173,12,1736,1736,4245,0,0]>Ao[3173,13,1819,1819,4820,4245,0]Ao[1127,13,4305,4305,5210,4245,0]An[2408,13,2409,2409,132,4066,0]Ao[2408,13,2409,2409,2299,4066,0]Ag[477,14,478,0,3118,0,0]Ag[477,14,478,0,4587,0,0]Al[3173,14,3174,1419,3119,0,0]Al[3173,14,3174,1419,4588,0,0]Ag[477,13,478,0,4242,0,0]Al[3173,13,3174,1733,4244,0,0]Aj[477,13,478,2036,4448,0,0]0Ak[2029,13,2030,431,4490,0,0]Ai[477,13,478,1126,520,0,0]Al[3173,13,3174,3741,3298,0,0]Al[1127,13,1418,1418,4592,0,0]Ae[1127,2,1128,0,0,0,0]Aj[477,13,478,2385,2312,0,0]Al[3173,13,3174,2354,2313,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1419,3304,0,0]6Ag[477,13,478,0,4606,0,0]Al[3173,13,3174,3276,4607,0,0]32Ak[1228,14,1229,1229,141,0,0]Ag[477,14,478,0,2340,0,0]Al[3173,14,3174,3174,2341,0,0]Al[2029,14,2030,2030,2342,0,0]Ah[477,14,3735,0,4885,0,0]Al[3173,14,3736,3736,4886,0,0]Al[3173,13,3174,2044,1907,0,0]Al[3173,13,3174,4387,5257,0,0]Al[3173,13,3174,4387,5262,0,0]Aj[477,13,478,4162,3328,0,0]Al[3173,13,3174,2044,3329,0,0]>=Al[1228,14,1417,1417,1221,0,0]Ai[536,14,537,537,4048,0,0]Ah[477,13,1818,0,4817,0,0]Ah[477,13,1818,0,4819,0,0]Al[3173,13,1819,1819,4818,0,0]Al[3173,13,1819,1819,4820,0,0]6Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]Af[477,5,478,4616,0,0,0]Ah[3173,5,3174,4616,0,0,0]Ag[477,15,478,0,4583,0,0]Ag[477,15,478,0,4200,0,0]Al[3173,15,3174,3741,4584,0,0]Al[3173,15,3174,2354,4201,0,0]Ah[3173,6,3174,2354,0,0,0]Ah[3173,5,3174,1829,0,0,0]Af[477,5,478,2353,0,0,0]Aj[477,15,478,4157,4483,0,0]Ag[477,5,1818,1819,0,0,0]Ae[3173,5,1819,0,0,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Ah[3173,5,3174,2354,0,0,0]Al[3173,15,3174,1798,4894,0,0]?=5454Al[3173,15,3174,2354,4345,0,0]Ag[477,5,3735,2408,0,0,0]Ah[3173,5,3736,2408,0,0,0]Ah[3173,5,3174,1798,0,0,0]98Ah[3173,6,3174,1152,0,0,0]:9Af[477,5,478,1733,0,0,0]Ah[3173,5,3174,1733,0,0,0]Ae[3173,5,1731,0,0,0,0]Al[3173,13,3174,3741,3298,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1419,3304,0,0]Al[1127,14,1128,1128,3086,0,0]Ae[3173,2,3174,0,0,0,0]Ag[477,13,478,0,4460,0,0]Al[3173,13,3174,1733,4461,0,0]Aj[477,12,478,2036,4448,0,0]Ab[4390,3,0,0,0,0,0]Ak[1228,14,1229,1229,141,0,0]Al[1228,14,1417,1417,4083,0,0]98Aj[477,13,478,2385,3325,0,0]Al[3173,13,3174,2354,3326,0,0]Ag[477,13,478,0,4924,0,0]Al[3173,13,3174,3276,4925,0,0];==;:Ak[1228,14,1229,1229,141,0,0]21Al[1127,14,1128,1128,3086,0,0]Aj[477,13,478,4157,3322,0,0]Aj[477,13,478,4157,5008,0,0]Al[3173,13,3174,2044,3323,0,0]Aj[477,13,478,3740,3295,0,0]Aj[477,13,478,2385,2312,0,0]Ai[477,13,478,1126,520,0,0]Al[3173,13,3174,2354,2313,0,0]<;Ag[477,13,478,0,1480,0,0]Ag[477,13,478,0,1463,0,0]Al[3173,13,3174,1856,1481,0,0]Al[3173,13,3174,1403,1464,0,0]20Ae[1228,7,1417,0,0,0,0]?>Al[1228,14,1417,1417,1221,0,0]Ao[1127,13,1128,1128,4999,3127,0]Ag[477,15,478,0,3099,0,0]Af[477,5,478,1393,0,0,0]Al[3173,15,3174,1393,3100,0,0]Ah[3173,5,3174,1393,0,0,0]Af[477,6,478,3741,0,0,0]Ah[3173,6,3174,3741,0,0,0]Al[3173,15,3174,2354,4345,0,0]Ah[3173,5,3174,1829,0,0,0]Af[477,5,478,1419,0,0,0]Ah[3173,5,3174,1419,0,0,0]97Ae[1127,5,1418,0,0,0,0]:8Ah[3173,6,3174,1798,0,0,0]Af[477,5,478,1148,0,0,0]Ah[3173,5,3174,1148,0,0,0]Al[3173,15,3174,1403,4897,0,0]Af[477,5,478,1430,0,0,0]Ah[3173,5,3174,1430,0,0,0]Af[477,5,478,3276,0,0,0]Ah[3173,5,3174,3276,0,0,0]Ag[477,15,478,0,4583,0,0]Al[3173,15,3174,3741,4584,0,0]Af[477,5,478,3740,0,0,0]Aj[477,15,478,4157,3121,0,0]Aj[477,15,478,4157,3324,0,0]Aj[477,15,478,4162,3342,0,0]Aj[477,15,478,4162,4248,0,0]An[1228,13,1229,1229,141,4344,0]Ao[1228,13,1229,1229,4075,4344,0]Ao[1228,13,1229,1229,4047,4344,0]Am[477,13,478,3740,3297,4344,0]Am[477,13,478,3740,1450,4344,0]Am[477,13,478,3740,5072,4344,0]Am[477,13,478,3740,4898,4344,0]Am[477,13,478,2353,2308,4344,0]Am[477,13,478,2353,4347,4344,0]Am[477,13,478,4270,1458,4344,0]Al[477,13,478,1126,520,4344,0]Am[477,13,478,1126,4491,4344,0]Am[477,13,478,4157,4484,4344,0]Am[477,13,478,4157,3121,4344,0]Am[477,13,478,4157,3324,4344,0]Am[477,13,478,4157,4483,4344,0]Am[477,13,478,4162,3342,4344,0]Am[477,13,478,4162,4248,4344,0]Aj[477,13,478,0,1480,4344,0]Aj[477,13,478,0,5069,4344,0]Aj[477,13,478,0,1463,4344,0]Aj[477,13,478,0,3317,4344,0]Aj[477,13,478,0,3116,4344,0]Aj[477,13,478,0,4954,4344,0]Aj[477,13,478,0,3290,4344,0]Aj[477,13,478,0,2343,4344,0]Aj[477,13,478,0,3303,4344,0]Aj[477,13,478,0,3118,4344,0]Aj[477,13,478,0,4587,4344,0]Aj[477,13,478,0,4194,4344,0]Aj[477,13,478,0,4583,4344,0]Aj[477,13,478,0,3112,4344,0]Aj[477,13,478,0,4450,4344,0]Aj[477,13,478,0,4606,4344,0]Aj[477,13,478,0,4924,4344,0]Aj[477,13,478,0,4939,4344,0]Aj[477,13,478,0,4064,4344,0]Aj[477,13,478,0,4460,4344,0]Aj[477,13,478,0,4242,4344,0]Aj[477,13,478,0,5126,4344,0]Aj[477,13,478,0,4188,4344,0]Aj[477,13,478,0,4050,4344,0]Aj[477,13,478,0,3099,4344,0]Aj[477,13,478,0,4580,4344,0]Aj[477,13,478,0,4787,4344,0]Aj[477,13,478,0,4200,4344,0]Aj[477,13,478,0,4203,4344,0]Aj[477,13,478,0,4208,4344,0]Aj[477,13,478,0,4959,4344,0]Aj[477,13,478,0,3306,4344,0]Ai[477,13,478,0,526,4344,0]Ai[477,13,478,0,133,4344,0]Aj[477,13,478,0,2330,4344,0]Aj[477,13,478,0,3097,4344,0]Aj[477,13,3735,0,501,4344,0]Ak[477,13,3735,0,3286,4344,0]Ak[477,13,3735,0,1188,4344,0]Ak[477,13,3735,0,4055,4344,0]Ak[477,13,3735,0,1886,4344,0]Ak[477,13,3735,0,4366,4344,0]Ak[477,13,1818,0,1201,4344,0]Ak[477,13,1818,0,4817,4344,0]Ak[477,13,1818,0,5063,4344,0]Ak[477,13,1818,0,4819,4344,0]Ao[3173,13,3174,1856,1481,4344,0]Ao[3173,13,3174,1856,5070,4344,0]Ao[3173,13,3174,3147,4899,4344,0]Ao[3173,13,3174,1403,1464,4344,0]Ao[3173,13,3174,1403,4238,4344,0]Ao[3173,13,3174,1403,3318,4344,0]Ao[3173,13,3174,1403,3117,4344,0]Ao[3173,13,3174,1403,4897,4344,0]Ao[3173,13,3174,1403,4955,4344,0]Ao[3173,13,3174,1403,3293,4344,0]Ao[3173,13,3174,1419,2344,4344,0]Ao[3173,13,3174,1419,3304,4344,0]Ao[3173,13,3174,1419,3119,4344,0]Ao[3173,13,3174,1419,4588,4344,0]Ao[3173,13,3174,1152,3122,4344,0]Ao[3173,13,3174,1152,4374,4344,0]Ao[3173,13,3174,1157,3343,4344,0]Ao[3173,13,3174,1157,4063,4344,0]Ao[3173,13,3174,2044,4485,4344,0]Ao[3173,13,3174,2044,4198,4344,0]Ao[3173,13,3174,3741,4584,4344,0]Ao[3173,13,3174,3741,3113,4344,0]Ao[3173,13,3174,3741,4451,4344,0]Ao[3173,13,3174,3741,5157,4344,0]Ao[3173,13,3174,3276,4607,4344,0]Ao[3173,13,3174,3276,4925,4344,0]Ao[3173,13,3174,3276,4940,4344,0]Ao[3173,13,3174,3276,4065,4344,0]Ao[3173,13,3174,1733,4461,4344,0]Ao[3173,13,3174,1733,4244,4344,0]Ao[3173,13,3174,1733,5127,4344,0]Ao[3173,13,3174,1733,4190,4344,0]Ao[3173,13,3174,1733,4051,4344,0]Ao[3173,13,3174,1393,3100,4344,0]Ao[3173,13,3174,1393,3310,4344,0]Ao[3173,13,3174,1393,3332,4344,0]Ao[3173,13,3174,1393,4205,4344,0]Ao[3173,13,3174,1393,4582,4344,0]Ao[3173,13,3174,1393,4789,4344,0]Ao[3173,13,3174,1393,2302,4344,0]Ao[3173,13,3174,2354,4793,4344,0]Ao[3173,13,3174,2354,4201,4344,0]Ao[3173,13,3174,1798,4596,4344,0]Ao[3173,13,3174,1798,5160,4344,0]Ao[3173,13,3174,1798,2316,4344,0]Ao[3173,13,3174,1798,4894,4344,0]Ao[3173,13,3174,1829,4204,4344,0]Ao[3173,13,3174,1829,4209,4344,0]Al[3173,13,3174,0,4489,4344,0]Al[3173,13,3174,0,4960,4344,0]Ao[3173,13,3174,4387,3307,4344,0]Ao[3173,13,3174,4387,5262,4344,0]An[3173,13,3174,3174,527,4344,0]An[3173,13,3174,3174,134,4344,0]Ao[3173,13,3174,3174,2331,4344,0]Ao[3173,13,3174,3174,3098,4344,0]Ao[3173,13,1731,1731,4380,4344,0]Ao[3173,13,5111,5111,5007,4344,0]Ao[3173,13,5111,5111,1203,4344,0]Al[3173,13,1819,0,1202,4344,0]Ao[3173,13,1819,1819,4818,4344,0]Ao[3173,13,1819,1819,5064,4344,0]Ao[3173,13,1819,1819,4820,4344,0]Am[3173,13,3736,535,502,4344,0]An[3173,13,3736,535,3287,4344,0]Ao[3173,13,3736,1389,1189,4344,0]Ao[3173,13,3736,1389,4056,4344,0]Ao[3173,13,3736,2408,1887,4344,0]Ao[3173,13,3736,2408,4367,4344,0]Ao[3173,13,4853,4853,1207,4344,0]Ao[2029,13,2030,2030,2328,4344,0]Ao[2029,13,2030,2030,2342,4344,0]Ao[2029,13,2030,2030,4210,4344,0]Ao[1127,13,1128,1128,4999,4344,0]Ao[1127,13,1418,1418,4592,4344,0]Ao[1127,13,4305,4305,5209,4344,0]Ao[1127,13,4305,4305,5210,4344,0]Af[477,7,478,2036,0,0,0]Ag[477,13,478,0,4460,0,0]Al[3173,13,3174,1733,4461,0,0]Ah[477,12,1735,0,3109,0,0]Al[3173,12,1736,1736,3110,0,0]Al[2029,14,2030,2030,2328,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1419,3304,0,0]10Ae[1127,7,1128,0,0,0,0]Aj[477,13,478,2353,2308,0,0]Ae[4390,7,4391,0,0,0,0]Ae[1228,7,1229,0,0,0,0]Al[3173,13,3174,3741,3298,0,0]Aj[477,13,478,3740,3297,0,0]1Al[3173,13,3174,2044,4485,0,0]Al[1228,13,1417,1417,1221,0,0]Al[1228,14,1417,1417,4083,0,0]Aj[477,12,478,2036,1898,0,0]Am[477,13,478,1431,4249,1898,0]Am[477,13,478,1169,4078,1898,0]Ah[477,12,1735,0,1899,0,0]Ak[477,13,1818,0,3337,1899,0]Al[3173,12,1736,1736,1900,0,0]Ao[3173,13,1819,1819,3338,1900,0]Af[477,5,478,1126,0,0,0]Ag[477,15,478,0,3099,0,0]Af[477,5,478,1393,0,0,0]Al[3173,15,3174,1393,3100,0,0]Ah[3173,5,3174,1393,0,0,0]Ag[477,15,478,0,4583,0,0]Al[3173,15,3174,3741,4584,0,0]Ah[3173,5,3174,2354,0,0,0]Al[3173,15,3174,3741,1206,0,0]Af[477,5,478,2354,0,0,0]2Ag[477,15,478,0,4200,0,0]Al[3173,15,3174,2354,4201,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]32Ag[477,15,478,0,3112,0,0]Al[3173,15,3174,3741,3113,0,0]Ag[477,15,478,0,3099,0,0]?0?0?43Ag[477,5,1818,1819,0,0,0]Ae[3173,5,1819,0,0,0,0]10Af[477,5,478,4995,0,0,0]Ah[3173,5,3174,4995,0,0,0]Al[3173,15,4853,4853,1204,0,0]7Al[3173,15,3174,1403,4897,0,0]7Al[3173,15,3174,1798,4596,0,0]Ag[477,12,478,0,2336,0,0]Aj[477,13,478,0,4338,2336,0]Aj[477,13,478,0,4222,2336,0]Al[3173,12,3174,1430,2337,0,0]Ao[3173,13,3174,4616,4339,2337,0]Ao[3173,13,3174,1430,4223,2337,0]Ae[4088,7,4089,0,0,0,0]Aj[477,13,478,3740,3297,0,0]Al[3173,13,3174,3741,3298,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1419,3304,0,0]32An[1228,13,1229,1229,141,4462,0]An[2408,13,2409,2409,132,4462,0]Ao[2408,13,2409,2409,2299,4462,0]Ao[1127,13,1128,1128,3086,4462,0]An[1127,13,1725,1725,142,4462,0]An[1127,13,1824,1824,143,4462,0]Ak[2029,13,2030,431,4490,0,0]Ag[477,13,478,0,4606,0,0]Al[3173,13,3174,3276,4607,0,0]:9Ae[3173,2,3174,0,0,0,0]Aj[477,13,478,2353,2308,0,0]Al[3173,13,3174,2354,2309,0,0]?>43Aj[477,13,478,4157,3322,0,0]Ae[4390,7,4391,0,0,0,0]7Al[1127,14,1128,1128,3086,0,0]80Ae[1228,7,1229,0,0,0,0]Ag[477,13,478,0,4242,0,0]Ag[477,13,478,0,4188,0,0]Al[3173,13,3174,1733,4244,0,0]Al[3173,13,3174,1733,4190,0,0]Ag[477,14,478,0,4242,0,0]Al[3173,14,3174,1733,4244,0,0]53Aj[477,13,478,4162,3328,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Ag[477,15,478,0,4583,0,0]Al[3173,15,3174,1403,4897,0,0]Al[3173,15,3174,3741,4584,0,0]Ah[477,10,3735,3736,0,0,0]Af[3173,10,3736,0,0,0,0]Ag[477,5,3735,3736,0,0,0]Ae[3173,5,3736,0,0,0,0]Ah[477,10,1735,1736,0,0,0]Af[3173,10,1736,0,0,0,0]Ag[477,15,478,0,4200,0,0]Al[3173,15,3174,2354,4201,0,0]Ag[477,10,478,3741,0,0,0]Ai[3173,10,3174,3741,0,0,0]Ah[3173,6,3174,1798,0,0,0]Ah[3173,6,3174,1403,0,0,0]Af[477,6,478,3740,0,0,0]Ah[3173,6,3174,3147,0,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]10Aj[477,15,478,3740,4898,0,0]Al[3173,15,3174,3147,4899,0,0]Ag[477,14,478,0,3118,0,0]Al[3173,14,3174,1419,3119,0,0]Ah[477,14,3735,0,4885,0,0]Al[3173,14,3736,3736,4886,0,0]Aj[477,13,478,3740,3297,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1419,3304,0,0]10Al[3173,13,3174,3147,3089,0,0]Aj[477,13,478,2353,2308,0,0]Al[3173,13,3174,2354,2309,0,0]Ab[4917,3,0,0,0,0,0]Ao[1127,13,1128,1128,4999,3127,0]3Aj[477,13,478,4162,3328,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Af[477,5,478,3276,0,0,0]Ah[3173,5,3174,3276,0,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]54Ai[3173,10,3174,1798,0,0,0]Al[3173,15,3174,3741,1206,0,0]7600Al[3173,15,3174,1152,4374,0,0]4365Al[3173,15,4853,4853,1204,0,0]Ae[1127,9,1128,0,0,0,0]:9Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]<;<;Af[477,6,478,1403,0,0,0]Ah[3173,6,3174,1403,0,0,0]32Al[1228,15,1229,1229,4075,0,0]Ac[477,5,478,0,0,0,0]Ae[3173,5,3174,0,0,0,0]Ag[477,15,478,0,4200,0,0]Ag[477,5,1735,1736,0,0,0]Al[3173,15,3174,2354,4201,0,0]Al[3173,15,3174,1798,4894,0,0]Ae[3173,5,1736,0,0,0,0]An[1228,13,1229,1229,141,4452,0]An[2408,13,2409,2409,132,4452,0]Ao[1127,13,1128,1128,3086,4452,0]An[1127,13,1725,1725,142,4452,0]An[1127,13,1824,1824,143,4452,0]Ag[477,13,478,0,4606,0,0]Al[3173,13,3174,3276,4607,0,0]Ag[477,13,478,0,1480,0,0]Al[3173,13,3174,1856,1481,0,0]Aj[477,14,478,2353,4347,0,0]Aj[477,13,478,3740,5072,0,0]Ag[477,13,478,0,4460,0,0]Al[3173,13,3174,1733,4461,0,0]Ag[477,13,478,0,4338,0,0]Al[3173,13,3174,4616,4339,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1419,3304,0,0]7Al[3173,13,3174,3741,3298,0,0]Aj[477,13,478,3740,3297,0,0]Ah[477,13,1818,0,4817,0,0]Ah[477,13,1818,0,4819,0,0]Al[3173,13,1819,1819,4818,0,0]Al[3173,13,1819,1819,4820,0,0]45;:Ao[4088,13,4089,4089,5161,4953,0]Ag[477,13,478,0,4597,0,0]Al[3173,13,3174,1148,4598,0,0]Ae[1127,6,1128,0,0,0,0]Af[477,5,478,4270,0,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]10Ag[477,15,478,0,3112,0,0]Al[3173,15,3174,3741,3113,0,0]Ae[3173,5,5111,0,0,0,0]Af[477,5,478,4157,0,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]Al[3173,15,4853,4853,1204,0,0]Aj[477,14,478,2353,4347,0,0]Ai[477,13,478,0,526,3078,0]Ai[477,13,478,0,133,3078,0]An[3173,13,3174,3174,527,3078,0]An[3173,13,3174,3174,134,3078,0]Ao[2408,13,2409,2409,2299,3333,0]Ag[477,13,478,0,4939,0,0]Al[3173,13,3174,3276,4940,0,0]Ah[477,12,1735,0,1899,0,0]Ak[477,13,1818,0,3337,1899,0]Al[3173,12,1736,1736,1900,0,0]Ao[3173,13,1819,1819,3338,1900,0]Aj[477,13,478,3740,3297,0,0]Ag[477,12,478,0,4891,0,0]Al[3173,12,3174,3741,4892,0,0]Ag[477,13,478,0,4606,0,0]Al[3173,13,3174,3276,4607,0,0]Ag[477,14,478,0,3118,0,0]Al[3173,14,3174,1419,3119,0,0]Ah[477,12,3735,0,3299,0,0]Aj[477,13,3735,0,501,3299,0]Ak[477,13,3735,0,1188,3299,0]Ak[477,13,3735,0,1886,3299,0]Al[3173,12,3736,3736,3300,0,0]Am[3173,13,3736,535,502,3300,0]Ao[3173,13,3736,1389,1189,3300,0]Ao[3173,13,3736,2408,1887,3300,0]Aj[477,14,478,2353,4347,0,0]Al[3173,13,3174,2044,1907,0,0]Ah[3173,2,4385,4161,0,0,0]Ae[1228,7,1229,0,0,0,0]==Ai[477,13,478,1126,520,0,0]Ak[2029,13,2030,431,4490,0,0]032Ah[477,12,1735,0,1899,0,0]Ak[477,13,1818,0,3337,1899,0]Ae[3173,2,3174,0,0,0,0]Al[3173,12,1736,1736,1900,0,0]Ao[3173,13,1819,1819,3338,1900,0]Ag[477,10,478,4387,0,0,0]Ai[3173,10,3174,4387,0,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]Ag[477,15,478,0,4200,0,0]Al[3173,15,3174,2354,4201,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Af[477,5,478,1733,0,0,0]Ah[3173,5,3174,1733,0,0,0]Ah[477,12,1735,0,3109,0,0]Al[3173,12,1736,1736,3110,0,0]Aj[477,13,478,2353,2308,0,0]Al[3173,13,3174,3741,3298,0,0]Al[3173,13,3174,2354,2309,0,0]Ah[477,13,3735,0,4366,0,0]Al[3173,13,3736,2408,4367,0,0]Aj[477,13,478,2036,3125,0,0]0Aj[477,13,478,3740,3297,0,0]5Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1419,3304,0,0]Ag[477,13,478,0,4606,0,0]Al[3173,13,3174,3276,4607,0,0]Al[1127,14,1128,1128,3086,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Al[3173,15,4853,4853,1204,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]10Ag[477,15,478,0,4954,0,0]Ag[477,15,478,0,3112,0,0]Ag[477,15,478,0,4450,0,0]Al[3173,15,3174,1403,4955,0,0]Al[3173,15,3174,3741,3113,0,0]Al[3173,15,3174,3741,4451,0,0]Ah[3173,5,3174,3741,0,0,0]Ag[477,15,478,0,3099,0,0]Al[3173,15,3174,1393,3100,0,0]Ah[3173,5,3174,1798,0,0,0]Ae[4088,5,4089,0,0,0,0]Al[1127,13,1128,1128,3086,0,0]Aj[477,13,478,3740,3297,0,0]Ah[477,12,1735,0,1899,0,0]Ak[477,13,1818,0,3337,1899,0]Al[3173,12,1736,1736,1900,0,0]Ao[3173,13,1819,1819,3338,1900,0]Ah[3173,7,5042,5041,0,0,0]0Al[3173,13,3174,3741,3298,0,0]6Ai[477,13,478,1126,520,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1419,3304,0,0]Ao[4088,13,4089,4089,5161,4953,0]0Ag[477,13,478,0,4188,0,0]Al[3173,13,3174,1733,4190,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]10Ag[477,13,478,0,4924,0,0]Al[3173,13,3174,3276,4925,0,0]Am[1228,13,1229,1229,141,499,0]Am[2408,13,2409,2409,132,499,0]An[1127,13,1128,1128,3086,499,0]Am[1127,13,1725,1725,142,499,0]Am[1127,13,1824,1824,143,499,0]??Ah[477,13,3735,0,3299,0,0]Aj[477,13,3735,0,501,3299,0]Al[3173,13,3736,3736,3300,0,0]Am[3173,13,3736,535,502,3300,0]Ai[477,13,478,1126,520,0,0]An[1228,13,1229,1229,141,4462,0]An[2408,13,2409,2409,132,4462,0]Ao[2408,13,2409,2409,2299,4462,0]Ao[1127,13,1128,1128,3086,4462,0]An[1127,13,1725,1725,142,4462,0]An[1127,13,1824,1824,143,4462,0]Ag[477,13,478,0,4242,0,0]Al[3173,13,3174,1733,4244,0,0]Ag[477,19,478,0,4959,0,0]Ai[3173,19,3174,0,4960,0,0]Ae[1127,5,4305,0,0,0,0]0Ag[477,13,478,0,4460,0,0]Al[3173,13,3174,1733,4461,0,0]Ag[477,13,478,0,4188,0,0]Al[3173,13,3174,1733,4190,0,0]Aj[477,13,478,2353,2308,0,0]Aj[477,13,478,2385,2312,0,0]Al[3173,13,3174,2354,2313,0,0]Al[3173,13,3174,3741,3298,0,0]Ao[4088,13,4089,4089,5161,4953,0];:Ag[477,5,1735,1736,0,0,0]Ae[3173,5,1736,0,0,0,0]Ah[477,15,1818,0,1201,0,0]Ai[3173,15,1819,0,1202,0,0]?>?>Aj[477,13,478,3740,3297,0,0]6Ag[477,13,478,0,4606,0,0]Al[3173,13,3174,3276,4607,0,0]Ae[4088,7,4089,0,0,0,0]Ae[4390,7,4391,0,0,0,0]Ag[477,14,478,0,3303,0,0]Al[3173,14,3174,1419,3304,0,0]?>>>>A`[\"{IFfBCAb}\",[]]?A`[\"{KIbBFL`}\",[]]A`[\"{cc{}}\",[\"T\"]]0000A`[\"{KIbABCf}\",[]]111Aa[\"{AJ@fAHCd}\",[]]22A`[\"{BBChIFf}\",[]]33333333A`[\"{BHMnIFd}\",[]]A`[\"{BEFlIFd}\",[]]5A`[\"{BEHbIFd}\",[]]A`[\"{BBNfIFd}\",[]]A`[\"{BBOhIFd}\",[]]A`[\"{BCMjIFd}\",[]]A`[\"{BCOdIFd}\",[]]A`[\"{BHLlIFd}\",[]]A`[\"{BHGfIFd}\",[]]A`[\"{BEIlIFd}\",[]]A`[\"{AIN`IFd}\",[]]>>>>>>>>>>>>An[\"{{{AHO`{c}}}AJAjCNh}\",[\"S\"]]??????????????Bm[\"{{{AHO`{KI`}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bk[\"{{{AHO`{KFn}}{AHO`{CGbc}}}ICjKF`}\",[\"H\"]]Bn[\"{{{AHO`{AOL`}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{B@Mf}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bm[\"{{{AHO`{DAl}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bm[\"{{{AHO`{A@j}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{ABCd}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{AHCb}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bm[\"{{{AHO`{KIb}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{BFDf}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bk[\"{{{AHO`{KG`}}{AHO`{CGbc}}}ICjKF`}\",[\"H\"]]Bn[\"{{{AHO`{AHFd}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{BBBl}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{AJAn}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{AOLb}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{BEGb}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{BFCl}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{B@Mh}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bm[\"{{{AHO`{DAn}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bm[\"{{{AHO`{A@l}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{ABCf}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{AHCd}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Aa[\"{{}c{}}\",[\"U\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n[\"{{}ICj}\",[]]0000Cc[\"{{{AHO`{CGbKDl}}KCb}{{KGd{{KFl{IDb}}AHFd}}}}\",[]]Cf[\"{{{AHO`{CGbBB@j}}}{{KFl{c}}}{}}\",[\"Iterator::Item\"]]Dl[\"{{{AHO`{BFBn}}ICh{AHO`{CGb{K@j{Oh}}}}}{{KGd{{AHO`{{K@j{Oh}}}}AOJj}}}}\",[]]Bi[\"{{{AHO`{CGbNNf}}A@l}{{KFl{ABCf}}}}\",[]]Dl[\"{{{AHO`{BFC`}}ICh{AHO`{CGb{K@j{Oh}}}}}{{KGd{{AHO`{{K@j{Oh}}}}AOJl}}}}\",[]]Bf[\"{{{AHO`{AILb}}KFn}{{KFl{DA`}}}}\",[]]Bf[\"{{{AHO`{DA`}}}{{KGd{ICjAIKd}}}}\",[]]A`[\"{AILnCNb}\",[]]Am[\"{{{AHO`{CGbAILd}}}ICj}\",[]]A`[\"{AIM`CNb}\",[]]Ck[\"{{{ABb{{AHO`{CGbAIJd}}}}{AHO`{CGbNIn}}}{{DAh{ICj}}}}\",[]]Dk[\"{{{ABb{{AHO`{CGbBD@f}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbABBf}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbABBh}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbAIKn}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{ABb{{AHO`{CGbNLb}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{ABb{{AHO`{CGbKFf}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbAH@h}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbAAO`}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbBAA`}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbBAAn}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{ABb{{AHO`{CGbKCj}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbBBNb}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{ABb{{AHO`{CGbNMn}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Bf[\"{{{AHO`{CGbAILd}}}{{KFl{IFl}}}}\",[]]De[\"{{{AHO`{CGbAIOd}}{AHO`{CGb{K@j{Oh}}}}}{{KGd{{KFl{KCb}}AHFb}}}}\",[]]Bm[\"{{{AHO`{CGbAIOf}}DC`}{{KGd{KDlBBBl}}}}\",[]]C`[\"{{DAn{AHO`{CGbc}}}{{KGd{BGAnIFf}}}A@f}\",[\"R\"]]Bf[\"{{{AHO`{CGbAHBb}}}{{KFl{ICn}}}}\",[]]Ec[\"{{{AHO`{BCAd}}AJAj{AHO`{CGb{K@j{AIMh}}}}{AHO`{CGb{K@j{ABC`}}}}}{{KGf{KCb}}}}\",[]]Cn[\"{{{AHO`{BFBn}}ICh{AHO`{CGb{ABh{Oh}}}}}{{KGd{ICjAOJj}}}}\",[]]Cn[\"{{{AHO`{BFC`}}ICh{AHO`{CGb{ABh{Oh}}}}}{{KGd{ICjAOJl}}}}\",[]]C`[\"{{{AHO`{CGbAHBb}}ICnDC`}{{KGd{ICjBFDf}}}}\",[]]Cc[\"{{{AHO`{BCAd}}AJAj{AHO`{ABDl}}}{{KGf{ICj}}}}\",[]]Bn[\"{{{AHO`{CGbBH@l}}CN`}{{AHO`{CGbBH@l}}}}\",[]]Bn[\"{{{AHO`{CGbBH@n}}CN`}{{AHO`{CGbBH@n}}}}\",[]]Aj[\"{{{AHO`{AILb}}}DAl}\",[]]Aj[\"{{{AHO`{AILd}}}DAn}\",[]]Cn[\"{{{AHO`{NJj}}{AHO`{{K@j{Oh}}}}{AHO`{CGb{K@j{Oh}}}}}ICj}\",[]]Cn[\"{{{AHO`{NJl}}{AHO`{{K@j{Oh}}}}{AHO`{CGb{K@j{Oh}}}}}ICj}\",[]]A`[\"{BC@lCNb}\",[]]A`[\"{ABDhKCb}\",[]]A`[\"{ABDjKCb}\",[]]o[\"{IHdDC`}\",[]]Bm[\"{{{AHO`{CGbAIOd}}KI`}{{KGd{ICjB@Ld}}}}\",[]]Bm[\"{{{AHO`{CGbBGB`}}KI`}{{KGd{ICjB@Ld}}}}\",[]]Bm[\"{{{AHO`{CGbAIOf}}KIb}{{KGd{ICjB@Ll}}}}\",[]]Bn[\"{{{AHO`{AJAf}}{AHO`{CMl}}}{{KFl{ICn}}}}\",[]]Bn[\"{{{AHO`{BBOd}}{AHO`{CMl}}}{{KFl{ICn}}}}\",[]]Bn[\"{{{AHO`{BEJj}}{AHO`{CMl}}}{{KFl{ICn}}}}\",[]]Bn[\"{{{AHO`{AJAh}}{AHO`{CMl}}}{{KFl{ICn}}}}\",[]]Bn[\"{{{AHO`{BBOf}}{AHO`{CMl}}}{{KFl{ICn}}}}\",[]]Bn[\"{{{AHO`{BEJl}}{AHO`{CMl}}}{{KFl{ICn}}}}\",[]]Bd[\"{{{AHO`{BEGf}}}{{KFl{BB@h}}}}\",[]]Bd[\"{{{AHO`{BEGh}}}{{KFl{BB@j}}}}\",[]]Bi[\"{{{A@b{BFBf}}NK`CMn}{{A@d{AILf}}}}\",[]]Bi[\"{{{A@b{AIJl}}NK`CMn}{{A@d{AILf}}}}\",[]]Bi[\"{{{A@b{AOJn}}NK`CMn}{{A@d{AILf}}}}\",[]]Bi[\"{{{A@b{BBAl}}NK`CMn}{{A@d{AILf}}}}\",[]]Bi[\"{{{A@b{BFBh}}NK`CMn}{{A@d{AILh}}}}\",[]]Bi[\"{{{A@b{AIJn}}NK`CMn}{{A@d{AILh}}}}\",[]]Bi[\"{{{A@b{AOK`}}NK`CMn}{{A@d{AILh}}}}\",[]]Bi[\"{{{A@b{BBAn}}NK`CMn}{{A@d{AILh}}}}\",[]]Aj[\"{BG@n{{A@b{BFBh}}}}\",[]]o[\"{ID`ICn}\",[]]A`[\"{ABDnCNb}\",[]]o[\"{IDbICn}\",[]]A`[\"{ABE`CNb}\",[]]Aa[\"{AOIjBCM`}\",[]]Ai[\"{{{AHO`{AAj}}}AAj}\",[]]Ak[\"{{{AHO`{AOMf}}}AOMf}\",[]]Ak[\"{{{AHO`{AOIn}}}AOIn}\",[]]Ak[\"{{{AHO`{AILb}}}AILb}\",[]]Ai[\"{{{AHO`{KEd}}}KEd}\",[]]Ak[\"{{{AHO`{BGJ`}}}BGJ`}\",[]]Ak[\"{{{AHO`{BFDd}}}BFDd}\",[]]Ak[\"{{{AHO`{AB@h}}}AB@h}\",[]]Ak[\"{{{AHO`{BAOf}}}BAOf}\",[]]Ai[\"{{{AHO`{KFd}}}KFd}\",[]]Ai[\"{{{AHO`{DA`}}}DA`}\",[]]Ak[\"{{{AHO`{BCAf}}}BCAf}\",[]]Ak[\"{{{AHO`{BC@h}}}BC@h}\",[]]Ak[\"{{{AHO`{AHFb}}}AHFb}\",[]]Ak[\"{{{AHO`{AIOh}}}AIOh}\",[]]Ak[\"{{{AHO`{BC@f}}}BC@f}\",[]]Ak[\"{{{AHO`{AJAl}}}AJAl}\",[]]Ak[\"{{{AHO`{BAC`}}}BAC`}\",[]]Ai[\"{{{AHO`{KI`}}}KI`}\",[]]Ak[\"{{{AHO`{BGIj}}}BGIj}\",[]]Ai[\"{{{AHO`{KFn}}}KFn}\",[]]Ak[\"{{{AHO`{AINj}}}AINj}\",[]]Ak[\"{{{AHO`{AHEh}}}AHEh}\",[]]Ak[\"{{{AHO`{BFKd}}}BFKd}\",[]]Ak[\"{{{AHO`{AIKd}}}AIKd}\",[]]Ak[\"{{{AHO`{ABDn}}}ABDn}\",[]]Ak[\"{{{AHO`{AILn}}}AILn}\",[]]Ak[\"{{{AHO`{AHEl}}}AHEl}\",[]]Ak[\"{{{AHO`{BCMf}}}BCMf}\",[]]Ak[\"{{{AHO`{B@Ld}}}B@Ld}\",[]]Ak[\"{{{AHO`{BCLn}}}BCLn}\",[]]Ak[\"{{{AHO`{AHE`}}}AHE`}\",[]]Ak[\"{{{AHO`{BBLd}}}BBLd}\",[]]Ak[\"{{{AHO`{BCOl}}}BCOl}\",[]]Ak[\"{{{AHO`{BFGh}}}BFGh}\",[]]Ak[\"{{{AHO`{BFIf}}}BFIf}\",[]]Ak[\"{{{AHO`{AOL`}}}AOL`}\",[]]Ak[\"{{{AHO`{BBMh}}}BBMh}\",[]]Ak[\"{{{AHO`{BABd}}}BABd}\",[]]Ak[\"{{{AHO`{BH@l}}}BH@l}\",[]]Ak[\"{{{AHO`{B@Kh}}}B@Kh}\",[]]Ak[\"{{{AHO`{AOJd}}}AOJd}\",[]]Ak[\"{{{AHO`{AHCf}}}AHCf}\",[]]Ak[\"{{{AHO`{BCLh}}}BCLh}\",[]]Ak[\"{{{AHO`{BEFf}}}BEFf}\",[]]Ak[\"{{{AHO`{B@M`}}}B@M`}\",[]]Ak[\"{{{AHO`{B@Mf}}}B@Mf}\",[]]Ak[\"{{{AHO`{B@N`}}}B@N`}\",[]]Ak[\"{{{AHO`{BFKn}}}BFKn}\",[]]Ak[\"{{{AHO`{AIMd}}}AIMd}\",[]]Ai[\"{{{AHO`{DAl}}}DAl}\",[]]Ai[\"{{{AHO`{A@j}}}A@j}\",[]]Ak[\"{{{AHO`{ABCd}}}ABCd}\",[]]Ak[\"{{{AHO`{AHCb}}}AHCb}\",[]]Ai[\"{{{AHO`{CNj}}}CNj}\",[]]Ak[\"{{{AHO`{AIJl}}}AIJl}\",[]]Ai[\"{{{AHO`{IDh}}}IDh}\",[]]Ak[\"{{{AHO`{AOJn}}}AOJn}\",[]]Ai[\"{{{AHO`{NKl}}}NKl}\",[]]Ak[\"{{{AHO`{BBAl}}}BBAl}\",[]]Ai[\"{{{AHO`{IFb}}}IFb}\",[]]Ak[\"{{{AHO`{BEJb}}}BEJb}\",[]]Ak[\"{{{AHO`{BGHn}}}BGHn}\",[]]Ak[\"{{{AHO`{BEJf}}}BEJf}\",[]]Ai[\"{{{AHO`{KIb}}}KIb}\",[]]Ak[\"{{{AHO`{BGIl}}}BGIl}\",[]]Ak[\"{{{AHO`{BFDf}}}BFDf}\",[]]Ai[\"{{{AHO`{KG`}}}KG`}\",[]]Ak[\"{{{AHO`{BAAl}}}BAAl}\",[]]Ak[\"{{{AHO`{AINl}}}AINl}\",[]]Ak[\"{{{AHO`{AHEj}}}AHEj}\",[]]Ak[\"{{{AHO`{BFDb}}}BFDb}\",[]]Ak[\"{{{AHO`{BFKf}}}BFKf}\",[]]Ak[\"{{{AHO`{AIKj}}}AIKj}\",[]]Ak[\"{{{AHO`{ABE`}}}ABE`}\",[]]Ak[\"{{{AHO`{AIM`}}}AIM`}\",[]]Ak[\"{{{AHO`{AHEn}}}AHEn}\",[]]Ak[\"{{{AHO`{BCMh}}}BCMh}\",[]]Ak[\"{{{AHO`{AHFd}}}AHFd}\",[]]Ak[\"{{{AHO`{BBBl}}}BBBl}\",[]]Ak[\"{{{AHO`{AJAn}}}AJAn}\",[]]Ak[\"{{{AHO`{AOKn}}}AOKn}\",[]]Ak[\"{{{AHO`{BC@j}}}BC@j}\",[]]Ak[\"{{{AHO`{B@Ll}}}B@Ll}\",[]]Ak[\"{{{AHO`{BCM`}}}BCM`}\",[]]Ak[\"{{{AHO`{AHEb}}}AHEb}\",[]]Ak[\"{{{AHO`{BBLf}}}BBLf}\",[]]Ak[\"{{{AHO`{BHDj}}}BHDj}\",[]]Ak[\"{{{AHO`{BCOn}}}BCOn}\",[]]Ak[\"{{{AHO`{BFGj}}}BFGj}\",[]]Ak[\"{{{AHO`{BFIh}}}BFIh}\",[]]Ak[\"{{{AHO`{AOLb}}}AOLb}\",[]]Ak[\"{{{AHO`{BBMj}}}BBMj}\",[]]Ak[\"{{{AHO`{BABh}}}BABh}\",[]]Ak[\"{{{AHO`{BH@n}}}BH@n}\",[]]Ak[\"{{{AHO`{B@Kl}}}B@Kl}\",[]]Ak[\"{{{AHO`{AOJf}}}AOJf}\",[]]Ak[\"{{{AHO`{AHCh}}}AHCh}\",[]]Ak[\"{{{AHO`{AIMl}}}AIMl}\",[]]Ak[\"{{{AHO`{AJ@h}}}AJ@h}\",[]]Ak[\"{{{AHO`{B@Mj}}}B@Mj}\",[]]Ak[\"{{{AHO`{BCLl}}}BCLl}\",[]]Ak[\"{{{AHO`{BEFj}}}BEFj}\",[]]Ak[\"{{{AHO`{AAOl}}}AAOl}\",[]]Ak[\"{{{AHO`{BEGb}}}BEGb}\",[]]Ak[\"{{{AHO`{B@Mb}}}B@Mb}\",[]]Ak[\"{{{AHO`{BCNh}}}BCNh}\",[]]Ak[\"{{{AHO`{BHE`}}}BHE`}\",[]]Ak[\"{{{AHO`{ABAh}}}ABAh}\",[]]Ak[\"{{{AHO`{BFCl}}}BFCl}\",[]]Ak[\"{{{AHO`{B@Mh}}}B@Mh}\",[]]Ak[\"{{{AHO`{B@Nb}}}B@Nb}\",[]]Ak[\"{{{AHO`{BCAb}}}BCAb}\",[]]Ak[\"{{{AHO`{BFL`}}}BFL`}\",[]]Ak[\"{{{AHO`{AIMf}}}AIMf}\",[]]Ak[\"{{{AHO`{BIAl}}}BIAl}\",[]]Ai[\"{{{AHO`{DAn}}}DAn}\",[]]Ai[\"{{{AHO`{A@l}}}A@l}\",[]]Ak[\"{{{AHO`{ABCf}}}ABCf}\",[]]Ak[\"{{{AHO`{AHCd}}}AHCd}\",[]]Ak[\"{{{AHO`{BBCh}}}BBCh}\",[]]Ak[\"{{{AHO`{BGAn}}}BGAn}\",[]]Ai[\"{{{AHO`{IFf}}}IFf}\",[]]Ai[\"{{{AHO`{IFd}}}IFd}\",[]]Ak[\"{{{AHO`{BEJd}}}BEJd}\",[]]Ak[\"{{{AHO`{BGI`}}}BGI`}\",[]]Ak[\"{{{AHO`{BEJh}}}BEJh}\",[]]Ai[\"{{{AHO`{CNl}}}CNl}\",[]]Ak[\"{{{AHO`{AIJn}}}AIJn}\",[]]Ai[\"{{{AHO`{IDj}}}IDj}\",[]]Ak[\"{{{AHO`{AOK`}}}AOK`}\",[]]Ai[\"{{{AHO`{NKn}}}NKn}\",[]]Ak[\"{{{AHO`{BBAn}}}BBAn}\",[]]Ai[\"{{{AHO`{IFn}}}IFn}\",[]]Ak[\"{{{AHO`{ABC`}}}ABC`}\",[]]Ak[\"{{{AHO`{ABDl}}}ABDl}\",[]]Ak[\"{{{AHO`{B@Nd}}}B@Nd}\",[]]Ak[\"{{{AHO`{BG@n}}}BG@n}\",[]]Ak[\"{{{AHO`{BCN`}}}BCN`}\",[]]Ak[\"{{{AHO`{BHKb}}}BHKb}\",[]]Ak[\"{{{AHO`{BHKd}}}BHKd}\",[]]Bn[\"{{{AHO`{AILb}}KI`{AHO`{{K@j{Oh}}}}}ICj}\",[]]Bn[\"{{{AHO`{AB@h}}KI`{AHO`{{K@j{Oh}}}}}ICj}\",[]]Bf[\"{{{AHO`{DA`}}}{{KGd{ICjBBLd}}}}\",[]]Bf[\"{{{AHO`{CGbAILd}}NK`KIbICn}ICj}\",[]]Bj[\"{{{AHO`{B@Mf}}}{{AHO`{{K@j{Oh}}}}}}\",[]]Bj[\"{{{AHO`{B@Mh}}}{{AHO`{{K@j{Oh}}}}}}\",[]]Aa[\"{BCAbAIMl}\",[]]A`[\"{ABCdCNb}\",[]]A`[\"{ABCfCNb}\",[]]A`[\"{B@N`DC`}\",[]]A`[\"{B@NbDC`}\",[]]l[\"{NKb}\",[]]l[\"{NKd}\",[]]Cf[\"{{{AHO`{BFHd}}{AHO`{CGbA@f}}}{{KGd{B@MhBFCl}}}}\",[]]Cf[\"{{{AHO`{BIE`}}{AHO`{CGbA@f}}}{{KGd{B@MhBFCl}}}}\",[]]Bc[\"{{{AHO`{AILd}}}{{ABh{KG`}}}}\",[]]Bd[\"{{{AHO`{IHj}}{AHO`{CMl}}}ICj}\",[]]Am[\"{{{AHO`{AOJ`}}KCb}ICj}\",[]]Ai[\"{{{AHO`{IHl}}}ICj}\",[]]Bg[\"{{{ABb{{AHO`{CGbAIJd}}}}NK`}ICj}\",[]]Bm[\"{{{AHO`{CGbAJ@`}}KI`}{{KGd{ICjB@Ld}}}}\",[]]Bm[\"{{{AHO`{CGbAJ@b}}KIb}{{KGd{ICjB@Ll}}}}\",[]]Am[\"{ABA`{{KGd{ICjAIOj}}}}\",[]]Dc[\"{{{AHO`{CGbAB@j}}ABAb{AHO`{CGb{ABh{Oh}}}}}{{KGd{ABDjAIOl}}}}\",[]]Bj[\"{{{AHO`{NMd}}{ABb{{A@d{KEn}}}}}ICj}\",[]]Bk[\"{{{AHO`{BACb}}{ABb{{A@d{KEn}}}}}ICj}\",[]]Bk[\"{{{AHO`{AOMl}}{ABb{{A@d{KEn}}}}}ICj}\",[]]o[\"{AAjDC`}\",[]]Ak[\"{{{AHO`{AILb}}}BCMf}\",[]]Ak[\"{{{AHO`{AB@h}}}BAOf}\",[]]Aj[\"{{{AHO`{DA`}}}AHEl}\",[]]A`[\"{KEdBCMf}\",[]]An[\"{{{AHO`{CGbAILd}}}BCMh}\",[]]Ce[\"{{{AHO`{CGbAINn}}{KFl{KHl}}}{{AHO`{CGbAINn}}}}\",[]]Ce[\"{{{AHO`{CGbAIO`}}{KFl{KHl}}}{{AHO`{CGbAIO`}}}}\",[]]Ck[\"{{{AHO`{CGbAJ@`}}{AHO`{{K@j{Oh}}}}}{{KGd{KCbAJAl}}}}\",[]]Ck[\"{{{AHO`{CGbAJ@b}}{AHO`{{K@j{Oh}}}}}{{KGd{KCbAJAn}}}}\",[]]Bl[\"{{{AHO`{BGAn}}{AHO`{CGbc}}}ICjKDj}\",[\"W\"]]Aj[\"{{{AHO`{AB@h}}}KCj}\",[]]An[\"{ABA`{{KGd{AIKnBCLn}}}}\",[]]Bi[\"{{{AHO`{CGbNNf}}A@l}{{KFl{ABCf}}}}\",[]]Fj[\"{{{AHO`{CGbAB@j}}ABAbNK`{AHO`{CGb{ABh{Oh}}}}{KFl{{A@b{BABh}}}}}{{KGd{{KC`{BEGbAILd}}{A@d{AOIj}}}}}}\",[]]Am[\"{AHO`{{AHO`{c}}}{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A`[\"{AAjAOIn}\",[]]Bc[\"{{AJ@fBBLbAAj}{{KGh{AOJ`}}}}\",[]]Aj[\"{AJ@f{{KGf{AB@h}}}}\",[]]Ba[\"{{}{{KGd{ICj{A@d{IEd}}}}}}\",[]]Ak[\"{{{AHO`{AILb}}}BCLn}\",[]]Be[\"{{{AHO`{AB@j}}}{{AHO`{BBMj}}}}\",[]]o[\"{OhBFKn}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{BABdA@b}\",[]]o[\"{OhBFL`}\",[]]A`[\"{AIM`CNb}\",[]]A`[\"{BABhA@b}\",[]]A`[\"{BCAbKFl}\",[]]Bm[\"{{{AHO`{CGbc}}}{{KGd{KI`BBCh}}}A@f}\",[\"B\"]]Bm[\"{{{AHO`{CGbc}}}{{KGd{KFnBBCh}}}A@f}\",[\"B\"]]Bn[\"{{{AHO`{CGbc}}}{{KGd{AHCfBBCh}}}A@f}\",[\"B\"]]Bn[\"{{{AHO`{CGbc}}}{{KGd{BFKnBBCh}}}A@f}\",[\"B\"]]Bn[\"{{{AHO`{CGbc}}}{{KGd{ABCdBBCh}}}A@f}\",[\"B\"]]Bi[\"{{{AHO`{CGbc}}}{{KGl{KIb}}}A@f}\",[\"B\"]]Bi[\"{{{AHO`{CGbc}}}{{KGl{KG`}}}A@f}\",[\"B\"]]Bj[\"{{{AHO`{CGbc}}}{{KGl{AHCh}}}A@f}\",[\"B\"]]Bj[\"{{{AHO`{CGbc}}}{{KGl{AIMl}}}A@f}\",[\"B\"]]Ek[\"{{{AHO`{CGb{KEl{AAOb}}}}{AHO`{c}}{AHO`{{K@j{CN`}}}}DC`}{{KGd{BCNhBFCl}}}{BFHdIHh}}\",[\"\"]]Bj[\"{{{AHO`{CGbc}}}{{KGl{BFL`}}}A@f}\",[\"B\"]]Bj[\"{{{AHO`{CGbc}}}{{KGl{ABCf}}}A@f}\",[\"B\"]]Bj[\"{{{AHO`{CGbc}}}{{KGl{AHBd}}}A@f}\",[\"B\"]]A`[\"{ABC`KFl}\",[]]Bk[\"{{{AHO`{KI`}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bk[\"{{{AHO`{KFn}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{AHCf}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{BFKn}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{ABCd}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bk[\"{{{AHO`{KIb}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bk[\"{{{AHO`{KG`}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{AHCh}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{AIMl}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{AAOl}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{BFL`}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{ABCf}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{AHBn}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bj[\"{{{AHO`{CGbAJ@`}}}{{KGd{ICjB@Ld}}}}\",[]]Bj[\"{{{AHO`{CGbAJ@b}}}{{KGd{ICjAOKn}}}}\",[]]An[\"{{{AHO`{AONh}}AB@d}ICj}\",[]]Ea[\"{{{AHO`{CGbAB@j}}NK`AHCd{KFl{B@Nb}}AAOb{AHO`{CGb{ABh{Oh}}}}}{{KFl{BAOb}}}}\",[]]o[\"{D@dNKb}\",[]]o[\"{D@fNKd}\",[]]A`[\"{ABA`ICj}\",[]]Ba[\"{{{AHO`{CGbAB@j}}ABAb}ICj}\",[]]Bh[\"{{{AHO`{AJAf}}{AHO`{CMl}}ICn}ICj}\",[]]Bh[\"{{{AHO`{BBOd}}{AHO`{CMl}}ICn}ICj}\",[]]Bh[\"{{{AHO`{BEJj}}{AHO`{CMl}}ICn}ICj}\",[]]Bh[\"{{{AHO`{AJAh}}{AHO`{CMl}}ICn}ICj}\",[]]Bh[\"{{{AHO`{BBOf}}{AHO`{CMl}}ICn}ICj}\",[]]Bh[\"{{{AHO`{BEJl}}{AHO`{CMl}}ICn}ICj}\",[]]A`[\"{AHAlDC`}\",[]]o[\"{ID`CNb}\",[]]o[\"{IDbCNb}\",[]]Ch[\"{{{AHO`{CGbAILf}}NK`NK`CNbCNbDC`{AHO`{BAAj}}}ICj}\",[]]Cg[\"{{{AHO`{CGbCNj}}NK`NK`CNbCNbDC`{AHO`{BAAj}}}ICj}\",[]]Cg[\"{{{AHO`{CGbIDh}}NK`NK`CNbCNbDC`{AHO`{BAAj}}}ICj}\",[]]Cg[\"{{{AHO`{CGbNKl}}NK`NK`CNbCNbDC`{AHO`{BAAj}}}ICj}\",[]]Ch[\"{{{AHO`{CGbAILh}}NK`NK`CNbCNbDC`{AHO`{BAAl}}}ICj}\",[]]Cg[\"{{{AHO`{CGbCNl}}NK`NK`CNbCNbDC`{AHO`{BAAl}}}ICj}\",[]]Cg[\"{{{AHO`{CGbIDj}}NK`NK`CNbCNbDC`{AHO`{BAAl}}}ICj}\",[]]Cg[\"{{{AHO`{CGbNKn}}NK`NK`CNbCNbDC`{AHO`{BAAl}}}ICj}\",[]]o[\"{D@dNKb}\",[]]o[\"{D@fNKd}\",[]]o[\"{BEKfOh}\",[]]A`[\"{KEdBCLn}\",[]]A`[\"{BCLhICn}\",[]]A`[\"{BEFfICn}\",[]]Aa[\"{AH@`BFD`}\",[]]A`[\"{BCLlICn}\",[]]A`[\"{BEFjICn}\",[]]A`[\"{BCAbKHl}\",[]]Aa[\"{BBLjBCM`}\",[]]Aa[\"{AH@bBFDb}\",[]]Bg[\"{{{AHO`{AB@h}}AHFl}{{KGf{ICj}}}}\",[]]A`[\"{ABA`ICj}\",[]]Cf[\"{{{AHO`{CGbAB@j}}ABAb{AHO`{CGb{ABh{Oh}}}}}ABDj}\",[]]Ak[\"{{{AHO`{AHCb}}}AJ@f}\",[]]l[\"{NKb}\",[]]Ak[\"{{{AHO`{AHCd}}}AJ@f}\",[]]l[\"{NKd}\",[]]An[\"{{AB@fAAj}{{KGh{ICj}}}}\",[]]B`[\"{{BABdAJ@f}{{KGf{AB@h}}}}\",[]]Bc[\"{BABb{{KGd{ICj{A@d{IEd}}}}}}\",[]]Bk[\"{{{AHO`{BFDd}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{BC@h}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{AHFb}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{AIOh}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{BC@f}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{AJAl}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{BAC`}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{BCLn}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{BCM`}}}{{KFl{{AHO`{IEd}}}}}}\",[]]A`[\"{ABDhKFl}\",[]]A`[\"{ABDjKFl}\",[]]A`[\"{ABDlKFl}\",[]]Bg[\"{{{AHO`{DA`}}}{{KGd{AINjAIKd}}}}\",[]]Aa[\"{BAAbAINj}\",[]]Aa[\"{BAAdAINl}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]A`[\"{ABC`KCb}\",[]]Aa[\"{AHElABDn}\",[]]Aa[\"{BCMfABDn}\",[]]Aa[\"{AHEnABE`}\",[]]Aa[\"{BCMhABE`}\",[]]3210Dh[\"{{{AHO`{NJj}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjAOJj}}}}\",[]]Dh[\"{{{AHO`{NJl}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjAOJl}}}}\",[]]Aj[\"{{{AHO`{AILf}}}CNb}\",[]]Ai[\"{{{AHO`{CNj}}}CNb}\",[]]Ai[\"{{{AHO`{IDh}}}CNb}\",[]]Ai[\"{{{AHO`{NKl}}}CNb}\",[]]Aj[\"{{{AHO`{AILh}}}CNb}\",[]]Ai[\"{{{AHO`{CNl}}}CNb}\",[]]Ai[\"{{{AHO`{IDj}}}CNb}\",[]]Ai[\"{{{AHO`{NKn}}}CNb}\",[]]Aj[\"{{{AHO`{BGOj}}}KCb}\",[]]Aj[\"{{{AHO`{BGOl}}}KCb}\",[]]Aj[\"{{{AHO`{BIAb}}}KCb}\",[]]Aj[\"{{{AHO`{BIAl}}}KCb}\",[]]o[\"{AAjKCb}\",[]]n[\"{{}NIl}\",[]]0A`[\"{A@hAAOf}\",[]]111Ba[\"{{AHO`{AHO`{c}}}ABBj{}}\",[\"K\"]]0000000000000000000Cg[\"{{{AHO`{AB@h}}AJ@f{AHO`{CMl}}}{{KGd{AIKnB@M`}}}}\",[]]Dl[\"{{{AHO`{CGbAB@j}}NK`B@KlAJ@f{AHO`{CMl}}}{{KGd{{KC`{BEGbAILd}}B@Mb}}}}\",[]]Bd[\"{{{AHO`{ABA`}}}{{KFl{BEGf}}}}\",[]]Dm[\"{{{AHO`{AHDj}}KFnCNb{AHO`{{K@j{Oh}}}}{AHO`{CGbAAOb}}}{{KGd{ICjAOJj}}}}\",[]]Ca[\"{{{AHO`{AHCl}}KCb{AHO`{CGb{K@j{Oh}}}}}ICj}\",[]]Bd[\"{{{AHO`{ABAb}}}{{KFl{BEGh}}}}\",[]]Dm[\"{{{AHO`{AHDl}}KG`CNb{AHO`{{K@j{Oh}}}}{AHO`{CGbAAOb}}}{{KGd{ICjAOJl}}}}\",[]]Ca[\"{{{AHO`{AHCn}}KCb{AHO`{CGb{K@j{Oh}}}}}ICj}\",[]]o[\"{{}AONf}\",[]]n[\"{{}IHj}\",[]]o[\"{{}AOJ`}\",[]]o[\"{{}BANd}\",[]]o[\"{{}BAOf}\",[]]n[\"{{}KI`}\",[]]n[\"{{}KFn}\",[]]o[\"{{}AINj}\",[]]o[\"{{}AIKd}\",[]]o[\"{{}ABDn}\",[]]o[\"{{}AILn}\",[]]o[\"{{}AHEl}\",[]]o[\"{{}BCMf}\",[]]o[\"{{}B@Ld}\",[]]o[\"{{}BCOl}\",[]]o[\"{{}BFGh}\",[]]o[\"{{}BFIf}\",[]]o[\"{{}AOL`}\",[]]o[\"{{}BBMh}\",[]]o[\"{{}BH@l}\",[]]o[\"{{}BEJj}\",[]]o[\"{{}AIJl}\",[]]o[\"{{}AOJn}\",[]]o[\"{{}BBAl}\",[]]o[\"{{}BFBj}\",[]]o[\"{{}BANf}\",[]]o[\"{{}BIAb}\",[]]n[\"{{}KIb}\",[]]n[\"{{}KG`}\",[]]o[\"{{}AINl}\",[]]o[\"{{}AIKj}\",[]]o[\"{{}ABE`}\",[]]o[\"{{}AIM`}\",[]]o[\"{{}AHEn}\",[]]o[\"{{}BCMh}\",[]]o[\"{{}BC@j}\",[]]o[\"{{}B@Ll}\",[]]o[\"{{}BCOn}\",[]]o[\"{{}BFGj}\",[]]o[\"{{}BFIh}\",[]]o[\"{{}AOLb}\",[]]o[\"{{}BBMj}\",[]]o[\"{{}BH@n}\",[]]o[\"{{}BIAl}\",[]]o[\"{{}BEJl}\",[]]o[\"{{}AIJn}\",[]]o[\"{{}AOK`}\",[]]o[\"{{}BBAn}\",[]]o[\"{{}BFBl}\",[]]o[\"{{}ABC`}\",[]]n[\"{{}IHl}\",[]]o[\"{{}BCN`}\",[]]o[\"{{}BAOd}\",[]]Ak[\"{{{AHO`{BBBb}}}B@Mh}\",[]]Ak[\"{{{AHO`{BCNh}}}B@Mh}\",[]]Aa[\"{BHE`B@Mh}\",[]]Aa[\"{BFLbB@Mh}\",[]]A`[\"{D@hB@Mh}\",[]]A`[\"{IH`B@Mh}\",[]]A`[\"{IHdB@Mh}\",[]]Aa[\"{BEKfB@Mh}\",[]]Cg[\"{{{AHO`{AHDj}}KFnCNb{AHO`{CGb{K@j{Oh}}}}KCb}ICj}\",[]]Ca[\"{{{AHO`{AHCl}}KCb{AHO`{CGb{K@j{Oh}}}}}ICj}\",[]]Cg[\"{{{AHO`{AHDl}}KG`CNb{AHO`{CGb{K@j{Oh}}}}KCb}ICj}\",[]]Ca[\"{{{AHO`{AHCn}}KCb{AHO`{CGb{K@j{Oh}}}}}ICj}\",[]]Aj[\"{{{AHO`{AIOd}}}DC`}\",[]]Aj[\"{{{AHO`{BGB`}}}DC`}\",[]]Ak[\"{{{AHO`{AILf}}}BFBj}\",[]]Aj[\"{{{AHO`{CNj}}}BFBj}\",[]]Aj[\"{{{AHO`{IDh}}}BFBj}\",[]]Aj[\"{{{AHO`{NKl}}}BFBj}\",[]]Ak[\"{{{AHO`{AILh}}}BFBl}\",[]]Aj[\"{{{AHO`{CNl}}}BFBl}\",[]]Aj[\"{{{AHO`{IDj}}}BFBl}\",[]]Aj[\"{{{AHO`{NKn}}}BFBl}\",[]]Bn[\"{{{AHO`{CGbBCOl}}CMn}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}CMn}{{AHO`{CGbBCOn}}}}\",[]]Bf[\"{{{AHO`{CGbAILf}}NK`CNbCNb}ICj}\",[]]Bf[\"{{{AHO`{CGbAILh}}NK`CNbCNb}ICj}\",[]]Aj[\"{{{AHO`{AILb}}}KFf}\",[]]Bc[\"{{{AHO`{ABBh}}}{{KFl{KFn}}}}\",[]]Aa[\"{BHE`B@Mh}\",[]]Aa[\"{BFLbB@Mh}\",[]]A`[\"{D@hB@Mh}\",[]]A`[\"{IH`B@Mh}\",[]]Aa[\"{BEKfB@Mh}\",[]]Aj[\"{{{AHO`{AJ@`}}}NMn}\",[]]Bn[\"{{{AHO`{AJ@b}}}{{KGd{{KFl{KIb}}B@Ll}}}}\",[]]o[\"{AAjKCb}\",[]]o[\"{IHjKCb}\",[]]Am[\"{{{AHO`{CGbAILd}}}NNf}\",[]]Aj[\"{{{AHO`{AHDj}}}KCb}\",[]]Aj[\"{{{AHO`{AHDl}}}KCb}\",[]]A`[\"{AHO`KHn}\",[]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bd[\"{{{AHO`{BGJ`}}}{{KFl{AILb}}}}\",[]]Bc[\"{{{AHO`{BCAf}}}{{KFl{DA`}}}}\",[]]Bn[\"{{{AHO`{CGbB@Kh}}CN`}{{AHO`{CGbB@Kh}}}}\",[]]Bn[\"{{{AHO`{CGbB@Kl}}CN`}{{AHO`{CGbB@Kl}}}}\",[]]A`[\"{BHE`CN`}\",[]]A`[\"{BFLbCN`}\",[]]o[\"{D@hCN`}\",[]]o[\"{IH`CN`}\",[]]A`[\"{AHAlKFl}\",[]]Aa[\"{ABDlAHO`}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Aa[\"{BC@lAB@d}\",[]]A`[\"{KDlBC@j}\",[]]Aa[\"{AHElAILn}\",[]]Aa[\"{BCMfAILn}\",[]]Aa[\"{AHEnAIM`}\",[]]Aa[\"{BCMhAIM`}\",[]]3210Bh[\"{{{AHO`{CGbc}}KCb}B@Mf{A@fIHh}}\",[\"\"]]Bh[\"{{{AHO`{CGbc}}KCb}B@Mh{A@fIHh}}\",[\"\"]]o[\"{{}BCNj}\",[]]o[\"{{}BCNl}\",[]]A`[\"{CNbBIAb}\",[]]Cd[\"{{{AHO`{CMl}}}{{KGh{AOMfc}}}{}}\",[\"FromStr::Err\"]]Cd[\"{{{AHO`{CMl}}}{{KGh{AOInc}}}{}}\",[\"FromStr::Err\"]]o[\"{CN`KI`}\",[]]o[\"{CN`KIb}\",[]]Al[\"{CNb{{KGd{KI`BGIj}}}}\",[]]Al[\"{CNb{{KGd{KIbBGIl}}}}\",[]]Aa[\"{BGGnAOJh}\",[]]Aa[\"{BIEbBB@b}\",[]]Aa[\"{BGH`AOJh}\",[]]Aa[\"{BIEdBB@b}\",[]]Af[\"{{}{{KFl{Ab}}}}\",[]]0000Aj[\"{{{AHO`{AILd}}}DC`}\",[]]Bo[\"{{{AHO`{CGbBFIf}}AB@d}{{AHO`{CGbBFIf}}}}\",[]]Bo[\"{{{AHO`{CGbBFIh}}AB@d}{{AHO`{CGbBFIh}}}}\",[]]Bb[\"{{{A@d{AILf}}}{{A@d{A@`}}}}\",[]]Ba[\"{{{A@d{CNj}}}{{A@d{A@`}}}}\",[]]Ba[\"{{{A@d{IDh}}}{{A@d{A@`}}}}\",[]]Ba[\"{{{A@d{NKl}}}{{A@d{A@`}}}}\",[]]Bb[\"{{{A@d{AILh}}}{{A@d{A@`}}}}\",[]]Ba[\"{{{A@d{CNl}}}{{A@d{A@`}}}}\",[]]Ba[\"{{{A@d{IDj}}}{{A@d{A@`}}}}\",[]]Ba[\"{{{A@d{NKn}}}{{A@d{A@`}}}}\",[]]Aj[\"{{{AHO`{BGJ`}}}DC`}\",[]]Bo[\"{{{AHO`{CGbBH@l}}AB@d}{{AHO`{CGbBH@l}}}}\",[]]Bo[\"{{{AHO`{CGbBH@n}}AB@d}{{AHO`{CGbBH@n}}}}\",[]]Bc[\"{{{AHO`{AIKn}}}{{KFl{KFb}}}}\",[]]Bc[\"{{{AHO`{ABA`}}}{{KFl{KFb}}}}\",[]]Bm[\"{{{AHO`{DA`}}}{{KGd{{KFl{KFb}}AIKd}}}}\",[]]Bc[\"{{{AHO`{AHCb}}}{{KFl{KFb}}}}\",[]]Bc[\"{{{AHO`{ABAb}}}{{KFl{KFb}}}}\",[]]Bc[\"{{{AHO`{AHCd}}}{{KFl{KFb}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Bc[\"{{{AHO`{AHBb}}}{{KFl{KCb}}}}\",[]]Am[\"{{{AHO`{AONh}}KCb}ICj}\",[]]Aj[\"{{{AHO`{AILb}}}NLb}\",[]]Bg[\"{{{AHO`{AJ@`}}}{{KGd{CElB@Ld}}}}\",[]]Bg[\"{{{AHO`{AJ@b}}}{{KGd{CElB@Ll}}}}\",[]]A`[\"{BAOjKFl}\",[]]A`[\"{BAOlKFl}\",[]]A`[\"{AHAlKFl}\",[]]A`[\"{AOIjKFl}\",[]]Ck[\"{{{AHO`{CGbBBMh}}{KFl{{IIh{Oh}}}}}{{AHO`{CGbBBMh}}}}\",[]]Ck[\"{{{AHO`{CGbBBMj}}{KFl{{IIh{Oh}}}}}{{AHO`{CGbBBMj}}}}\",[]]A`[\"{BFBjKFl}\",[]]A`[\"{BFBlKFl}\",[]]Ac[\"{AHO`c{}}\",[\"T\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Ao[\"{c{{KGd{e}}}{}{}}\",[\"U\",\"T\"]]000000000000000000000000000000000000000000000000000000Al[\"{CNb{{KGd{KI`BGIj}}}}\",[]]Al[\"{ICh{{KGd{KI`BGIj}}}}\",[]]Al[\"{KCb{{KGd{KI`BGIj}}}}\",[]]3333333333333333333Aj[\"{AB@d{{KGd{AOL`}}}}\",[]]444444Ai[\"{CNb{{KGd{AHCf}}}}\",[]]55555555555555555555555555555Bc[\"{{{A@b{B@Kd}}}{{KGd{BEJb}}}}\",[]]Aj[\"{B@Kd{{KGd{BEJb}}}}\",[]]77Bc[\"{{{A@b{BAB`}}}{{KGd{BEJf}}}}\",[]]Aj[\"{BAB`{{KGd{BEJf}}}}\",[]]999999999999999999999Al[\"{ICh{{KGd{KIbBGIl}}}}\",[]]Al[\"{KCb{{KGd{KIbBGIl}}}}\",[]];Al[\"{CNb{{KGd{KIbBGIl}}}}\",[]]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Bm[\"{B@Kd{{KGd{BEJdc}}}{}}\",[\"TryFrom::Error\"]]Cf[\"{{{A@b{B@Kd}}}{{KGd{BEJdc}}}{}}\",[\"TryFrom::Error\"]]Ao[\"{c{{KGd{e}}}{}{}}\",[\"U\",\"T\"]]00Cf[\"{{{A@b{BAB`}}}{{KGd{BEJhc}}}{}}\",[\"TryFrom::Error\"]]Bm[\"{BAB`{{KGd{BEJhc}}}{}}\",[\"TryFrom::Error\"]]22222222222222222222222Aj[\"{{}{{KGd{c}}}{}}\",[\"U\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Cd[\"{{{ABb{{AHO`{CGbc}}}}{AHO`{CGbNIn}}}DAh{}}\",[\"F\"]]000000000Cc[\"{{{AHO`{BCAd}}AJAj{AHO`{ABDl}}}{{KGf{ICj}}}}\",[]]Bk[\"{{{AHO`{BGOj}}B@Mf}{{KGd{ICjAIMd}}}}\",[]]Bk[\"{{{AHO`{BGOl}}B@Mh}{{KGd{ICjAIMf}}}}\",[]]0Bk[\"{{{AHO`{BIAb}}B@Mh}{{KGd{ICjAIMf}}}}\",[]]Ak[\"{{{AHO`{AILb}}}AAO`}\",[]]Bi[\"{{{AHO`{CGbBACd}}}{{KGd{KI`IF`}}}}\",[]]Bi[\"{{{AHO`{CGbBACd}}}{{KGd{DAlIF`}}}}\",[]]Bi[\"{{{AHO`{CGbBACd}}}{{KGd{A@jIF`}}}}\",[]]Bj[\"{{{AHO`{CGbBACd}}}{{KGd{ABCdIF`}}}}\",[]]Bf[\"{{{AHO`{CGbBACd}}}{{KGj{KIb}}}}\",[]]Bf[\"{{{AHO`{CGbBACd}}}{{KGj{DAn}}}}\",[]]Bf[\"{{{AHO`{CGbBACd}}}{{KGj{A@l}}}}\",[]]Bg[\"{{{AHO`{CGbBACd}}}{{KGj{ABCf}}}}\",[]]Aa[\"{AOInBGIh}\",[]]Bd[\"{{{AHO`{AILf}}}{{A@d{AILf}}}}\",[]]Bc[\"{{{AHO`{CNj}}}{{A@d{AILf}}}}\",[]]Bc[\"{{{AHO`{IDh}}}{{A@d{AILf}}}}\",[]]Bc[\"{{{AHO`{NKl}}}{{A@d{AILf}}}}\",[]]Bd[\"{{{AHO`{AILh}}}{{A@d{AILh}}}}\",[]]Bc[\"{{{AHO`{CNl}}}{{A@d{AILh}}}}\",[]]Bc[\"{{{AHO`{IDj}}}{{A@d{AILh}}}}\",[]]Bc[\"{{{AHO`{NKn}}}{{A@d{AILh}}}}\",[]]A`[\"{ABDnCNb}\",[]]An[\"{{{AHO`{CGbAILd}}}AHBb}\",[]]A`[\"{ABE`CNb}\",[]]C`[\"{{{AHO`{CGbB@Mf}}}{{AHO`{CGb{K@j{Oh}}}}}}\",[]]C`[\"{{{AHO`{CGbB@Mh}}}{{AHO`{CGb{K@j{Oh}}}}}}\",[]]Ah[\"{Oh{{KFl{B@N`}}}}\",[]]Ah[\"{Oh{{KFl{B@Nb}}}}\",[]]Ah[\"{Oh{{KFl{B@Nd}}}}\",[]]A`[\"{ABCdDAl}\",[]]A`[\"{ABCfDAn}\",[]]Bi[\"{AIKn{{KGd{{KC`{AILbBD@f}}AIKn}}}}\",[]]Aa[\"{{}c{}}\",[\"I\"]]o[\"{DAlDC`}\",[]]o[\"{DAnDC`}\",[]]Aj[\"{{{AHO`{AILd}}}DC`}\",[]]21Aj[\"{{{AHO`{ABA`}}}DC`}\",[]]Aj[\"{{{AHO`{ABAb}}}DC`}\",[]]Bn[\"{{{AHO`{CGbBABd}}DC`}{{AHO`{CGbBABd}}}}\",[]]Bn[\"{{{AHO`{CGbBABh}}DC`}{{AHO`{CGbBABh}}}}\",[]]Bm[\"{{{AHO`{NMd}}NK`}{{ABb{{A@d{AIJd}}}}}}\",[]]Bn[\"{{{AHO`{BACb}}NK`}{{ABb{{A@d{AIJd}}}}}}\",[]]Bn[\"{{{AHO`{AOMl}}NK`}{{ABb{{A@d{AIJd}}}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Ak[\"{{{AHO`{AILb}}}ABBf}\",[]]Bo[\"{{{AHO`{AILb}}cAINj}ABBh{{D@b{AHCb}}}}\",[\"\"]]Ce[\"{{{AHO`{CGbAILd}}AHCdAINlNK`}{{KGd{KG`AHEb}}}}\",[]]43Bm[\"{{{AHO`{CGbAILd}}KG`}{{KGd{ICjAIKj}}}}\",[]]E`[\"{{{ABb{{AHO`{CGbBGIb}}}}{AHO`{CGbNIn}}}{{DAh{{KFl{c}}}}}{}}\",[\"Stream::Item\"]]E`[\"{{{ABb{{AHO`{CGbBEFb}}}}{AHO`{CGbNIn}}}{{DAh{{KFl{c}}}}}{}}\",[\"Stream::Item\"]]E`[\"{{{ABb{{AHO`{CGbAINh}}}}{AHO`{CGbNIn}}}{{DAh{{KFl{c}}}}}{}}\",[\"Stream::Item\"]]E`[\"{{{ABb{{AHO`{CGbBGA`}}}}{AHO`{CGbNIn}}}{{DAh{{KFl{c}}}}}{}}\",[\"Stream::Item\"]]E`[\"{{{ABb{{AHO`{CGbAIOd}}}}{AHO`{CGbNIn}}{AHO`{CGbNLn}}}{{DAh{{KGf{ICj}}}}}}\",[]]Ec[\"{{{AHO`{CGbAIOd}}{AHO`{CGbNIn}}{AHO`{CGb{K@j{Oh}}}}}{{DAh{{KGd{KCbAHFb}}}}}}\",[]]Ef[\"{{{ABb{{AHO`{CGbAIOd}}}}{AHO`{CGbNIn}}{AHO`{CGb{K@j{Oh}}}}}{{DAh{{KGf{KCb}}}}}}\",[]]Fg[\"{{{AHO`{CGbBBL`}}{AHO`{CGbNIn}}{AHO`{CGb{K@j{AIMh}}}}{AHO`{CGb{K@j{ABC`}}}}}{{DAh{{KGf{KCb}}}}}}\",[]]Dn[\"{{{ABb{{AHO`{CGbAHFj}}}}{AHO`{ABDl}}{AHO`{CGbNIn}}}{{DAh{{KGf{ICj}}}}}}\",[]]>=Ce[\"{{{AHO`{CGbBBMh}}{A@b{NJj}}}{{AHO`{CGbBBMh}}}}\",[]]Ce[\"{{{AHO`{CGbBBMj}}{A@b{NJl}}}{{AHO`{CGbBBMj}}}}\",[]]Cj[\"{{{AHO`{BABf}}CN`B@Mf{AHO`{{K@j{Oh}}}}}{{IIh{Oh}}}}\",[]]Cj[\"{{{AHO`{BEJf}}CN`B@Mf{AHO`{{K@j{Oh}}}}}{{IIh{Oh}}}}\",[]]Cj[\"{{{AHO`{BABj}}CN`B@Mh{AHO`{{K@j{Oh}}}}}{{IIh{Oh}}}}\",[]]Cj[\"{{{AHO`{BEJh}}CN`B@Mh{AHO`{{K@j{Oh}}}}}{{IIh{Oh}}}}\",[]]Cj[\"{{{AHO`{BHKd}}CN`B@Md{AHO`{{K@j{Oh}}}}}{{IIh{Oh}}}}\",[]]Bb[\"{KCb{{KC`{KCb{KFl{KCb}}}}}}\",[]]000Aj[\"{{{AHO`{AILb}}}KCb}\",[]]A`[\"{AHO`KHl}\",[]]0000000000000000000000000000000000000000000000000000000000000000000Cf[\"{{{AHO`{CGbBABd}}{A@b{BFBn}}}{{AHO`{CGbBABd}}}}\",[]]Cf[\"{{{AHO`{CGbBABh}}{A@b{BFC`}}}{{AHO`{CGbBABh}}}}\",[]]A`[\"{BHE`IGf}\",[]]A`[\"{BABdA@b}\",[]]A`[\"{BABhA@b}\",[]]Aj[\"{{{AHO`{AB@h}}}ICj}\",[]]Ck[\"{{{AHO`{CGbAJ@`}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjAJAl}}}}\",[]]n[\"{{}KCb}\",[]]Ak[\"{{{AHO`{AILb}}}AH@h}\",[]]Ba[\"{{{AHO`{CGbABDn}}ABDn}ICj}\",[]]Ba[\"{{{AHO`{CGbAILn}}AILn}ICj}\",[]]Ba[\"{{{AHO`{CGbBCMf}}AHEl}ICj}\",[]]Ba[\"{{{AHO`{CGbABE`}}ABE`}ICj}\",[]]Ba[\"{{{AHO`{CGbAIM`}}AIM`}ICj}\",[]]Ba[\"{{{AHO`{CGbBCMh}}AHEn}ICj}\",[]]Bn[\"{{{AHO`{CGbBCOl}}DC`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}DC`}{{AHO`{CGbBCOn}}}}\",[]]Bi[\"{{{AHO`{CGb}}}{{AHO`{CGbc}}}{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bg[\"{{{AHO`{AIOd}}}{{KGd{CNbB@Ld}}}}\",[]]Bg[\"{{{AHO`{AIOf}}}{{KGd{CNbB@Ll}}}}\",[]]Bc[\"{{AHO`{AHO`{CGbc}}}ICj{}}\",[\"T\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Cc[\"{{{AHO`{CGbAILd}}NK`KG`KIb}{{KGd{ICjBBLf}}}}\",[]]A`[\"{AHAlDC`}\",[]]B`[\"{{AHO`{AHO`{c}}}DC`{}}\",[\"K\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Aa[\"{BCLhBFKn}\",[]]A`[\"{BEFfKI`}\",[]]Aa[\"{BCLlBFL`}\",[]]A`[\"{BEFjKIb}\",[]]A`[\"{BFAnKIb}\",[]]A`[\"{BCNnKIb}\",[]]o[\"{NMjKIb}\",[]]Aa[\"{BCLhAIMj}\",[]]Aa[\"{BCLlAIMl}\",[]]o[\"{KI`CNb}\",[]]o[\"{KIbCNb}\",[]]Aj[\"{{{AHO`{AILd}}}DC`}\",[]]Aj[\"{{{AHO`{BAOd}}}DC`}\",[]]Bd[\"{{{AHO`{BBL`}}}{{KGf{AJ@f}}}}\",[]]Bd[\"{{{AHO`{AB@h}}}{{KGf{AJ@f}}}}\",[]]A`[\"{AHElCNb}\",[]]A`[\"{BCMfCNb}\",[]]A`[\"{AHEnCNb}\",[]]A`[\"{BCMhCNb}\",[]]Cc[\"{{{AHO`{BCN`}}{AHO`{AIOn}}CNb}{{A@b{AONh}}}}\",[]]Bn[\"{{{AHO`{CGbBCOl}}DC`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}DC`}{{AHO`{CGbBCOn}}}}\",[]]Bg[\"{{{AHO`{AILb}}KFn}{{KFl{AHEl}}}}\",[]]o[\"{KEdABh}\",[]]A`[\"{AHBfA@d}\",[]]Bj[\"{{{AHO`{CGbAILd}}KG`}{{KFl{AHEn}}}}\",[]]A`[\"{AHBhA@d}\",[]]Db[\"{{{ABb{{AHO`{CGbAJ@`}}}}{AHO`{CGbNIn}}}{{DAh{{KGf{ICj}}}}}}\",[]]00Eg[\"{{{ABb{{AHO`{CGbAJ@`}}}}{AHO`{CGbNIn}}{AHO`{{K@j{Oh}}}}}{{DAh{{KGd{KCbAJAl}}}}}}\",[]]Ec[\"{{{ABb{{AHO`{CGbAJ@`}}}}{AHO`{CGbNIn}}{AHO`{{K@j{Oh}}}}}{{DAh{{KGf{KCb}}}}}}\",[]]0Bm[\"{{{AHO`{IHl}}{AHO`{D@n}}}{{KGf{ICj}}}}\",[]]Cd[\"{{{AHO`{CGbAIOd}}KCb}{{KGd{{KFl{ICn}}AHFb}}}}\",[]]Cd[\"{{{AHO`{CGbBGB`}}KCb}{{KGd{{KFl{ID`}}AHFb}}}}\",[]]Cn[\"{{{AHO`{CGbAIOd}}{AHO`{CGb{K@j{Oh}}}}}{{KGd{ICjBC@f}}}}\",[]]Bk[\"{{{AHO`{DA`}}AINj}{{KGd{AINjBFKd}}}}\",[]]Bn[\"{{{AHO`{CGbAINn}}NK`}{{AHO`{CGbAINn}}}}\",[]]Bn[\"{{{AHO`{CGbAIO`}}NK`}{{AHO`{CGbAIO`}}}}\",[]]A`[\"{BC@lAG`}\",[]]o[\"{IHjCNb}\",[]]o[\"{{}AJ@f}\",[]]0o[\"{{}AHO`}\",[]]Bk[\"{{ABA`{A@b{BABd}}}{{KGd{AIKnBCLn}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Bh[\"{{{AHO`{AHAl}}AJ@f}{{KGh{AHFl}}}}\",[]]A`[\"{AHElCMn}\",[]]Aj[\"{{{AHO`{AILd}}}CMn}\",[]]A`[\"{AHEnCMn}\",[]]Ce[\"{{{AHO`{CGbAINn}}{KFl{KHl}}}{{AHO`{CGbAINn}}}}\",[]]Ce[\"{{{AHO`{CGbAIO`}}{KFl{KHl}}}{{AHO`{CGbAIO`}}}}\",[]]Aa[\"{ABDhAJ@f}\",[]]Aa[\"{ABDjAJ@f}\",[]]Aa[\"{ABDlAJ@f}\",[]]Aa[\"{AJ@fAHCb}\",[]]Aa[\"{AJ@fAHCd}\",[]]o[\"{AAjCMn}\",[]]Bn[\"{{{AHO`{CGbBCOl}}CMn}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}CMn}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHAlCMn}\",[]]Bo[\"{{{AHO`{CGbBCOl}}AB@d}{{AHO`{CGbBCOl}}}}\",[]]Bo[\"{{{AHO`{CGbBCOn}}AB@d}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHAlKFl}\",[]]k[\"{{}}\",[]]0000000000Bh[\"{ABA`c{}}\",[\"IntoFuture::IntoFuture\"]]11Aj[\"{{{AHO`{BAOd}}}DC`}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]o[\"{AAjKCb}\",[]]Ao[\"{{AHO`c}{{NKf{c}}}{}}\",[\"S\"]]0C`[\"{{{AHO`{CGbAILf}}NK`CNbDC`{KFl{CNb}}}ICj}\",[]]Bo[\"{{{AHO`{CGbCNj}}NK`CNbDC`{KFl{CNb}}}ICj}\",[]]C`[\"{{{AHO`{CGbAILh}}NK`CNbDC`{KFl{CNb}}}ICj}\",[]]Bo[\"{{{AHO`{CGbCNl}}NK`CNbDC`{KFl{CNb}}}ICj}\",[]]Bk[\"{{{AHO`{CGbIHl}}NK`{AHO`{BCN`}}}ICj}\",[]]A`[\"{BFBjKFl}\",[]]A`[\"{BFBlKFl}\",[]]Bn[\"{{{AHO`{KI`}}{AHO`{KI`}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{KFn}}{AHO`{KFn}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{AOL`}}{AHO`{AOL`}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{B@Mf}}{AHO`{B@Mf}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{DAl}}{AHO`{DAl}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{A@j}}{AHO`{A@j}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{ABCd}}{AHO`{ABCd}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{KIb}}{AHO`{KIb}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{BFDf}}{AHO`{BFDf}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{KG`}}{AHO`{KG`}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{AHFd}}{AHO`{AHFd}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{BBBl}}{AHO`{BBBl}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{AJAn}}{AHO`{AJAn}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{AOLb}}{AHO`{AOLb}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{BEGb}}{AHO`{BEGb}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{BFCl}}{AHO`{BFCl}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{B@Mh}}{AHO`{B@Mh}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{DAn}}{AHO`{DAn}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{A@l}}{AHO`{A@l}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{ABCf}}{AHO`{ABCf}}}{{KFl{ABBj}}}}\",[]]Ak[\"{{{AHO`{AILb}}}AINh}\",[]]Bk[\"{{{AHO`{AILd}}KG`}{{KGd{AINlAIKj}}}}\",[]]Cc[\"{{{AHO`{CGbAIOd}}KCb}{{KGd{{ABh{Oh}}BC@h}}}}\",[]]Bb[\"{{{AHO`{CGbAILd}}ABCf}AIOf}\",[]]Aj[\"{{{AHO`{AHCl}}}KCb}\",[]]Aj[\"{{{AHO`{AHCn}}}KCb}\",[]]Bb[\"{{{AHO`{CGbAILd}}ABCf}AJ@b}\",[]]Bn[\"{{{AHO`{CGbBCOl}}CNb}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}CNb}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHAlKFl}\",[]]A`[\"{BAOjKFl}\",[]]A`[\"{BAOlKFl}\",[]]Cf[\"{{{AHO`{CGbBABd}}{A@b{AJ@n}}}{{AHO`{CGbBABd}}}}\",[]]Cf[\"{{{AHO`{CGbBABh}}{A@b{AJA`}}}{{AHO`{CGbBABh}}}}\",[]]Cf[\"{{{AHO`{CGbB@Kh}}{A@b{AJAf}}}{{AHO`{CGbB@Kh}}}}\",[]]Cf[\"{{{AHO`{CGbB@Kl}}{A@b{AJAh}}}{{AHO`{CGbB@Kl}}}}\",[]]o[\"{AAjCNb}\",[]]Bn[\"{{{AHO`{CGbBFIf}}CMn}{{AHO`{CGbBFIf}}}}\",[]]Bn[\"{{{AHO`{CGbBFIh}}CMn}{{AHO`{CGbBFIh}}}}\",[]]Ak[\"{{{AHO`{AILb}}}BGJ`}\",[]]Aj[\"{{{AHO`{DA`}}}BCAf}\",[]]Aj[\"{{{A@b{BABf}}}BABd}\",[]]Aj[\"{{{A@b{BABj}}}BABh}\",[]]Aj[\"{{BCNjc}BCNjABCn}\",[\"\"]]Aj[\"{{BCNlc}BCNlABCn}\",[\"\"]]Bm[\"{{{AHO`{CGbAJ@`}}ICn}{{KGd{ICjAJAl}}}}\",[]]n[\"{{}KFb}\",[]]o[\"{NIlNIl}\",[]]0000Bd[\"{{{AHO`{BGOj}}}{{KFl{AB@d}}}}\",[]]Bd[\"{{{AHO`{BGOl}}}{{KFl{AB@d}}}}\",[]]Bd[\"{{{AHO`{BIAb}}}{{KFl{AB@d}}}}\",[]]Bd[\"{{{AHO`{BIAl}}}{{KFl{AB@d}}}}\",[]]Bd[\"{{{AHO`{AILb}}}{{KFl{BCLn}}}}\",[]]Ck[\"{{{AHO`{AB@h}}B@KhAJ@f{AHO`{CMl}}}{{KGd{AIKnB@M`}}}}\",[]]Ak[\"{{{AHO`{BAAl}}}AB@d}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]An[\"{{AIObDC`}{{KGh{KCb}}}}\",[]]Cl[\"{{{AHO`{NMf}}}{{KFl{{KC`{{A@d{AHCl}}{A@d{AHDj}}}}}}}}\",[]]Cm[\"{{{AHO`{AJAb}}}{{KFl{{KC`{{A@d{AHCl}}{A@d{AHDj}}}}}}}}\",[]]Cl[\"{{{AHO`{NMh}}}{{KFl{{KC`{{A@d{AHCn}}{A@d{AHDl}}}}}}}}\",[]]Cm[\"{{{AHO`{AJAd}}}{{KFl{{KC`{{A@d{AHCn}}{A@d{AHDl}}}}}}}}\",[]]A`[\"{BGGnABh}\",[]]A`[\"{BIEbABh}\",[]]A`[\"{BGH`ABh}\",[]]A`[\"{BIEdABh}\",[]]An[\"{{{AHO`{CGbBGOj}}}B@Mf}\",[]]An[\"{{{AHO`{CGbBGOl}}}B@Mh}\",[]]An[\"{{{AHO`{CGbBIAb}}}B@Mh}\",[]]An[\"{{{AHO`{CGbBIAl}}}B@Mh}\",[]]Ak[\"{{{AHO`{BCAd}}}B@Oj}\",[]]Ba[\"{{{AHO`{CGbAILd}}BCMd}ICj}\",[]]Bo[\"{{{AHO`{CGbAB@j}}BEGbBAOd}{{KFl{BCMd}}}}\",[]]B`[\"{{{AHO`{NMf}}B@MfDAl}D@d}\",[]]Bn[\"{{{AHO`{BABf}}CN`B@Mf}{{KGd{D@dBFLf}}}}\",[]]Ba[\"{{{AHO`{AJAb}}B@MfDAl}D@d}\",[]]Bn[\"{{{AHO`{BEJf}}CN`B@Mf}{{KGd{D@dBFLf}}}}\",[]]B`[\"{{{AHO`{NMh}}B@MhDAn}D@f}\",[]]Bn[\"{{{AHO`{BABj}}CN`B@Mh}{{KGd{D@fBFLl}}}}\",[]]Ba[\"{{{AHO`{AJAd}}B@MhDAn}D@f}\",[]]Bn[\"{{{AHO`{BEJh}}CN`B@Mh}{{KGd{D@fBFLl}}}}\",[]]Bn[\"{{{AHO`{BHKd}}CN`B@Md}{{KGd{D@fBFLl}}}}\",[]]Aj[\"{{}{{NM`{c}}}{}}\",[\"T\"]]0A`[\"{AHElCNb}\",[]]A`[\"{BCMfCNb}\",[]]A`[\"{AHEnCNb}\",[]]A`[\"{BCMhCNb}\",[]]Bn[\"{{{AHO`{CGbBABd}}KCb}{{AHO`{CGbBABd}}}}\",[]]Bn[\"{{{AHO`{CGbBABh}}KCb}{{AHO`{CGbBABh}}}}\",[]]Aj[\"{{{AHO`{BBL`}}}DC`}\",[]]0Aj[\"{{{AHO`{BCAd}}}DC`}\",[]]Bg[\"{{{AHO`{DA`}}}{{KGd{AHCbAIKd}}}}\",[]]Bk[\"{{{AHO`{AILd}}KG`}{{KGd{AHCdAIKj}}}}\",[]]Cc[\"{{{AHO`{BCN`}}{AHO`{AIOb}}CNb}{{A@b{AONh}}}}\",[]]Ak[\"{{{AHO`{ABA`}}}B@Mf}\",[]]Ak[\"{{{AHO`{ABAb}}}B@Mh}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Bf[\"{{{AHO`{CGbAILd}}}{{KFl{NK`}}}}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{A@b{AOLn}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{A@b{AOM`}}}{{AHO`{CGbBCOn}}}}\",[]]43A`[\"{AAjAOMf}\",[]]A`[\"{ABDhKFl}\",[]]A`[\"{ABDjKFl}\",[]]A`[\"{ABDlKFl}\",[]]A`[\"{BFBjKFl}\",[]]A`[\"{BFBlKFl}\",[]]Ai[\"{{{AHO`{NNf}}}KCb}\",[]]Bo[\"{{{AHO`{CGbBIAb}}AB@d}{{AHO`{CGbBIAb}}}}\",[]]Bo[\"{{{AHO`{CGbBIAl}}AB@d}{{AHO`{CGbBIAl}}}}\",[]]Bj[\"{{{AHO`{AJ@`}}CEl}{{KGd{ICjB@Ld}}}}\",[]]Bm[\"{{{AHO`{CGbAJ@b}}CEl}{{KGd{ICjB@Ll}}}}\",[]]?>A`[\"{IHjAONf}\",[]]A`[\"{AOJ`IHj}\",[]]Bj[\"{{{A@b{B@Kd}}IHn}{{KGd{BEJbBGHn}}}}\",[]]Bj[\"{{{A@b{BAB`}}IHn}{{KGd{BEJfBGHn}}}}\",[]]Bj[\"{{{A@b{B@Kd}}IHn}{{KGd{BEJdBGI`}}}}\",[]]Bj[\"{{{A@b{BAB`}}IHn}{{KGd{BEJhBGI`}}}}\",[]]Dj[\"{{{AHO`{CGbAJ@b}}{AHO`{CGb{AHO`{CGb{K@j{ICn}}}}}}}{{KGd{KCbAJAn}}}}\",[]]Aj[\"{{{AHO`{AILd}}}DC`}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]A`[\"{AHAlDC`}\",[]]Cd[\"{{{AHO`{CGbBBMh}}{A@b{A`}}}{{AHO`{CGbBBMh}}}}\",[]]Cd[\"{{{AHO`{CGbBBMj}}{A@b{A`}}}{{AHO`{CGbBBMj}}}}\",[]]Bk[\"{{{AHO`{BBL`}}}{{ABb{{A@d{AHFj}}}}}}\",[]]o[\"{AAjCNb}\",[]]Aa[\"{AONfAHD`}\",[]]76Aa[\"{AIOjABA`}\",[]]Aa[\"{AIOlABAb}\",[]]Cf[\"{{{AHO`{CGbBFGh}}{KFl{AB@d}}}{{AHO`{CGbBFGh}}}}\",[]]Cf[\"{{{AHO`{CGbBFGj}}{KFl{AB@d}}}{{AHO`{CGbBFGj}}}}\",[]]9;:An[\"{{{AHO`{AONh}}AB@d}ICj}\",[]]B`[\"{{{AHO`{CGbAILf}}CMn}ICj}\",[]]Ao[\"{{{AHO`{CGbCNj}}CMn}ICj}\",[]]Ao[\"{{{AHO`{CGbIDh}}CMn}ICj}\",[]]Ao[\"{{{AHO`{CGbNKl}}CMn}ICj}\",[]]B`[\"{{{AHO`{CGbAILh}}CMn}ICj}\",[]]Ao[\"{{{AHO`{CGbCNl}}CMn}ICj}\",[]]Ao[\"{{{AHO`{CGbIDj}}CMn}ICj}\",[]]Ao[\"{{{AHO`{CGbNKn}}CMn}ICj}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]10Bj[\"{{{AHO`{AILb}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bi[\"{{{AHO`{NMf}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bj[\"{{{AHO`{AJAb}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bi[\"{{{AHO`{NMh}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bj[\"{{{AHO`{AJAd}}}{{KFl{{A@d{A@`}}}}}}\",[]]Db[\"{{{ABb{{AHO`{CGbAJ@`}}}}{AHO`{CGbNIn}}}{{DAh{{KGf{ICj}}}}}}\",[]]Db[\"{{{AHO`{CGbAILd}}NK`B@Oj{AHO`{CGb{ABh{Oh}}}}}{{KFl{ABDj}}}}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{AHO`{CMl}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{AHO`{CMl}}}{{AHO`{CGbBCOn}}}}\",[]]Ak[\"{{{AHO`{AILb}}}BAA`}\",[]]Bj[\"{{{AHO`{AILb}}ICn}{{KGd{ICjBFDd}}}}\",[]]Bn[\"{{{AHO`{CGbBCOl}}DC`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}DC`}{{AHO`{CGbBCOn}}}}\",[]]Ai[\"{{{AHO`{NJj}}}KCb}\",[]]Ai[\"{{{AHO`{NJl}}}KCb}\",[]]Dh[\"{{{AHO`{B@Kj}}CN`{AHO`{CMl}}{AHO`{BGAn}}}{{KGd{{A@d{NMf}}B@M`}}}}\",[]]Cb[\"{{{AHO`{BABf}}CN`{AHO`{BGAn}}}{{A@d{NMf}}}}\",[]]Dh[\"{{{AHO`{BEJb}}CN`{AHO`{CMl}}{AHO`{BGAn}}}{{KGd{{A@d{NMf}}B@M`}}}}\",[]]Cb[\"{{{AHO`{BEJf}}CN`{AHO`{BGAn}}}{{A@d{NMf}}}}\",[]]Dh[\"{{{AHO`{B@Kn}}CN`{AHO`{CMl}}{AHO`{BGAn}}}{{KGd{{A@d{NMh}}B@Mb}}}}\",[]]Cb[\"{{{AHO`{BABj}}CN`{AHO`{BGAn}}}{{A@d{NMh}}}}\",[]]Dh[\"{{{AHO`{BEJd}}CN`{AHO`{CMl}}{AHO`{BGAn}}}{{KGd{{A@d{NMh}}B@Mb}}}}\",[]]Cb[\"{{{AHO`{BEJh}}CN`{AHO`{BGAn}}}{{A@d{NMh}}}}\",[]]Dh[\"{{{AHO`{BHKb}}CN`{AHO`{CMl}}{AHO`{BGAn}}}{{KGd{{A@d{NMh}}B@Ln}}}}\",[]]Cb[\"{{{AHO`{BHKd}}CN`{AHO`{BGAn}}}{{A@d{NMh}}}}\",[]]Dd[\"{{{ABb{{AHO`{CGbc}}}}{AHO`{CGbNIn}}}{{DAh{{KFl{KGd}}}}}{}}\",[\"S\"]]0Bn[\"{{AJ@fBBLbAAj}{{KGh{{KC`{AB@fAIL`}}}}}}\",[]]Bd[\"{{{AHO`{AILd}}}{{AHO`{NMh}}}}\",[]]A`[\"{AOJ`IHj}\",[]]Cb[\"{{{AHO`{AOLn}}DAlAJ@fB@MfNK`}{{KFl{AINn}}}}\",[]]Cb[\"{{{AHO`{BCNj}}DAlAJ@fB@MfNK`}{{KFl{AINn}}}}\",[]]Cb[\"{{{AHO`{AOM`}}DAnAJ@fB@MhNK`}{{KFl{AIO`}}}}\",[]]Cb[\"{{{AHO`{BCNl}}DAnAJ@fB@MhNK`}{{KFl{AIO`}}}}\",[]]B`[\"{{{AHO`{CGbAILd}}NK`}ICj}\",[]]Ca[\"{{{AHO`{CGbAIKn}}}{{KGd{{A@d{A@`}}BCLn}}}}\",[]]Bj[\"{{{AHO`{AILb}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bi[\"{{{AHO`{NMf}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bj[\"{{{AHO`{AJAb}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bi[\"{{{AHO`{NMh}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bj[\"{{{AHO`{AJAd}}}{{KFl{{A@d{A@`}}}}}}\",[]]o[\"{AILnOh}\",[]]o[\"{AIM`Oh}\",[]]Ai[\"{{{AHO`{CMl}}}DC`}\",[]]Aj[\"{{{AHO`{AILf}}}CNb}\",[]]Ai[\"{{{AHO`{CNj}}}CNb}\",[]]Bn[\"{{{AHO`{CGbAIJl}}CNb}{{AHO`{CGbAIJl}}}}\",[]]Ai[\"{{{AHO`{IDh}}}CNb}\",[]]Bn[\"{{{AHO`{CGbAOJn}}CNb}{{AHO`{CGbAOJn}}}}\",[]]Ai[\"{{{AHO`{NKl}}}CNb}\",[]]Bn[\"{{{AHO`{CGbBBAl}}CNb}{{AHO`{CGbBBAl}}}}\",[]]Aj[\"{{{AHO`{AILh}}}CNb}\",[]]Ai[\"{{{AHO`{CNl}}}CNb}\",[]]Bn[\"{{{AHO`{CGbAIJn}}CNb}{{AHO`{CGbAIJn}}}}\",[]]Ai[\"{{{AHO`{IDj}}}CNb}\",[]]Bn[\"{{{AHO`{CGbAOK`}}CNb}{{AHO`{CGbAOK`}}}}\",[]]Ai[\"{{{AHO`{NKn}}}CNb}\",[]]Bn[\"{{{AHO`{CGbBBAn}}CNb}{{AHO`{CGbBBAn}}}}\",[]]Aa[\"{AIOdBGB`}\",[]]Ai[\"{{{AHO`{NMf}}}DC`}\",[]]Aj[\"{{{AHO`{AJAb}}}DC`}\",[]]Aj[\"{{{AHO`{AILd}}}DC`}\",[]]Ai[\"{{{AHO`{NMh}}}DC`}\",[]]Aj[\"{{{AHO`{AJAd}}}DC`}\",[]]Co[\"{{{AHO`{NMf}}B@Mf{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}DC`}\",[]]D`[\"{{{AHO`{AJAb}}B@Mf{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}DC`}\",[]]Co[\"{{{AHO`{NMh}}B@Mh{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}DC`}\",[]]D`[\"{{{AHO`{AJAd}}B@Mh{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}DC`}\",[]]Bn[\"{{{AHO`{CGbBFIf}}CMn}{{AHO`{CGbBFIf}}}}\",[]]Bn[\"{{{AHO`{CGbBFIh}}CMn}{{AHO`{CGbBFIh}}}}\",[]]Cd[\"{{{AHO`{CGbNMf}}}{{KFl{{NKb{{A@d{AHDj}}}}}}}}\",[]]Ce[\"{{{AHO`{CGbAJAb}}}{{KFl{{NKb{{A@d{AHDj}}}}}}}}\",[]]Cd[\"{{{AHO`{CGbNMh}}}{{KFl{{NKd{{A@d{AHDl}}}}}}}}\",[]]Ce[\"{{{AHO`{CGbAJAd}}}{{KFl{{NKd{{A@d{AHDl}}}}}}}}\",[]]Bf[\"{{{AHO`{CGbAILf}}CMnCNbNK`}ICj}\",[]]Be[\"{{{AHO`{CGbCNj}}CMnCNbNK`}ICj}\",[]]Bf[\"{{{AHO`{CGbAILh}}CMnCNbNK`}ICj}\",[]]Be[\"{{{AHO`{CGbCNl}}CMnCNbNK`}ICj}\",[]]Bf[\"{{{AHO`{CGbAILf}}NK`CMnCNb}ICj}\",[]]Be[\"{{{AHO`{CGbCNj}}NK`CMnCNb}ICj}\",[]]Bf[\"{{{AHO`{CGbAILh}}NK`CMnCNb}ICj}\",[]]Be[\"{{{AHO`{CGbCNl}}NK`CMnCNb}ICj}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]De[\"{{{AHO`{CGbBCOl}}c{AHO`{CMl}}}{{AHO`{CGbBCOl}}}{{ICl{D@n}}}}\",[\"\"]]De[\"{{{AHO`{CGbBCOn}}c{AHO`{CMl}}}{{AHO`{CGbBCOn}}}{{ICl{D@n}}}}\",[\"\"]]Ci[\"{{{AHO`{CGbNMf}}{AHO`{{K@j{Oh}}}}}{{KGd{DC`IEl}}}}\",[]]Cj[\"{{{AHO`{CGbAJAb}}{AHO`{{K@j{Oh}}}}}{{KGd{DC`IEl}}}}\",[]]Cj[\"{{{AHO`{CGbNMh}}{AHO`{{K@j{Oh}}}}}{{KGd{DC`BCAb}}}}\",[]]Ck[\"{{{AHO`{CGbAJAd}}{AHO`{{K@j{Oh}}}}}{{KGd{DC`BCAb}}}}\",[]]o[\"{AAjDC`}\",[]]Bn[\"{{{AHO`{CGbBCOl}}KI`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}KIb}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHAlKFl}\",[]]Ca[\"{{{AHO`{CGbAIOd}}}{{KGd{{KFl{KI`}}AIOh}}}}\",[]]Ca[\"{{{AHO`{CGbBGB`}}}{{KGd{{KFl{KI`}}AIOh}}}}\",[]]Ca[\"{{{AHO`{CGbAIOf}}}{{KGd{{KFl{KIb}}B@Ll}}}}\",[]]Ak[\"{{{AHO`{AIKn}}}AJ@f}\",[]]Ak[\"{{{AHO`{ABA`}}}AJ@f}\",[]]Bg[\"{{{AHO`{DA`}}}{{KGd{AJ@fAIKd}}}}\",[]]Ak[\"{{{AHO`{ABAb}}}AJ@f}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]B`[\"{{KI`c}KI`{{D@b{KI`}}}}\",[\"\"]]B`[\"{{KFnc}KFn{{D@b{KFn}}}}\",[\"\"]]B`[\"{{KIbc}KIb{{D@b{KIb}}}}\",[\"\"]]B`[\"{{KG`c}KG`{{D@b{KG`}}}}\",[\"\"]]20Ae[\"{{AB@dCNb}AG`}\",[]]Bn[\"{{{AHO`{CGbBCOl}}AFn}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}AFn}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{IHjAB@d}\",[]]B`[\"{{}{{AHO`{{K@j{BG@n}}}}}}\",[]]An[\"{{AHO`{ONf{CGbOh}}}ICj}\",[]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000An[\"{{}{{KFl{{A@b{NMd}}}}}}\",[]]Bn[\"{{{AHO`{CGbBBMh}}DC`}{{AHO`{CGbBBMh}}}}\",[]]Bn[\"{{{AHO`{CGbBBMj}}DC`}{{AHO`{CGbBBMj}}}}\",[]]Aj[\"{{{AHO`{AHDj}}}CNb}\",[]]Aj[\"{{{AHO`{AHDl}}}CNb}\",[]]A`[\"{ABC`KFl}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]10Bf[\"{{{AHO`{CMl}}}{{KGh{CNbBBB`}}}}\",[]]Bn[\"{{{AHO`{AB@h}}{A@d{BBL`}}}{{KGf{ICj}}}}\",[]]Ai[\"{AIL`{{KGh{ICj}}}}\",[]]Cj[\"{{{AHO`{NMb}}BBLbBBBj{AHO`{AAj}}}{{KC`{AJ@fAB@f}}}}\",[]]Cb[\"{{{AHO`{CGbAILd}}KG`AINl}{{KGd{AINlBFKf}}}}\",[]]Am[\"{{{AHO`{AILb}}CNb}ICj}\",[]]B`[\"{{{AHO`{CGbAILd}}CNb}ICj}\",[]]A`[\"{BC@jDC`}\",[]]B`[\"{{{AHO`{CGbIHj}}BC@l}ICj}\",[]]Aa[\"{AONfAHD`}\",[]]Bn[\"{{{AHO`{NMd}}AHFl}{{KGf{{A@d{BBL`}}}}}}\",[]]Bo[\"{{{AHO`{BACb}}AHFl}{{KGf{{A@d{BBL`}}}}}}\",[]]Bo[\"{{{AHO`{AOMl}}AHFl}{{KGf{{A@d{BBL`}}}}}}\",[]]Ci[\"{{{AHO`{CGbNMf}}{AHO`{CGb{ABh{Oh}}}}}{{KFl{D@d}}}}\",[]]Cj[\"{{{AHO`{CGbAJAb}}{AHO`{CGb{ABh{Oh}}}}}{{KFl{D@d}}}}\",[]]Ci[\"{{{AHO`{CGbNMh}}{AHO`{CGb{ABh{Oh}}}}}{{KFl{D@f}}}}\",[]]Cj[\"{{{AHO`{CGbAJAd}}{AHO`{CGb{ABh{Oh}}}}}{{KFl{D@f}}}}\",[]]Cb[\"{{{AHO`{ABD`}}IChAJ@lAB@d}{{KGd{ICjBCOf}}}}\",[]]Cb[\"{{{AHO`{BANd}}IChAJ@lAB@d}{{KGd{ICjBCOf}}}}\",[]]Cb[\"{{{AHO`{B@Ol}}IChAJ@lAB@d}{{KGd{ICjBCOf}}}}\",[]]Cb[\"{{{AHO`{ABDb}}IChAJ@lAB@d}{{KGd{ICjBCOh}}}}\",[]]Cb[\"{{{AHO`{BANf}}IChAJ@lAB@d}{{KGd{ICjBCOh}}}}\",[]]Cb[\"{{{AHO`{B@On}}IChAJ@lAB@d}{{KGd{ICjBCOh}}}}\",[]]Al[\"{{}{{KGd{B@KfBGI`}}}}\",[]]Bn[\"{{{AHO`{AILb}}KFn}{{KFl{{A@d{AILf}}}}}}\",[]]Bo[\"{{{AHO`{AILd}}KG`}{{KFl{{AHO`{AILh}}}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Aj[\"{{{AHO`{AILb}}}ICj}\",[]]Am[\"{{{AHO`{CGbAILd}}}ICj}\",[]]Bf[\"{{{AHO`{AIJb}}}{{KGd{AAjIEn}}}}\",[]]Bf[\"{{{AHO`{AIJb}}}{{KGd{A@hIEn}}}}\",[]]Bg[\"{{{AHO`{AIJb}}}{{KGd{AAOfIEn}}}}\",[]]Bg[\"{{{AHO`{AIJb}}}{{KGd{AHAlIEn}}}}\",[]]Bf[\"{{{AHO`{AIJb}}}{{KGd{AAlIEn}}}}\",[]]Bf[\"{{{AHO`{AIJb}}}{{KGd{AAnIEn}}}}\",[]]Ak[\"{{{AHO`{BCAd}}}B@Oj}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{KFl{AOL`}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{KFl{AOLb}}}{{AHO`{CGbBCOn}}}}\",[]]<;Aj[\"{{{AHO`{AB@h}}}KCb}\",[]]Aj[\"{{{AHO`{AB@j}}}KCb}\",[]]Bo[\"{{{AHO`{AILb}}cAINj}ABBh{{D@b{AHCb}}}}\",[\"\"]]Co[\"{{{AHO`{CGbAILd}}AHCdAINlNK`}{{KGd{{KC`{KG`DC`}}AHEb}}}}\",[]]Bn[\"{{{AHO`{CGbBCOl}}CN`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}CN`}{{AHO`{CGbBCOn}}}}\",[]]Df[\"{{{AHO`{CGbAIOd}}{AHO`{CGb{K@j{ICn}}}}}{{KGd{{KFl{KCb}}AHFb}}}}\",[]]Ai[\"{AIL`{{KGh{ICj}}}}\",[]]Bg[\"{{{AHO`{BCAd}}AJAj}{{KGf{KCb}}}}\",[]]A`[\"{AHAlCNb}\",[]]10Aj[\"{{{AHO`{AAj}}}BCOj}\",[]]Cf[\"{{{AHO`{CGbBABd}}{A@b{BCOl}}}{{AHO`{CGbBABd}}}}\",[]]Cf[\"{{{AHO`{CGbB@Kh}}{A@b{BCOl}}}{{AHO`{CGbB@Kh}}}}\",[]]Cf[\"{{{AHO`{CGbBABh}}{A@b{BCOn}}}{{AHO`{CGbBABh}}}}\",[]]Cf[\"{{{AHO`{CGbB@Kl}}{A@b{BCOn}}}{{AHO`{CGbB@Kl}}}}\",[]]Aa[\"{BABdBH@l}\",[]]Aa[\"{BABhBH@n}\",[]]Bj[\"{{{ABh{BBLb}}BBBj}{{KGd{BABdIFb}}}}\",[]]Bj[\"{{{ABh{BBLb}}BBBj}{{KGd{BABhIFd}}}}\",[]]Co[\"{{{AHO`{CGbAJ@`}}{AHO`{CGb{K@j{ICn}}}}}{{KGd{ICjAJAl}}}}\",[]]A`[\"{AHElCNb}\",[]]A`[\"{AHEnCNb}\",[]]A`[\"{BFBjCNb}\",[]]A`[\"{BFBlCNb}\",[]]Bc[\"{{{AHO`{AILb}}}{{KFl{KCb}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]10Aj[\"{{{AHO`{AHBb}}}KCb}\",[]]Be[\"{{{AHO`{AB@h}}{KFl{BABd}}}ICj}\",[]]Bo[\"{{{AHO`{CGbAB@j}}{KFl{{A@b{BABh}}}}}ICj}\",[]]Bd[\"{{{AHO`{BG@n}}}{{KFl{BBBh}}}}\",[]]Aj[\"{{{AHO`{AB@h}}}ICj}\",[]]Dj[\"{{{AHO`{CGbAJ@`}}{AHO`{CGb{AHO`{CGb{K@j{ICn}}}}}}}{{KGd{KCbAJAl}}}}\",[]]o[\"{{}AHO`}\",[]]n[\"{{}NIl}\",[]]0000Bn[\"{{{AHO`{CGbBCOl}}KCb}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}KCb}{{AHO`{CGbBCOn}}}}\",[]]o[\"{CNbKI`}\",[]]o[\"{CNbKIb}\",[]]A`[\"{BAOfCNb}\",[]]Bf[\"{{{AHO`{BGJ`}}{AHO`{BGJ`}}}DC`}\",[]]Bo[\"{{{AHO`{CGbBBMh}}AB@d}{{AHO`{CGbBBMh}}}}\",[]]Bo[\"{{{AHO`{CGbBBMj}}AB@d}{{AHO`{CGbBBMj}}}}\",[]]Ae[\"{{KCbCNb}BANd}\",[]]Ae[\"{{KCbCNb}BANf}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]7Bg[\"{{{AHO`{AILd}}KG`}{{KFl{AINl}}}}\",[]]An[\"{{{AHO`{AILb}}ICn}BAAn}\",[]]Bm[\"{{{AHO`{CGbBEJb}}{ABh{{ABh{Oh}}}}}ICj}\",[]]Bm[\"{{{AHO`{CGbBEJf}}{ABh{{ABh{Oh}}}}}ICj}\",[]]Bm[\"{{{AHO`{CGbBEJd}}{ABh{{ABh{Oh}}}}}ICj}\",[]]Bm[\"{{{AHO`{CGbBEJh}}{ABh{{ABh{Oh}}}}}ICj}\",[]]Am[\"{{{AHO`{AILb}}KI`}ICj}\",[]]B`[\"{{{AHO`{CGbAILd}}KIb}ICj}\",[]]Ce[\"{{{AHO`{CGbBBMh}}{ABh{CN`}}}{{AHO`{CGbBBMh}}}}\",[]]Ce[\"{{{AHO`{CGbBBMj}}{ABh{CN`}}}{{AHO`{CGbBBMj}}}}\",[]]Fg[\"{{{AHO`{BHEb}}{AHO`{BBLb}}{AHO`{{K@j{BBLb}}}}{AHO`{AJ@d}}{AHO`{{K@j{Oh}}}}ABEb}{{KGd{BFK`IEh}}}}\",[]]Af[\"{{BCNjNK`}BCNj}\",[]]Af[\"{{BCNlNK`}BCNl}\",[]]A`[\"{BAOfCNb}\",[]]Al[\"{BACd{{KGd{DAlIF`}}}}\",[]]Al[\"{BACd{{KGd{A@jIF`}}}}\",[]]Ai[\"{BACd{{KGj{DAn}}}}\",[]]Ai[\"{BACd{{KGj{A@l}}}}\",[]]o[\"{NIlNIl}\",[]]Bo[\"{{{AHO`{CGbBFIf}}AB@d}{{AHO`{CGbBFIf}}}}\",[]]Bo[\"{{{AHO`{CGbBFIh}}AB@d}{{AHO`{CGbBFIh}}}}\",[]]Bb[\"{{{AHO`{NMf}}}{{KFl{DC`}}}}\",[]]Bc[\"{{{AHO`{AJAb}}}{{KFl{DC`}}}}\",[]]Bb[\"{{{AHO`{NMh}}}{{KFl{DC`}}}}\",[]]Bc[\"{{{AHO`{AJAd}}}{{KFl{DC`}}}}\",[]]Bg[\"{{{AHO`{AILb}}}{{KGd{ICjBCLn}}}}\",[]]Ba[\"{{{AHO`{BFCf}}KFnAHCb}DC`}\",[]]Ba[\"{{{AHO`{BFCh}}KG`AHCd}DC`}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{KFl{AB@d}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{KFl{AB@d}}}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHElCNb}\",[]]A`[\"{AHEnCNb}\",[]]Bo[\"{{{AHO`{CGbAILf}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bn[\"{{{AHO`{CGbCNj}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bn[\"{{{AHO`{CGbIDh}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bn[\"{{{AHO`{CGbNKl}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bo[\"{{{AHO`{CGbAILh}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bn[\"{{{AHO`{CGbCNl}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bn[\"{{{AHO`{CGbIDj}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bn[\"{{{AHO`{CGbNKn}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]A`[\"{BAOfCNb}\",[]]Al[\"{{{AHO`{NNf}}A@l}CNb}\",[]]Bi[\"{{{AHO`{CGbAIOn}}CNb}{{KGh{ICj}}}}\",[]]<;A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]10Cf[\"{{{AHO`{CGbBCOl}}{KFl{BFGh}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{KFl{BFGj}}}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHElCNb}\",[]]A`[\"{AHEnCNb}\",[]]Bi[\"{{{AHO`{CGbAIJb}}}{{KGd{AAjIEn}}}}\",[]]Bi[\"{{{AHO`{CGbAIJb}}}{{KGd{A@hIEn}}}}\",[]]Bj[\"{{{AHO`{CGbAIJb}}}{{KGd{AAOfIEn}}}}\",[]]Bj[\"{{{AHO`{CGbAIJb}}}{{KGd{AHAlIEn}}}}\",[]]Bi[\"{{{AHO`{CGbAIJb}}}{{KGd{AAlIEn}}}}\",[]]Bi[\"{{{AHO`{CGbAIJb}}}{{KGd{AAnIEn}}}}\",[]]Cd[\"{{{A@b{AIL`}}CNbDC`}{{KGh{{KC`{BC@lBC@l}}}}}}\",[]]Bn[\"{{{AHO`{CGbBABd}}CNb}{{AHO`{CGbBABd}}}}\",[]]Bn[\"{{{AHO`{CGbBABh}}CNb}{{AHO`{CGbBABh}}}}\",[]]Aj[\"{{{AHO`{AILb}}}DC`}\",[]]Ak[\"{{{AHO`{BBL`}}}B@Oj}\",[]]0Ci[\"{{{AHO`{CGbBBMh}}CMn}{{KGd{{AHO`{CGbBBMh}}AOJd}}}}\",[]]Ci[\"{{{AHO`{CGbBBMj}}CMn}{{KGd{{AHO`{CGbBBMj}}AOJf}}}}\",[]]A`[\"{AHAlCMn}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{KFl{BFIf}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{KFl{BFIh}}}{{AHO`{CGbBCOn}}}}\",[]]Bg[\"{{{AHO`{CGbAILd}}}{{KFl{BAOd}}}}\",[]]Cf[\"{{{AHO`{CGbBABd}}{KFl{BABl}}}{{AHO`{CGbBABd}}}}\",[]]Cf[\"{{{AHO`{CGbBABh}}{KFl{BABl}}}{{AHO`{CGbBABh}}}}\",[]]Cf[\"{{{AHO`{CGbBABd}}{KFl{BABn}}}{{AHO`{CGbBABd}}}}\",[]]Cf[\"{{{AHO`{CGbBABh}}{KFl{BABn}}}{{AHO`{CGbBABh}}}}\",[]]Cd[\"{{}{{KGd{{KC`{{ABh{BBLb}}BBBj}}{A@d{IEd}}}}}}\",[]]Bn[\"{{{AHO`{CGbBFGh}}KI`}{{AHO`{CGbBFGh}}}}\",[]]Bn[\"{{{AHO`{CGbBFGj}}KIb}{{AHO`{CGbBFGj}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Bo[\"{{{AHO`{CGbBABd}}AB@d}{{AHO`{CGbBABd}}}}\",[]]Bo[\"{{{AHO`{CGbBABh}}AB@d}{{AHO`{CGbBABh}}}}\",[]]Ci[\"{{{AHO`{DA`}}{KFl{AB@d}}}{{KGd{{KFl{AB@d}}AIKd}}}}\",[]]Bj[\"{{{AHO`{BCAd}}AJAjKCb}{{KGf{ICj}}}}\",[]]054Bm[\"{{{AHO`{NMf}}}{{KGd{{KFl{BGAn}}IEl}}}}\",[]]Bn[\"{{{AHO`{AJAb}}}{{KGd{{KFl{BGAn}}IEl}}}}\",[]]Bn[\"{{{AHO`{NMh}}}{{KGd{{KFl{BGAn}}BCAb}}}}\",[]]Bo[\"{{{AHO`{AJAd}}}{{KGd{{KFl{BGAn}}BCAb}}}}\",[]]Aj[\"{{{AHO`{AHDj}}}CNb}\",[]]Aj[\"{{{AHO`{AHDl}}}CNb}\",[]]Bl[\"{{{AHO`{AB@h}}{KFl{{A@b{BFCf}}}}}ICj}\",[]]Cc[\"{{{AHO`{CGbAILd}}{KFl{{AHO`{BFCh}}}}NK`}ICj}\",[]]Aj[\"{{{AHO`{AB@j}}}CNb}\",[]]Bn[\"{{{AHO`{CGbBBAl}}AFn}{{AHO`{CGbBBAl}}}}\",[]]Bn[\"{{{AHO`{CGbBBAn}}AFn}{{AHO`{CGbBBAn}}}}\",[]]Ak[\"{{{AHO`{AHFj}}}B@Oj}\",[]]0Ak[\"{{{AHO`{AILb}}}BGA`}\",[]]Cb[\"{{{AHO`{AILd}}KG`}{{KGd{{KFl{AJ@f}}AIKj}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Bn[\"{{{AHO`{CGbBCOl}}KI`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}KIb}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHAlKFl}\",[]]Bo[\"{{{AHO`{AHAl}}{AHO`{CMl}}}{{KGf{BCOj}}}}\",[]]Em[\"{{{AHO`{AILb}}{AHO`{CGb{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjBHLn}}}}\",[]]El[\"{{{AHO`{NMf}}{AHO`{CGb{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjBHLn}}}}\",[]]Em[\"{{{AHO`{AJAb}}{AHO`{CGb{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjBHLn}}}}\",[]]El[\"{{{AHO`{NMh}}{AHO`{CGb{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjBHM`}}}}\",[]]Em[\"{{{AHO`{AJAd}}{AHO`{CGb{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjBHM`}}}}\",[]]Am[\"{{{AHO`{CGbAHB`}}}ICj}\",[]]0Am[\"{{{AHO`{AILd}}A@l}CNb}\",[]]Ak[\"{{{AHO`{AILb}}}BGIb}\",[]]Bg[\"{{{AHO`{DA`}}}{{KGd{BEFbAIKd}}}}\",[]]>=E`[\"{{{AHO`{BHEb}}{AHO`{{K@j{Oh}}}}{AHO`{BBLb}}{AHO`{BGOn}}}{{KGd{BHGhIEh}}}}\",[]]0Bg[\"{{{A@b{BBCb}}}{{KGd{B@KhBGIn}}}}\",[]]Bg[\"{{{A@b{BBCb}}}{{KGd{B@KlBGIn}}}}\",[]]Bn[\"{{{AHO`{CGbBFGh}}KI`}{{AHO`{CGbBFGh}}}}\",[]]Bn[\"{{{AHO`{CGbBFGj}}KIb}{{AHO`{CGbBFGj}}}}\",[]]o[\"{NIlNIl}\",[]]0000Aj[\"{{{AHO`{AILd}}}DC`}\",[]]0Bd[\"{{{AHO`{CGbAILf}}CNbAB@d}ICj}\",[]]Bc[\"{{{AHO`{CGbCNj}}CNbAB@d}ICj}\",[]]Bd[\"{{{AHO`{CGbAILh}}CNbAB@d}ICj}\",[]]Bc[\"{{{AHO`{CGbCNl}}CNbAB@d}ICj}\",[]]Ci[\"{{{AHO`{DA`}}{KFl{AB@d}}}{{KGd{{KFl{AB@d}}AIKd}}}}\",[]]Cd[\"{{{AHO`{CGbAAj}}{AHO`{AIJb}}}{{KGd{ICjIEn}}}}\",[]]Cd[\"{{{AHO`{CGbA@h}}{AHO`{AIJb}}}{{KGd{ICjIEn}}}}\",[]]Ce[\"{{{AHO`{CGbAAOf}}{AHO`{AIJb}}}{{KGd{ICjIEn}}}}\",[]]Ce[\"{{{AHO`{CGbAHAl}}{AHO`{AIJb}}}{{KGd{ICjIEn}}}}\",[]]Cd[\"{{{AHO`{CGbAAl}}{AHO`{AIJb}}}{{KGd{ICjIEn}}}}\",[]]Cd[\"{{{AHO`{CGbAAn}}{AHO`{AIJb}}}{{KGd{ICjIEn}}}}\",[]]Bo[\"{{{AHO`{CGbBABd}}BH@l}{{AHO`{CGbBABd}}}}\",[]]Bo[\"{{{AHO`{CGbBABh}}BH@n}{{AHO`{CGbBABh}}}}\",[]]Aj[\"{{{AHO`{BBMh}}}CNb}\",[]]Aj[\"{{{AHO`{BBMj}}}CNb}\",[]]Cd[\"{{{AHO`{CGbB@Kh}}{A@b{A`}}}{{AHO`{CGbB@Kh}}}}\",[]]Cd[\"{{{AHO`{CGbB@Kl}}{A@b{A`}}}{{AHO`{CGbB@Kl}}}}\",[]]Cl[\"{{BBMh{KFl{BABd}}{A@d{BBL`}}{A@b{NMd}}}{{KGf{AB@h}}}}\",[]]Aj[\"{{{AHO`{ABA`}}}DC`}\",[]]Aj[\"{{{AHO`{ABAb}}}DC`}\",[]]Ca[\"{{{AHO`{CGbAILd}}NK`AIMn}{{KGd{ICjBHDj}}}}\",[]]Bd[\"{{{AHO`{BHEb}}}{{ABh{BCOb}}}}\",[]]Bj[\"{{{AHO`{AILb}}AJ@f}{{KGd{ICjIFh}}}}\",[]]Bm[\"{{{AHO`{CGbAILd}}AJ@f}{{KGd{ICjIFh}}}}\",[]]Bn[\"{{{AHO`{CGbBCOl}}KCb}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}KCb}{{AHO`{CGbBCOn}}}}\",[]]Bm[\"{{}{{KGd{{KC`{BBLbBFJb}}{A@d{IEd}}}}}}\",[]]Ai[\"{AIL`{{KGh{ICj}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]An[\"{{{AHO`{AB@h}}B@Kh}ICj}\",[]]Dc[\"{{{AHO`{CGbAILd}}NK`KG`{KFl{AB@d}}}{{KGd{{KFl{AB@d}}AIKj}}}}\",[]]o[\"{{}AHO`}\",[]]Aj[\"{{{AHO`{AILb}}}KCb}\",[]]Bn[\"{{{AHO`{CGbBABd}}CNb}{{AHO`{CGbBABd}}}}\",[]]Bn[\"{{{AHO`{CGbBABh}}CNb}{{AHO`{CGbBABh}}}}\",[]]Bn[\"{{{AHO`{CGbBCOl}}KI`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}KIb}{{AHO`{CGbBCOn}}}}\",[]]:Bn[\"{{{AHO`{CGbBCOl}}DC`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}DC`}{{AHO`{CGbBCOn}}}}\",[]]Bc[\"{{{AHO`{CGbAILd}}A@lKIb}ICj}\",[]]A`[\"{AHElCNb}\",[]]A`[\"{AHEnCNb}\",[]]Ak[\"{{}{{KGd{B@KhIFb}}}}\",[]]Ak[\"{{}{{KGd{B@KlIFd}}}}\",[]]6587Cg[\"{{{AHO`{CGbAAj}}{AHO`{CGbAIJb}}}{{KGd{ICjIEn}}}}\",[]]Cg[\"{{{AHO`{CGbA@h}}{AHO`{CGbAIJb}}}{{KGd{ICjIEn}}}}\",[]]Ch[\"{{{AHO`{CGbAAOf}}{AHO`{CGbAIJb}}}{{KGd{ICjIEn}}}}\",[]]Ch[\"{{{AHO`{CGbAHAl}}{AHO`{CGbAIJb}}}{{KGd{ICjIEn}}}}\",[]]Cg[\"{{{AHO`{CGbAAl}}{AHO`{CGbAIJb}}}{{KGd{ICjIEn}}}}\",[]]Cg[\"{{{AHO`{CGbAAn}}{AHO`{CGbAIJb}}}{{KGd{ICjIEn}}}}\",[]]n[\"{{}ICj}\",[]]Ce[\"{{{AHO`{CGbBCOl}}{KFl{KCb}}}{{AHO`{CGbBCOl}}}}\",[]]Ce[\"{{{AHO`{CGbBCOn}}{KFl{KCb}}}{{AHO`{CGbBCOn}}}}\",[]]Bn[\"{{{AHO`{AILb}}}{{KGd{{ABh{AJ@f}}IFh}}}}\",[]]Cd[\"{{{AHO`{CGbAILd}}NK`}{{KGd{{ABh{AJ@f}}IFh}}}}\",[]]Am[\"{{{AHO`{CGbAILf}}}ICj}\",[]]Al[\"{{{AHO`{CGbCNj}}}ICj}\",[]]Al[\"{{{AHO`{CGbIDh}}}ICj}\",[]]Am[\"{{{AHO`{CGbAILh}}}ICj}\",[]]Al[\"{{{AHO`{CGbCNl}}}ICj}\",[]]Al[\"{{{AHO`{CGbIDj}}}ICj}\",[]]Ai[\"{AIL`{{KGh{ICj}}}}\",[]]Bj[\"{{{AHO`{AILb}}AJ@f}{{KGd{ICjIFh}}}}\",[]]Bm[\"{{{AHO`{CGbAILd}}AJ@f}{{KGd{ICjIFh}}}}\",[]]D`[\"{{{AHO`{CGbAILd}}KG`{KFl{AB@d}}}{{KGd{{KFl{AB@d}}AIKj}}}}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{A@b{BFBf}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{A@b{BFBh}}}{{AHO`{CGbBCOn}}}}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{KFl{AB@d}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{KFl{AB@d}}}{{AHO`{CGbBCOn}}}}\",[]]Ce[\"{{{AHO`{CGbBCOl}}{KFl{CNb}}}{{AHO`{CGbBCOl}}}}\",[]]Ce[\"{{{AHO`{CGbBCOn}}{KFl{CNb}}}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{BAOjKFl}\",[]]A`[\"{BAOlKFl}\",[]];Bn[\"{{{AHO`{CGbBCOl}}DC`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}DC`}{{AHO`{CGbBCOn}}}}\",[]]Am[\"{{{AHO`{AILb}}KI`}ICj}\",[]]o[\"{NIlNIl}\",[]]Bn[\"{{{AHO`{CGbBCOl}}CN`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}CN`}{{AHO`{CGbBCOn}}}}\",[]]310;:54Bn[\"{{{AHO`{AILb}}}{{KGd{{ABh{AJ@f}}IFh}}}}\",[]]Bn[\"{{{AHO`{AILd}}}{{KGd{{ABh{AJ@f}}IFh}}}}\",[]]10Bm[\"{{{AHO`{CGbBCOl}}Oh}{{AHO`{CGbBCOl}}}}\",[]]Bm[\"{{{AHO`{CGbBCOn}}Oh}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{BGAnKFl}\",[]]") \ No newline at end of file +rd_("Bf[\"{{{AHO`{AOIn}}{AHO`{AOIn}}}DC`}\",[]]Bf[\"{{{AHO`{BFDd}}{AHO`{BFDd}}}DC`}\",[]]Bd[\"{{{AHO`{KFd}}{AHO`{KFd}}}DC`}\",[]]Bd[\"{{{AHO`{DA`}}{AHO`{DA`}}}DC`}\",[]]Bf[\"{{{AHO`{BCAf}}{AHO`{BCAf}}}DC`}\",[]]Bf[\"{{{AHO`{BC@h}}{AHO`{BC@h}}}DC`}\",[]]Bf[\"{{{AHO`{AHFb}}{AHO`{AHFb}}}DC`}\",[]]Bf[\"{{{AHO`{AIOh}}{AHO`{AIOh}}}DC`}\",[]]Bf[\"{{{AHO`{BC@f}}{AHO`{BC@f}}}DC`}\",[]]Bf[\"{{{AHO`{AJAl}}{AHO`{AJAl}}}DC`}\",[]]Bf[\"{{{AHO`{BAC`}}{AHO`{BAC`}}}DC`}\",[]]Bd[\"{{{AHO`{KI`}}{AHO`{KI`}}}DC`}\",[]]Bf[\"{{{AHO`{BGIj}}{AHO`{BGIj}}}DC`}\",[]]Bd[\"{{{AHO`{ID`}}{AHO`{ID`}}}DC`}\",[]]Bd[\"{{{AHO`{KFn}}{AHO`{KFn}}}DC`}\",[]]Bf[\"{{{AHO`{AINj}}{AHO`{AINj}}}DC`}\",[]]Bf[\"{{{AHO`{AHEh}}{AHO`{AHEh}}}DC`}\",[]]Bf[\"{{{AHO`{BFKd}}{AHO`{BFKd}}}DC`}\",[]]Bf[\"{{{AHO`{AIKd}}{AHO`{AIKd}}}DC`}\",[]]Bf[\"{{{AHO`{ABDn}}{AHO`{ABDn}}}DC`}\",[]]Bf[\"{{{AHO`{AILn}}{AHO`{AILn}}}DC`}\",[]]Bf[\"{{{AHO`{AHEl}}{AHO`{AHEl}}}DC`}\",[]]Bf[\"{{{AHO`{B@Ld}}{AHO`{B@Ld}}}DC`}\",[]]Bf[\"{{{AHO`{BCLn}}{AHO`{BCLn}}}DC`}\",[]]Bf[\"{{{AHO`{AHE`}}{AHO`{AHE`}}}DC`}\",[]]Bf[\"{{{AHO`{BBLd}}{AHO`{BBLd}}}DC`}\",[]]Bf[\"{{{AHO`{AOL`}}{AHO`{AOL`}}}DC`}\",[]]Bf[\"{{{AHO`{AOJd}}{AHO`{AOJd}}}DC`}\",[]]Bf[\"{{{AHO`{AHCf}}{AHO`{AHCf}}}DC`}\",[]]Bf[\"{{{AHO`{BCLh}}{AHO`{BCLh}}}DC`}\",[]]Bf[\"{{{AHO`{BEFf}}{AHO`{BEFf}}}DC`}\",[]]Bf[\"{{{AHO`{B@M`}}{AHO`{B@M`}}}DC`}\",[]]Bf[\"{{{AHO`{B@Mf}}{AHO`{B@Mf}}}DC`}\",[]]Bf[\"{{{AHO`{B@N`}}{AHO`{B@N`}}}DC`}\",[]]Bf[\"{{{AHO`{BFKn}}{AHO`{BFKn}}}DC`}\",[]]Bd[\"{{{AHO`{DAl}}{AHO`{DAl}}}DC`}\",[]]Bd[\"{{{AHO`{A@j}}{AHO`{A@j}}}DC`}\",[]]Bf[\"{{{AHO`{ABCd}}{AHO`{ABCd}}}DC`}\",[]]Bf[\"{{{AHO`{AHCb}}{AHO`{AHCb}}}DC`}\",[]]Bf[\"{{{AHO`{BHLn}}{AHO`{BHLn}}}DC`}\",[]]Bd[\"{{{AHO`{IFb}}{AHO`{IFb}}}DC`}\",[]]Bd[\"{{{AHO`{KIb}}{AHO`{KIb}}}DC`}\",[]]Bf[\"{{{AHO`{BGIl}}{AHO`{BGIl}}}DC`}\",[]]Bd[\"{{{AHO`{IDb}}{AHO`{IDb}}}DC`}\",[]]Bf[\"{{{AHO`{BFDf}}{AHO`{BFDf}}}DC`}\",[]]Bd[\"{{{AHO`{KG`}}{AHO`{KG`}}}DC`}\",[]]Bf[\"{{{AHO`{AINl}}{AHO`{AINl}}}DC`}\",[]]Bf[\"{{{AHO`{AHEj}}{AHO`{AHEj}}}DC`}\",[]]Bf[\"{{{AHO`{BFDb}}{AHO`{BFDb}}}DC`}\",[]]Bf[\"{{{AHO`{BFKf}}{AHO`{BFKf}}}DC`}\",[]]Bf[\"{{{AHO`{AIKj}}{AHO`{AIKj}}}DC`}\",[]]Bf[\"{{{AHO`{ABE`}}{AHO`{ABE`}}}DC`}\",[]]Bf[\"{{{AHO`{AIM`}}{AHO`{AIM`}}}DC`}\",[]]Bf[\"{{{AHO`{AHEn}}{AHO`{AHEn}}}DC`}\",[]]Bf[\"{{{AHO`{AHFd}}{AHO`{AHFd}}}DC`}\",[]]Bf[\"{{{AHO`{BBBl}}{AHO`{BBBl}}}DC`}\",[]]Bf[\"{{{AHO`{AJAn}}{AHO`{AJAn}}}DC`}\",[]]Bf[\"{{{AHO`{AOKn}}{AHO`{AOKn}}}DC`}\",[]]Bf[\"{{{AHO`{AONd}}{AHO`{AONd}}}DC`}\",[]]Bf[\"{{{AHO`{BC@j}}{AHO`{BC@j}}}DC`}\",[]]Bf[\"{{{AHO`{B@Ll}}{AHO`{B@Ll}}}DC`}\",[]]Bf[\"{{{AHO`{BCM`}}{AHO`{BCM`}}}DC`}\",[]]Bf[\"{{{AHO`{AHEb}}{AHO`{AHEb}}}DC`}\",[]]Bf[\"{{{AHO`{BBLf}}{AHO`{BBLf}}}DC`}\",[]]Bf[\"{{{AHO`{AOLb}}{AHO`{AOLb}}}DC`}\",[]]Bf[\"{{{AHO`{AOJf}}{AHO`{AOJf}}}DC`}\",[]]Bf[\"{{{AHO`{AHCh}}{AHO`{AHCh}}}DC`}\",[]]Bf[\"{{{AHO`{BBNd}}{AHO`{BBNd}}}DC`}\",[]]Bf[\"{{{AHO`{AIMl}}{AHO`{AIMl}}}DC`}\",[]]Bf[\"{{{AHO`{AJ@h}}{AHO`{AJ@h}}}DC`}\",[]]Bf[\"{{{AHO`{B@Mj}}{AHO`{B@Mj}}}DC`}\",[]]Bf[\"{{{AHO`{BCLl}}{AHO`{BCLl}}}DC`}\",[]]Bf[\"{{{AHO`{BEFj}}{AHO`{BEFj}}}DC`}\",[]]Bf[\"{{{AHO`{BEGb}}{AHO`{BEGb}}}DC`}\",[]]Bf[\"{{{AHO`{B@Mb}}{AHO`{B@Mb}}}DC`}\",[]]Bf[\"{{{AHO`{ABAh}}{AHO`{ABAh}}}DC`}\",[]]Bf[\"{{{AHO`{BFCl}}{AHO`{BFCl}}}DC`}\",[]]Bf[\"{{{AHO`{B@Mh}}{AHO`{B@Mh}}}DC`}\",[]]Bf[\"{{{AHO`{B@Nb}}{AHO`{B@Nb}}}DC`}\",[]]Bf[\"{{{AHO`{BCAb}}{AHO`{BCAb}}}DC`}\",[]]Bf[\"{{{AHO`{BFL`}}{AHO`{BFL`}}}DC`}\",[]]Bd[\"{{{AHO`{DAn}}{AHO`{DAn}}}DC`}\",[]]Bd[\"{{{AHO`{A@l}}{AHO`{A@l}}}DC`}\",[]]Bf[\"{{{AHO`{ABCf}}{AHO`{ABCf}}}DC`}\",[]]Bf[\"{{{AHO`{AHCd}}{AHO`{AHCd}}}DC`}\",[]]Bf[\"{{{AHO`{BBCh}}{AHO`{BBCh}}}DC`}\",[]]Bf[\"{{{AHO`{BGAn}}{AHO`{BGAn}}}DC`}\",[]]Bd[\"{{{AHO`{IFf}}{AHO`{IFf}}}DC`}\",[]]Bf[\"{{{AHO`{BHM`}}{AHO`{BHM`}}}DC`}\",[]]Bd[\"{{{AHO`{IFd}}{AHO`{IFd}}}DC`}\",[]]Bf[\"{{{AHO`{B@Nd}}{AHO`{B@Nd}}}DC`}\",[]]Ai[\"{{{AHO`{DA`}}}KFn}\",[]]Aj[\"{{{AHO`{BCAf}}}KFn}\",[]]Ak[\"{{{AHO`{AIOd}}}ABCd}\",[]]Ak[\"{{{AHO`{BGB`}}}ABCd}\",[]]Ak[\"{{{AHO`{AJ@`}}}ABCd}\",[]]A`[\"{AOKfKFn}\",[]]A`[\"{AH@`KFn}\",[]]A`[\"{AHBfKFn}\",[]]A`[\"{BAAbKFn}\",[]]A`[\"{BA@`KFn}\",[]]A`[\"{AOKhKG`}\",[]]A`[\"{AH@bKG`}\",[]]A`[\"{AHBhKG`}\",[]]A`[\"{BAAdKG`}\",[]]A`[\"{BA@bKG`}\",[]]Aa[\"{ABBlABCf}\",[]]Aa[\"{ABEdABCf}\",[]]Aa[\"{AB@lABCf}\",[]]A`[\"{NMjABCf}\",[]]A`[\"{AOMfNMb}\",[]]A`[\"{D@hABAh}\",[]]Ac[\"{{ABDnABDn}}\",[]]Ac[\"{{AILnAILn}}\",[]]Ac[\"{{BCMfAHEl}}\",[]]Bc[\"{{ABE`ABE`}c{}}\",[\"Add::Output\"]]Bc[\"{{AIM`AIM`}c{}}\",[\"Add::Output\"]]Bc[\"{{BCMhAHEn}c{}}\",[\"Add::Output\"]]Be[\"{{{AHO`{KI`}}{AHO`{KI`}}}ABBj}\",[]]Be[\"{{{AHO`{KFn}}{AHO`{KFn}}}ABBj}\",[]]Bg[\"{{{AHO`{AOL`}}{AHO`{AOL`}}}ABBj}\",[]]Bg[\"{{{AHO`{B@Mf}}{AHO`{B@Mf}}}ABBj}\",[]]Be[\"{{{AHO`{DAl}}{AHO`{DAl}}}ABBj}\",[]]Be[\"{{{AHO`{A@j}}{AHO`{A@j}}}ABBj}\",[]]Bg[\"{{{AHO`{ABCd}}{AHO`{ABCd}}}ABBj}\",[]]Be[\"{{{AHO`{KIb}}{AHO`{KIb}}}ABBj}\",[]]Bg[\"{{{AHO`{BFDf}}{AHO`{BFDf}}}ABBj}\",[]]Be[\"{{{AHO`{KG`}}{AHO`{KG`}}}ABBj}\",[]]Bg[\"{{{AHO`{AHFd}}{AHO`{AHFd}}}ABBj}\",[]]Bg[\"{{{AHO`{BBBl}}{AHO`{BBBl}}}ABBj}\",[]]Bg[\"{{{AHO`{AJAn}}{AHO`{AJAn}}}ABBj}\",[]]Bg[\"{{{AHO`{AOLb}}{AHO`{AOLb}}}ABBj}\",[]]Bg[\"{{{AHO`{BEGb}}{AHO`{BEGb}}}ABBj}\",[]]Bg[\"{{{AHO`{BFCl}}{AHO`{BFCl}}}ABBj}\",[]]Bg[\"{{{AHO`{B@Mh}}{AHO`{B@Mh}}}ABBj}\",[]]Be[\"{{{AHO`{DAn}}{AHO`{DAn}}}ABBj}\",[]]Be[\"{{{AHO`{A@l}}{AHO`{A@l}}}ABBj}\",[]]Bg[\"{{{AHO`{ABCf}}{AHO`{ABCf}}}ABBj}\",[]]A`[\"{ABCdA@j}\",[]]A`[\"{ABCfA@l}\",[]]o[\"{KFhA@l}\",[]]A`[\"{AHAbA@l}\",[]]A`[\"{ABDhKFl}\",[]]A`[\"{ABDjKFl}\",[]]A`[\"{ABC`KFl}\",[]]A`[\"{ABDlKFl}\",[]]Bh[\"{{{AHO`{AAj}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AOMf}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AOIn}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BC@l}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BHEb}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BGIb}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AIKn}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AILb}}{AHO`{CGbAHC`}}}KGb}\",[]]Bh[\"{{{AHO`{KEd}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BGJ`}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BFDd}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{AB@h}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BAOf}}{AHO`{CGbAHC`}}}KGb}\",[]]Bh[\"{{{AHO`{KFd}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{AINh}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BGA`}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{ABA`}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AIOj}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{BBNb}}{AHO`{CGbAHC`}}}KGb}\",[]]Bh[\"{{{AHO`{DA`}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BCAf}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AIOd}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BGB`}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BC@h}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{AHFb}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{AIOh}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{BC@f}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{BACb}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AOMl}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AJ@`}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AJAl}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{BAC`}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bh[\"{{{AHO`{NMn}}{AHO`{CGbAHC`}}}KGb}\",[]]Cd[\"{{{AHO`{KI`}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Ce[\"{{{AHO`{BGIj}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Cd[\"{{{AHO`{ID`}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Cd[\"{{{AHO`{KFn}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Ce[\"{{{AHO`{AINj}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{AHEh}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{BFKd}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Ce[\"{{{AHO`{AIKd}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Ce[\"{{{AHO`{ABDn}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{AILn}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{AHEl}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{BCMf}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{B@Ld}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Ce[\"{{{AHO`{BCLn}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Ce[\"{{{AHO`{AHE`}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Ce[\"{{{AHO`{BBLd}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Ce[\"{{{AHO`{BCNj}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{BCOl}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{BFGh}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{BFIf}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{AOL`}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{BBMh}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{BABd}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{BH@l}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{B@Kh}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{AOJd}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Ce[\"{{{AHO`{AHCf}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Ce[\"{{{AHO`{BCLh}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Ce[\"{{{AHO`{BEFf}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Ce[\"{{{AHO`{B@M`}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Ce[\"{{{AHO`{B@Mf}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Ce[\"{{{AHO`{B@N`}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{BFKn}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Ce[\"{{{AHO`{AIMd}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{BEJj}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Cd[\"{{{AHO`{DAl}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Cd[\"{{{AHO`{A@j}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Ce[\"{{{AHO`{ABCd}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Ce[\"{{{AHO`{ABDh}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{AHCb}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Cd[\"{{{AHO`{CNj}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{AIJl}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Cd[\"{{{AHO`{IDh}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{AOJn}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Cd[\"{{{AHO`{NKl}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{BBAl}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{BHLn}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{AOJj}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Ce[\"{{{AHO`{BFLf}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]Cd[\"{{{AHO`{IFb}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Ce[\"{{{AHO`{BGHn}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Bi[\"{{{AHO`{AILd}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AB@j}}{AHO`{CGbAHC`}}}KGb}\",[]]Bh[\"{{{AHO`{KIb}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{BGIl}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bh[\"{{{AHO`{IDb}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BFDf}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bh[\"{{{AHO`{KG`}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{BAAl}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AINl}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AHEj}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BFDb}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BFKf}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{AIKj}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{ABE`}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AIM`}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AHEn}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BCMh}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AHFd}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{BBBl}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{AJAn}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{AOKn}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{AONd}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BC@j}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{B@Ll}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{BCM`}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{AHEb}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{BBLf}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{BHDj}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bh[\"{{{AHO`{IFl}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BCNl}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BCOn}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BFGj}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BFIh}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AOLb}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BBMj}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BABh}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BH@n}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{B@Kl}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AOJf}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{AHCh}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{BBNd}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{AIMl}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{AJ@h}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{B@Mj}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{BCLl}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{BEFj}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{AAOl}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{BEGb}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{ABAb}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{B@Mb}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{AOIj}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AIOl}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{BBBb}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BCNh}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BHE`}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{ABAh}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BFCl}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{BCMd}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BAOd}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{B@Mh}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{B@Nb}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BCAb}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{BFL`}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{AIMf}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BIAl}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BEJl}}{AHO`{CGbAHC`}}}KGb}\",[]]Bh[\"{{{AHO`{DAn}}{AHO`{CGbAHC`}}}KGb}\",[]]Bh[\"{{{AHO`{A@l}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{ABCf}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{ABDj}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AHCd}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{BBCh}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{BGAn}}{AHO`{CGbAHC`}}}KGb}\",[]]Bh[\"{{{AHO`{IFf}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bi[\"{{{AHO`{BHM`}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AOJl}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BFLl}}{AHO`{CGbAHC`}}}KGb}\",[]]Cd[\"{{{AHO`{IFd}}{AHO`{CGbAHC`}}}{{KGd{ICjIEb}}}}\",[]]0Bi[\"{{{AHO`{BGI`}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bh[\"{{{AHO`{CNl}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AIJn}}{AHO`{CGbAHC`}}}KGb}\",[]]Bh[\"{{{AHO`{IDj}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{AOK`}}{AHO`{CGbAHC`}}}KGb}\",[]]Bh[\"{{{AHO`{NKn}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BBAn}}{AHO`{CGbAHC`}}}KGb}\",[]]Bh[\"{{{AHO`{IFh}}{AHO`{CGbAHC`}}}KGb}\",[]]0Bh[\"{{{AHO`{IFn}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{BCAd}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{ABC`}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{ABDl}}{AHO`{CGbAHC`}}}KGb}\",[]]Bi[\"{{{AHO`{B@Nd}}{AHO`{CGbAHC`}}}KGb}\",[]]Bd[\"{{{AHO`{BGIb}}}{{KFl{AJ@f}}}}\",[]]Ak[\"{{{AHO`{BAAl}}}AB@d}\",[]]A`[\"{ABDnCNb}\",[]]A`[\"{ABE`CNb}\",[]]Aj[\"{{{AHO`{BBBb}}}KCb}\",[]]A`[\"{BHE`CNb}\",[]]o[\"{D@hCNb}\",[]]A`[\"{ABC`KCb}\",[]]Cf[\"{{{AHO`{CGbBH@l}}{A@b{ABD`}}}{{AHO`{CGbBH@l}}}}\",[]]Cf[\"{{{AHO`{CGbBH@n}}{A@b{ABDb}}}{{AHO`{CGbBH@n}}}}\",[]]8Af[\"{{AB@dCNb}BC@l}\",[]]Ah[\"{{}{{A@b{BHEb}}}}\",[]]Ae[\"{{KCbCN`}BANd}\",[]]Ce[\"{{BBMh{KFl{BABd}}AHFl{A@b{NMd}}}{{KGf{AB@h}}}}\",[]]Ai[\"{{{A@d{IIb}}}AINn}\",[]]A`[\"{NLlBCNj}\",[]]Ai[\"{{{A@b{NJj}}}BBMh}\",[]]Be[\"{{{A@b{BABf}}{A@b{BFBn}}}BABd}\",[]]Aj[\"{{{A@b{B@Kj}}}B@Kh}\",[]]B`[\"{{{AHO`{{K@j{Oh}}}}}B@Mf}\",[]]Ae[\"{{CN`KCb}BEJj}\",[]]Ah[\"{{DAlA@jCNb}ABCd}\",[]]Am[\"{{AJ@f{KFl{KFb}}}AHCb}\",[]]Ao[\"{{{A@b{AOJn}}NK`CMn}IDh}\",[]]Ao[\"{{{A@b{BBAl}}NK`CMn}NKl}\",[]]Ae[\"{{KCbCN`}BANf}\",[]]Ai[\"{{{A@d{IIb}}}AIO`}\",[]]Bo[\"{{{A@b{BBMj}}{KFl{{A@b{BABh}}}}DC`}AB@j}\",[]]A`[\"{KCbBIE`}\",[]]o[\"{{}BIAb}\",[]]A`[\"{NLlBCNl}\",[]]Ai[\"{{{A@b{NJl}}}BBMj}\",[]]Be[\"{{{A@b{BABj}}{A@b{BFC`}}}BABh}\",[]]Aj[\"{{{A@b{B@Kn}}}B@Kl}\",[]]Ek[\"{{AAOb{AHO`{c}}{AHO`{{K@j{CN`}}}}DC`}{{KGd{{KC`{BBBb{KFl{AAOb}}}}BFCl}}}{BFHdIHh}}\",[\"\"]]B`[\"{{{AHO`{{K@j{Oh}}}}}B@Mh}\",[]]Af[\"{{BFL`KHl}BCAb}\",[]]A`[\"{KCbBIAl}\",[]]Ae[\"{{CN`KCb}BEJl}\",[]]Ah[\"{{DAnA@lCNb}ABCf}\",[]]Am[\"{{AJ@f{KFl{KFb}}}AHCd}\",[]]Ao[\"{{{A@b{AOK`}}NK`CMn}IDj}\",[]]Ao[\"{{{A@b{BBAn}}NK`CMn}NKn}\",[]]Aj[\"{AJAj{{KGf{BCAd}}}}\",[]]Aa[\"{BEJdBHKb}\",[]]Aa[\"{BEJhBHKd}\",[]]o[\"{DAlDAl}\",[]]o[\"{DAnDAn}\",[]]Ak[\"{{{AHO`{AJ@n}}}AJ@l}\",[]]Ai[\"{{{AHO`{NMd}}}NK`}\",[]]0Aj[\"{{{AHO`{BACb}}}NK`}\",[]]Ak[\"{{{AHO`{BBCd}}}AJ@l}\",[]]Ak[\"{{{AHO`{AJA`}}}AJ@l}\",[]]Ak[\"{{{AHO`{BBCf}}}AJ@l}\",[]]Bg[\"{{{AHO`{AILb}}KFn}{{KFl{AB@d}}}}\",[]]Aa[\"{AHElAB@d}\",[]]Bg[\"{{{AHO`{AILd}}KG`}{{KFl{AB@d}}}}\",[]]Aa[\"{AHEnAB@d}\",[]]Ah[\"{AAl{{KGh{ICj}}}}\",[]]Ah[\"{AAn{{KGh{ICj}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Aa[\"{BA@`AJ@f}\",[]]Aa[\"{BA@bAJ@f}\",[]]Aa[\"{ABC`AJ@f}\",[]]Aa[\"{BCAbBFL`}\",[]]A`[\"{AHElCNb}\",[]]A`[\"{AHEnCNb}\",[]]A`[\"{AAOlICn}\",[]]Am[\"{{{AHO`{CGbABBf}}}ICj}\",[]]Am[\"{{{AHO`{CGbABA`}}}ICj}\",[]]Al[\"{{{AHO`{CGbDA`}}}ICj}\",[]]Am[\"{{{AHO`{CGbBCAf}}}ICj}\",[]]Am[\"{{{AHO`{CGbAIOd}}}ICj}\",[]]Am[\"{{{AHO`{CGbAJ@`}}}ICj}\",[]]Al[\"{{{AHO`{CGbKDl}}}ICj}\",[]]A`[\"{cc{}}\",[\"T\"]]000000000000000000000000Aa[\"{BCLnBFDd}\",[]]1111111111111Aa[\"{AHFbBC@h}\",[]]2Aa[\"{AIOhAHFb}\",[]]3Aa[\"{BCLnAHFb}\",[]]Aa[\"{BCLnAIOh}\",[]]55Aa[\"{AHFbBC@f}\",[]]666Aa[\"{B@LdAJAl}\",[]]Aa[\"{BAC`AJAl}\",[]]Aa[\"{BCLnAJAl}\",[]]9Aa[\"{BCLnBAC`}\",[]]::::::::A`[\"{ABCdKI`}\",[]]A`[\"{BFKnKI`}\",[]]o[\"{CN`KI`}\",[]]o[\"{CMnKI`}\",[]]n[\"{OhKI`}\",[]]????Al[\"{cKFn{{D@b{CN`}}}}\",[\"T\"]]A`[\"{cc{}}\",[\"T\"]]00000000Aa[\"{AOJbBCLn}\",[]]1A`[\"{IDfBCLn}\",[]]A`[\"{IEjBCLn}\",[]]3333333A`[\"{KI`AOL`}\",[]]44444Aa[\"{BD@`AOJd}\",[]]Aa[\"{BGIjAOJd}\",[]]66A`[\"{IEjBCLh}\",[]]7Aa[\"{BFLfB@M`}\",[]]8888A`[\"{KI`BFKn}\",[]]9999A`[\"{KI`ABCd}\",[]]::Aa[\"{AJ@fAHCb}\",[]];;;;;;;;;;;;;;;;A`[\"{BBOhIFb}\",[]]A`[\"{AIN`IFb}\",[]]A`[\"{BCMjIFb}\",[]]A`[\"{BCOdIFb}\",[]]A`[\"{BHLlIFb}\",[]]A`[\"{BHGfIFb}\",[]]A`[\"{BEFlIFb}\",[]]A`[\"{BHMnIFb}\",[]]A`[\"{BEHbIFb}\",[]]A`[\"{BEIlIFb}\",[]]A`[\"{BBNfIFb}\",[]]A`[\"{cc{}}\",[\"T\"]]0000000000000000000000A`[\"{BFL`KIb}\",[]]n[\"{OhKIb}\",[]]o[\"{CMnKIb}\",[]]o[\"{CN`KIb}\",[]]A`[\"{ABCfKIb}\",[]]5555Al[\"{cKG`{{D@b{CN`}}}}\",[\"T\"]]666666666666666666Aa[\"{BCAbBCM`}\",[]]7777A`[\"{AHEjIFl}\",[]]888888A`[\"{KIbAOLb}\",[]]9999Aa[\"{BD@`AOJf}\",[]]:Aa[\"{BGIlAOJf}\",[]];;;;;Aa[\"{BCAbBCLl}\",[]]<<<<<>>>>A`[\"{IFfBCAb}\",[]]?A`[\"{KIbBFL`}\",[]]A`[\"{cc{}}\",[\"T\"]]0000A`[\"{KIbABCf}\",[]]111Aa[\"{AJ@fAHCd}\",[]]22A`[\"{BBChIFf}\",[]]33333333A`[\"{BHMnIFd}\",[]]A`[\"{BEFlIFd}\",[]]5A`[\"{BEHbIFd}\",[]]A`[\"{BBNfIFd}\",[]]A`[\"{BBOhIFd}\",[]]A`[\"{BCMjIFd}\",[]]A`[\"{BCOdIFd}\",[]]A`[\"{BHLlIFd}\",[]]A`[\"{BHGfIFd}\",[]]A`[\"{BEIlIFd}\",[]]A`[\"{AIN`IFd}\",[]]>>>>>>>>>>>>An[\"{{{AHO`{c}}}AJAjCNh}\",[\"S\"]]??????????????Bm[\"{{{AHO`{KI`}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bk[\"{{{AHO`{KFn}}{AHO`{CGbc}}}ICjKF`}\",[\"H\"]]Bn[\"{{{AHO`{AOL`}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{B@Mf}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bm[\"{{{AHO`{DAl}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bm[\"{{{AHO`{A@j}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{ABCd}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{AHCb}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bm[\"{{{AHO`{KIb}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{BFDf}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bk[\"{{{AHO`{KG`}}{AHO`{CGbc}}}ICjKF`}\",[\"H\"]]Bn[\"{{{AHO`{AHFd}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{BBBl}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{AJAn}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{AOLb}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{BEGb}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{BFCl}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{B@Mh}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bm[\"{{{AHO`{DAn}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bm[\"{{{AHO`{A@l}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{ABCf}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Bn[\"{{{AHO`{AHCd}}{AHO`{CGbc}}}ICjKF`}\",[\"__H\"]]Aa[\"{{}c{}}\",[\"U\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n[\"{{}ICj}\",[]]0000Cc[\"{{{AHO`{CGbKDl}}KCb}{{KGd{{KFl{IDb}}AHFd}}}}\",[]]Cf[\"{{{AHO`{CGbBB@j}}}{{KFl{c}}}{}}\",[\"Iterator::Item\"]]Dl[\"{{{AHO`{BFBn}}ICh{AHO`{CGb{K@j{Oh}}}}}{{KGd{{AHO`{{K@j{Oh}}}}AOJj}}}}\",[]]Bi[\"{{{AHO`{CGbNNf}}A@l}{{KFl{ABCf}}}}\",[]]Dl[\"{{{AHO`{BFC`}}ICh{AHO`{CGb{K@j{Oh}}}}}{{KGd{{AHO`{{K@j{Oh}}}}AOJl}}}}\",[]]Bf[\"{{{AHO`{AILb}}KFn}{{KFl{DA`}}}}\",[]]Bf[\"{{{AHO`{DA`}}}{{KGd{ICjAIKd}}}}\",[]]A`[\"{AILnCNb}\",[]]Am[\"{{{AHO`{CGbAILd}}}ICj}\",[]]A`[\"{AIM`CNb}\",[]]Ck[\"{{{ABb{{AHO`{CGbAIJd}}}}{AHO`{CGbNIn}}}{{DAh{ICj}}}}\",[]]Dk[\"{{{ABb{{AHO`{CGbBD@f}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbABBf}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbABBh}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbAIKn}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{ABb{{AHO`{CGbNLb}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{ABb{{AHO`{CGbKFf}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbAH@h}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbAAO`}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbBAA`}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbBAAn}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{ABb{{AHO`{CGbKCj}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{ABb{{AHO`{CGbBBNb}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{ABb{{AHO`{CGbNMn}}}}{AHO`{CGbNIn}}}{{DAh{c}}}{}}\",[\"Future::Output\"]]Bf[\"{{{AHO`{CGbAILd}}}{{KFl{IFl}}}}\",[]]De[\"{{{AHO`{CGbAIOd}}{AHO`{CGb{K@j{Oh}}}}}{{KGd{{KFl{KCb}}AHFb}}}}\",[]]Bm[\"{{{AHO`{CGbAIOf}}DC`}{{KGd{KDlBBBl}}}}\",[]]C`[\"{{DAn{AHO`{CGbc}}}{{KGd{BGAnIFf}}}A@f}\",[\"R\"]]Bf[\"{{{AHO`{CGbAHBb}}}{{KFl{ICn}}}}\",[]]Ec[\"{{{AHO`{BCAd}}AJAj{AHO`{CGb{K@j{AIMh}}}}{AHO`{CGb{K@j{ABC`}}}}}{{KGf{KCb}}}}\",[]]Cn[\"{{{AHO`{BFBn}}ICh{AHO`{CGb{ABh{Oh}}}}}{{KGd{ICjAOJj}}}}\",[]]Cn[\"{{{AHO`{BFC`}}ICh{AHO`{CGb{ABh{Oh}}}}}{{KGd{ICjAOJl}}}}\",[]]C`[\"{{{AHO`{CGbAHBb}}ICnDC`}{{KGd{ICjBFDf}}}}\",[]]Cc[\"{{{AHO`{BCAd}}AJAj{AHO`{ABDl}}}{{KGf{ICj}}}}\",[]]Bn[\"{{{AHO`{CGbBH@l}}CN`}{{AHO`{CGbBH@l}}}}\",[]]Bn[\"{{{AHO`{CGbBH@n}}CN`}{{AHO`{CGbBH@n}}}}\",[]]Aj[\"{{{AHO`{AILb}}}DAl}\",[]]Aj[\"{{{AHO`{AILd}}}DAn}\",[]]Cn[\"{{{AHO`{NJj}}{AHO`{{K@j{Oh}}}}{AHO`{CGb{K@j{Oh}}}}}ICj}\",[]]Cn[\"{{{AHO`{NJl}}{AHO`{{K@j{Oh}}}}{AHO`{CGb{K@j{Oh}}}}}ICj}\",[]]A`[\"{BC@lCNb}\",[]]A`[\"{ABDhKCb}\",[]]A`[\"{ABDjKCb}\",[]]o[\"{IHdDC`}\",[]]Bm[\"{{{AHO`{CGbAIOd}}KI`}{{KGd{ICjB@Ld}}}}\",[]]Bm[\"{{{AHO`{CGbBGB`}}KI`}{{KGd{ICjB@Ld}}}}\",[]]Bm[\"{{{AHO`{CGbAIOf}}KIb}{{KGd{ICjB@Ll}}}}\",[]]Bn[\"{{{AHO`{AJAf}}{AHO`{CMl}}}{{KFl{ICn}}}}\",[]]Bn[\"{{{AHO`{BBOd}}{AHO`{CMl}}}{{KFl{ICn}}}}\",[]]Bn[\"{{{AHO`{BEJj}}{AHO`{CMl}}}{{KFl{ICn}}}}\",[]]Bn[\"{{{AHO`{AJAh}}{AHO`{CMl}}}{{KFl{ICn}}}}\",[]]Bn[\"{{{AHO`{BBOf}}{AHO`{CMl}}}{{KFl{ICn}}}}\",[]]Bn[\"{{{AHO`{BEJl}}{AHO`{CMl}}}{{KFl{ICn}}}}\",[]]Bd[\"{{{AHO`{BEGf}}}{{KFl{BB@h}}}}\",[]]Bd[\"{{{AHO`{BEGh}}}{{KFl{BB@j}}}}\",[]]Bi[\"{{{A@b{BFBf}}NK`CMn}{{A@d{AILf}}}}\",[]]Bi[\"{{{A@b{AIJl}}NK`CMn}{{A@d{AILf}}}}\",[]]Bi[\"{{{A@b{AOJn}}NK`CMn}{{A@d{AILf}}}}\",[]]Bi[\"{{{A@b{BBAl}}NK`CMn}{{A@d{AILf}}}}\",[]]Bi[\"{{{A@b{BFBh}}NK`CMn}{{A@d{AILh}}}}\",[]]Bi[\"{{{A@b{AIJn}}NK`CMn}{{A@d{AILh}}}}\",[]]Bi[\"{{{A@b{AOK`}}NK`CMn}{{A@d{AILh}}}}\",[]]Bi[\"{{{A@b{BBAn}}NK`CMn}{{A@d{AILh}}}}\",[]]Aj[\"{BG@n{{A@b{BFBh}}}}\",[]]o[\"{ID`ICn}\",[]]A`[\"{ABDnCNb}\",[]]o[\"{IDbICn}\",[]]A`[\"{ABE`CNb}\",[]]Aa[\"{AOIjBCM`}\",[]]Ai[\"{{{AHO`{AAj}}}AAj}\",[]]Ak[\"{{{AHO`{AOMf}}}AOMf}\",[]]Ak[\"{{{AHO`{AOIn}}}AOIn}\",[]]Ak[\"{{{AHO`{AILb}}}AILb}\",[]]Ai[\"{{{AHO`{KEd}}}KEd}\",[]]Ak[\"{{{AHO`{BGJ`}}}BGJ`}\",[]]Ak[\"{{{AHO`{BFDd}}}BFDd}\",[]]Ak[\"{{{AHO`{AB@h}}}AB@h}\",[]]Ak[\"{{{AHO`{BAOf}}}BAOf}\",[]]Ai[\"{{{AHO`{KFd}}}KFd}\",[]]Ai[\"{{{AHO`{DA`}}}DA`}\",[]]Ak[\"{{{AHO`{BCAf}}}BCAf}\",[]]Ak[\"{{{AHO`{BC@h}}}BC@h}\",[]]Ak[\"{{{AHO`{AHFb}}}AHFb}\",[]]Ak[\"{{{AHO`{AIOh}}}AIOh}\",[]]Ak[\"{{{AHO`{BC@f}}}BC@f}\",[]]Ak[\"{{{AHO`{AJAl}}}AJAl}\",[]]Ak[\"{{{AHO`{BAC`}}}BAC`}\",[]]Ai[\"{{{AHO`{KI`}}}KI`}\",[]]Ak[\"{{{AHO`{BGIj}}}BGIj}\",[]]Ai[\"{{{AHO`{KFn}}}KFn}\",[]]Ak[\"{{{AHO`{AINj}}}AINj}\",[]]Ak[\"{{{AHO`{AHEh}}}AHEh}\",[]]Ak[\"{{{AHO`{BFKd}}}BFKd}\",[]]Ak[\"{{{AHO`{AIKd}}}AIKd}\",[]]Ak[\"{{{AHO`{ABDn}}}ABDn}\",[]]Ak[\"{{{AHO`{AILn}}}AILn}\",[]]Ak[\"{{{AHO`{AHEl}}}AHEl}\",[]]Ak[\"{{{AHO`{BCMf}}}BCMf}\",[]]Ak[\"{{{AHO`{B@Ld}}}B@Ld}\",[]]Ak[\"{{{AHO`{BCLn}}}BCLn}\",[]]Ak[\"{{{AHO`{AHE`}}}AHE`}\",[]]Ak[\"{{{AHO`{BBLd}}}BBLd}\",[]]Ak[\"{{{AHO`{BCOl}}}BCOl}\",[]]Ak[\"{{{AHO`{BFGh}}}BFGh}\",[]]Ak[\"{{{AHO`{BFIf}}}BFIf}\",[]]Ak[\"{{{AHO`{AOL`}}}AOL`}\",[]]Ak[\"{{{AHO`{BBMh}}}BBMh}\",[]]Ak[\"{{{AHO`{BABd}}}BABd}\",[]]Ak[\"{{{AHO`{BH@l}}}BH@l}\",[]]Ak[\"{{{AHO`{B@Kh}}}B@Kh}\",[]]Ak[\"{{{AHO`{AOJd}}}AOJd}\",[]]Ak[\"{{{AHO`{AHCf}}}AHCf}\",[]]Ak[\"{{{AHO`{BCLh}}}BCLh}\",[]]Ak[\"{{{AHO`{BEFf}}}BEFf}\",[]]Ak[\"{{{AHO`{B@M`}}}B@M`}\",[]]Ak[\"{{{AHO`{B@Mf}}}B@Mf}\",[]]Ak[\"{{{AHO`{B@N`}}}B@N`}\",[]]Ak[\"{{{AHO`{BFKn}}}BFKn}\",[]]Ak[\"{{{AHO`{AIMd}}}AIMd}\",[]]Ai[\"{{{AHO`{DAl}}}DAl}\",[]]Ai[\"{{{AHO`{A@j}}}A@j}\",[]]Ak[\"{{{AHO`{ABCd}}}ABCd}\",[]]Ak[\"{{{AHO`{AHCb}}}AHCb}\",[]]Ai[\"{{{AHO`{CNj}}}CNj}\",[]]Ak[\"{{{AHO`{AIJl}}}AIJl}\",[]]Ai[\"{{{AHO`{IDh}}}IDh}\",[]]Ak[\"{{{AHO`{AOJn}}}AOJn}\",[]]Ai[\"{{{AHO`{NKl}}}NKl}\",[]]Ak[\"{{{AHO`{BBAl}}}BBAl}\",[]]Ai[\"{{{AHO`{IFb}}}IFb}\",[]]Ak[\"{{{AHO`{BEJb}}}BEJb}\",[]]Ak[\"{{{AHO`{BGHn}}}BGHn}\",[]]Ak[\"{{{AHO`{BEJf}}}BEJf}\",[]]Ai[\"{{{AHO`{KIb}}}KIb}\",[]]Ak[\"{{{AHO`{BGIl}}}BGIl}\",[]]Ak[\"{{{AHO`{BFDf}}}BFDf}\",[]]Ai[\"{{{AHO`{KG`}}}KG`}\",[]]Ak[\"{{{AHO`{BAAl}}}BAAl}\",[]]Ak[\"{{{AHO`{AINl}}}AINl}\",[]]Ak[\"{{{AHO`{AHEj}}}AHEj}\",[]]Ak[\"{{{AHO`{BFDb}}}BFDb}\",[]]Ak[\"{{{AHO`{BFKf}}}BFKf}\",[]]Ak[\"{{{AHO`{AIKj}}}AIKj}\",[]]Ak[\"{{{AHO`{ABE`}}}ABE`}\",[]]Ak[\"{{{AHO`{AIM`}}}AIM`}\",[]]Ak[\"{{{AHO`{AHEn}}}AHEn}\",[]]Ak[\"{{{AHO`{BCMh}}}BCMh}\",[]]Ak[\"{{{AHO`{AHFd}}}AHFd}\",[]]Ak[\"{{{AHO`{BBBl}}}BBBl}\",[]]Ak[\"{{{AHO`{AJAn}}}AJAn}\",[]]Ak[\"{{{AHO`{AOKn}}}AOKn}\",[]]Ak[\"{{{AHO`{BC@j}}}BC@j}\",[]]Ak[\"{{{AHO`{B@Ll}}}B@Ll}\",[]]Ak[\"{{{AHO`{BCM`}}}BCM`}\",[]]Ak[\"{{{AHO`{AHEb}}}AHEb}\",[]]Ak[\"{{{AHO`{BBLf}}}BBLf}\",[]]Ak[\"{{{AHO`{BHDj}}}BHDj}\",[]]Ak[\"{{{AHO`{BCOn}}}BCOn}\",[]]Ak[\"{{{AHO`{BFGj}}}BFGj}\",[]]Ak[\"{{{AHO`{BFIh}}}BFIh}\",[]]Ak[\"{{{AHO`{AOLb}}}AOLb}\",[]]Ak[\"{{{AHO`{BBMj}}}BBMj}\",[]]Ak[\"{{{AHO`{BABh}}}BABh}\",[]]Ak[\"{{{AHO`{BH@n}}}BH@n}\",[]]Ak[\"{{{AHO`{B@Kl}}}B@Kl}\",[]]Ak[\"{{{AHO`{AOJf}}}AOJf}\",[]]Ak[\"{{{AHO`{AHCh}}}AHCh}\",[]]Ak[\"{{{AHO`{AIMl}}}AIMl}\",[]]Ak[\"{{{AHO`{AJ@h}}}AJ@h}\",[]]Ak[\"{{{AHO`{B@Mj}}}B@Mj}\",[]]Ak[\"{{{AHO`{BCLl}}}BCLl}\",[]]Ak[\"{{{AHO`{BEFj}}}BEFj}\",[]]Ak[\"{{{AHO`{AAOl}}}AAOl}\",[]]Ak[\"{{{AHO`{BEGb}}}BEGb}\",[]]Ak[\"{{{AHO`{B@Mb}}}B@Mb}\",[]]Ak[\"{{{AHO`{BCNh}}}BCNh}\",[]]Ak[\"{{{AHO`{BHE`}}}BHE`}\",[]]Ak[\"{{{AHO`{ABAh}}}ABAh}\",[]]Ak[\"{{{AHO`{BFCl}}}BFCl}\",[]]Ak[\"{{{AHO`{B@Mh}}}B@Mh}\",[]]Ak[\"{{{AHO`{B@Nb}}}B@Nb}\",[]]Ak[\"{{{AHO`{BCAb}}}BCAb}\",[]]Ak[\"{{{AHO`{BFL`}}}BFL`}\",[]]Ak[\"{{{AHO`{AIMf}}}AIMf}\",[]]Ak[\"{{{AHO`{BIAl}}}BIAl}\",[]]Ai[\"{{{AHO`{DAn}}}DAn}\",[]]Ai[\"{{{AHO`{A@l}}}A@l}\",[]]Ak[\"{{{AHO`{ABCf}}}ABCf}\",[]]Ak[\"{{{AHO`{AHCd}}}AHCd}\",[]]Ak[\"{{{AHO`{BBCh}}}BBCh}\",[]]Ak[\"{{{AHO`{BGAn}}}BGAn}\",[]]Ai[\"{{{AHO`{IFf}}}IFf}\",[]]Ai[\"{{{AHO`{IFd}}}IFd}\",[]]Ak[\"{{{AHO`{BEJd}}}BEJd}\",[]]Ak[\"{{{AHO`{BGI`}}}BGI`}\",[]]Ak[\"{{{AHO`{BEJh}}}BEJh}\",[]]Ai[\"{{{AHO`{CNl}}}CNl}\",[]]Ak[\"{{{AHO`{AIJn}}}AIJn}\",[]]Ai[\"{{{AHO`{IDj}}}IDj}\",[]]Ak[\"{{{AHO`{AOK`}}}AOK`}\",[]]Ai[\"{{{AHO`{NKn}}}NKn}\",[]]Ak[\"{{{AHO`{BBAn}}}BBAn}\",[]]Ai[\"{{{AHO`{IFn}}}IFn}\",[]]Ak[\"{{{AHO`{ABC`}}}ABC`}\",[]]Ak[\"{{{AHO`{ABDl}}}ABDl}\",[]]Ak[\"{{{AHO`{B@Nd}}}B@Nd}\",[]]Ak[\"{{{AHO`{BG@n}}}BG@n}\",[]]Ak[\"{{{AHO`{BCN`}}}BCN`}\",[]]Ak[\"{{{AHO`{BHKb}}}BHKb}\",[]]Ak[\"{{{AHO`{BHKd}}}BHKd}\",[]]Bn[\"{{{AHO`{AILb}}KI`{AHO`{{K@j{Oh}}}}}ICj}\",[]]Bn[\"{{{AHO`{AB@h}}KI`{AHO`{{K@j{Oh}}}}}ICj}\",[]]Bf[\"{{{AHO`{DA`}}}{{KGd{ICjBBLd}}}}\",[]]Bf[\"{{{AHO`{CGbAILd}}NK`KIbICn}ICj}\",[]]Bj[\"{{{AHO`{B@Mf}}}{{AHO`{{K@j{Oh}}}}}}\",[]]Bj[\"{{{AHO`{B@Mh}}}{{AHO`{{K@j{Oh}}}}}}\",[]]Aa[\"{BCAbAIMl}\",[]]A`[\"{ABCdCNb}\",[]]A`[\"{ABCfCNb}\",[]]A`[\"{B@N`DC`}\",[]]A`[\"{B@NbDC`}\",[]]l[\"{NKb}\",[]]l[\"{NKd}\",[]]Cf[\"{{{AHO`{BFHd}}{AHO`{CGbA@f}}}{{KGd{B@MhBFCl}}}}\",[]]Cf[\"{{{AHO`{BIE`}}{AHO`{CGbA@f}}}{{KGd{B@MhBFCl}}}}\",[]]Bc[\"{{{AHO`{AILd}}}{{ABh{KG`}}}}\",[]]Bd[\"{{{AHO`{IHj}}{AHO`{CMl}}}ICj}\",[]]Am[\"{{{AHO`{AOJ`}}KCb}ICj}\",[]]Ai[\"{{{AHO`{IHl}}}ICj}\",[]]Bg[\"{{{ABb{{AHO`{CGbAIJd}}}}NK`}ICj}\",[]]Bm[\"{{{AHO`{CGbAJ@`}}KI`}{{KGd{ICjB@Ld}}}}\",[]]Bm[\"{{{AHO`{CGbAJ@b}}KIb}{{KGd{ICjB@Ll}}}}\",[]]Am[\"{ABA`{{KGd{ICjAIOj}}}}\",[]]Dc[\"{{{AHO`{CGbAB@j}}ABAb{AHO`{CGb{ABh{Oh}}}}}{{KGd{ABDjAIOl}}}}\",[]]Bj[\"{{{AHO`{NMd}}{ABb{{A@d{KEn}}}}}ICj}\",[]]Bk[\"{{{AHO`{BACb}}{ABb{{A@d{KEn}}}}}ICj}\",[]]Bk[\"{{{AHO`{AOMl}}{ABb{{A@d{KEn}}}}}ICj}\",[]]o[\"{AAjDC`}\",[]]Ak[\"{{{AHO`{AILb}}}BCMf}\",[]]Ak[\"{{{AHO`{AB@h}}}BAOf}\",[]]Aj[\"{{{AHO`{DA`}}}AHEl}\",[]]A`[\"{KEdBCMf}\",[]]An[\"{{{AHO`{CGbAILd}}}BCMh}\",[]]Ce[\"{{{AHO`{CGbAINn}}{KFl{KHl}}}{{AHO`{CGbAINn}}}}\",[]]Ce[\"{{{AHO`{CGbAIO`}}{KFl{KHl}}}{{AHO`{CGbAIO`}}}}\",[]]Ck[\"{{{AHO`{CGbAJ@`}}{AHO`{{K@j{Oh}}}}}{{KGd{KCbAJAl}}}}\",[]]Ck[\"{{{AHO`{CGbAJ@b}}{AHO`{{K@j{Oh}}}}}{{KGd{KCbAJAn}}}}\",[]]Bl[\"{{{AHO`{BGAn}}{AHO`{CGbc}}}ICjKDj}\",[\"W\"]]Aj[\"{{{AHO`{AB@h}}}KCj}\",[]]An[\"{ABA`{{KGd{AIKnBCLn}}}}\",[]]Bi[\"{{{AHO`{CGbNNf}}A@l}{{KFl{ABCf}}}}\",[]]Fj[\"{{{AHO`{CGbAB@j}}ABAbNK`{AHO`{CGb{ABh{Oh}}}}{KFl{{A@b{BABh}}}}}{{KGd{{KC`{BEGbAILd}}{A@d{AOIj}}}}}}\",[]]Am[\"{AHO`{{AHO`{c}}}{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A`[\"{AAjAOIn}\",[]]Bc[\"{{AJ@fBBLbAAj}{{KGh{AOJ`}}}}\",[]]Aj[\"{AJ@f{{KGf{AB@h}}}}\",[]]Ba[\"{{}{{KGd{ICj{A@d{IEd}}}}}}\",[]]Ak[\"{{{AHO`{AILb}}}BCLn}\",[]]Be[\"{{{AHO`{AB@j}}}{{AHO`{BBMj}}}}\",[]]o[\"{OhBFKn}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{BABdA@b}\",[]]o[\"{OhBFL`}\",[]]A`[\"{AIM`CNb}\",[]]A`[\"{BABhA@b}\",[]]A`[\"{BCAbKFl}\",[]]Bm[\"{{{AHO`{CGbc}}}{{KGd{KI`BBCh}}}A@f}\",[\"B\"]]Bm[\"{{{AHO`{CGbc}}}{{KGd{KFnBBCh}}}A@f}\",[\"B\"]]Bn[\"{{{AHO`{CGbc}}}{{KGd{AHCfBBCh}}}A@f}\",[\"B\"]]Bn[\"{{{AHO`{CGbc}}}{{KGd{BFKnBBCh}}}A@f}\",[\"B\"]]Bn[\"{{{AHO`{CGbc}}}{{KGd{ABCdBBCh}}}A@f}\",[\"B\"]]Bi[\"{{{AHO`{CGbc}}}{{KGl{KIb}}}A@f}\",[\"B\"]]Bi[\"{{{AHO`{CGbc}}}{{KGl{KG`}}}A@f}\",[\"B\"]]Bj[\"{{{AHO`{CGbc}}}{{KGl{AHCh}}}A@f}\",[\"B\"]]Bj[\"{{{AHO`{CGbc}}}{{KGl{AIMl}}}A@f}\",[\"B\"]]Ek[\"{{{AHO`{CGb{KEl{AAOb}}}}{AHO`{c}}{AHO`{{K@j{CN`}}}}DC`}{{KGd{BCNhBFCl}}}{BFHdIHh}}\",[\"\"]]Bj[\"{{{AHO`{CGbc}}}{{KGl{BFL`}}}A@f}\",[\"B\"]]Bj[\"{{{AHO`{CGbc}}}{{KGl{ABCf}}}A@f}\",[\"B\"]]Bj[\"{{{AHO`{CGbc}}}{{KGl{AHBd}}}A@f}\",[\"B\"]]A`[\"{ABC`KFl}\",[]]Bk[\"{{{AHO`{KI`}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bk[\"{{{AHO`{KFn}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{AHCf}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{BFKn}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{ABCd}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bk[\"{{{AHO`{KIb}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bk[\"{{{AHO`{KG`}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{AHCh}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{AIMl}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{AAOl}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{BFL`}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{ABCf}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bl[\"{{{AHO`{AHBn}}{AHO`{CGbc}}}ICjKDj}\",[\"B\"]]Bj[\"{{{AHO`{CGbAJ@`}}}{{KGd{ICjB@Ld}}}}\",[]]Bj[\"{{{AHO`{CGbAJ@b}}}{{KGd{ICjAOKn}}}}\",[]]An[\"{{{AHO`{AONh}}AB@d}ICj}\",[]]Ea[\"{{{AHO`{CGbAB@j}}NK`AHCd{KFl{B@Nb}}AAOb{AHO`{CGb{ABh{Oh}}}}}{{KFl{BAOb}}}}\",[]]o[\"{D@dNKb}\",[]]o[\"{D@fNKd}\",[]]A`[\"{ABA`ICj}\",[]]Ba[\"{{{AHO`{CGbAB@j}}ABAb}ICj}\",[]]Bh[\"{{{AHO`{AJAf}}{AHO`{CMl}}ICn}ICj}\",[]]Bh[\"{{{AHO`{BBOd}}{AHO`{CMl}}ICn}ICj}\",[]]Bh[\"{{{AHO`{BEJj}}{AHO`{CMl}}ICn}ICj}\",[]]Bh[\"{{{AHO`{AJAh}}{AHO`{CMl}}ICn}ICj}\",[]]Bh[\"{{{AHO`{BBOf}}{AHO`{CMl}}ICn}ICj}\",[]]Bh[\"{{{AHO`{BEJl}}{AHO`{CMl}}ICn}ICj}\",[]]A`[\"{AHAlDC`}\",[]]o[\"{ID`CNb}\",[]]o[\"{IDbCNb}\",[]]Ch[\"{{{AHO`{CGbAILf}}NK`NK`CNbCNbDC`{AHO`{BAAj}}}ICj}\",[]]Cg[\"{{{AHO`{CGbCNj}}NK`NK`CNbCNbDC`{AHO`{BAAj}}}ICj}\",[]]Cg[\"{{{AHO`{CGbIDh}}NK`NK`CNbCNbDC`{AHO`{BAAj}}}ICj}\",[]]Cg[\"{{{AHO`{CGbNKl}}NK`NK`CNbCNbDC`{AHO`{BAAj}}}ICj}\",[]]Ch[\"{{{AHO`{CGbAILh}}NK`NK`CNbCNbDC`{AHO`{BAAl}}}ICj}\",[]]Cg[\"{{{AHO`{CGbCNl}}NK`NK`CNbCNbDC`{AHO`{BAAl}}}ICj}\",[]]Cg[\"{{{AHO`{CGbIDj}}NK`NK`CNbCNbDC`{AHO`{BAAl}}}ICj}\",[]]Cg[\"{{{AHO`{CGbNKn}}NK`NK`CNbCNbDC`{AHO`{BAAl}}}ICj}\",[]]o[\"{D@dNKb}\",[]]o[\"{D@fNKd}\",[]]o[\"{BEKfOh}\",[]]A`[\"{KEdBCLn}\",[]]A`[\"{BCLhICn}\",[]]A`[\"{BEFfICn}\",[]]Aa[\"{AH@`BFD`}\",[]]A`[\"{BCLlICn}\",[]]A`[\"{BEFjICn}\",[]]A`[\"{BCAbKHl}\",[]]Aa[\"{BBLjBCM`}\",[]]Aa[\"{AH@bBFDb}\",[]]Bg[\"{{{AHO`{AB@h}}AHFl}{{KGf{ICj}}}}\",[]]A`[\"{ABA`ICj}\",[]]Cf[\"{{{AHO`{CGbAB@j}}ABAb{AHO`{CGb{ABh{Oh}}}}}ABDj}\",[]]Ak[\"{{{AHO`{AHCb}}}AJ@f}\",[]]l[\"{NKb}\",[]]Ak[\"{{{AHO`{AHCd}}}AJ@f}\",[]]l[\"{NKd}\",[]]An[\"{{AB@fAAj}{{KGh{ICj}}}}\",[]]B`[\"{{BABdAJ@f}{{KGf{AB@h}}}}\",[]]Bc[\"{BAB`{{KGd{ICj{A@d{IEd}}}}}}\",[]]Bk[\"{{{AHO`{BFDd}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{BC@h}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{AHFb}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{AIOh}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{BC@f}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{AJAl}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{BAC`}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{BCLn}}}{{KFl{{AHO`{IEd}}}}}}\",[]]Bk[\"{{{AHO`{BCM`}}}{{KFl{{AHO`{IEd}}}}}}\",[]]A`[\"{ABDhKFl}\",[]]A`[\"{ABDjKFl}\",[]]A`[\"{ABDlKFl}\",[]]Bg[\"{{{AHO`{DA`}}}{{KGd{AINjAIKd}}}}\",[]]Aa[\"{BAAbAINj}\",[]]Aa[\"{BAAdAINl}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]A`[\"{ABC`KCb}\",[]]Aa[\"{AHElABDn}\",[]]Aa[\"{BCMfABDn}\",[]]Aa[\"{AHEnABE`}\",[]]Aa[\"{BCMhABE`}\",[]]3210Dh[\"{{{AHO`{NJj}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjAOJj}}}}\",[]]Dh[\"{{{AHO`{NJl}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjAOJl}}}}\",[]]Aj[\"{{{AHO`{AILf}}}CNb}\",[]]Ai[\"{{{AHO`{CNj}}}CNb}\",[]]Ai[\"{{{AHO`{IDh}}}CNb}\",[]]Ai[\"{{{AHO`{NKl}}}CNb}\",[]]Aj[\"{{{AHO`{AILh}}}CNb}\",[]]Ai[\"{{{AHO`{CNl}}}CNb}\",[]]Ai[\"{{{AHO`{IDj}}}CNb}\",[]]Ai[\"{{{AHO`{NKn}}}CNb}\",[]]Aj[\"{{{AHO`{BGOj}}}KCb}\",[]]Aj[\"{{{AHO`{BGOl}}}KCb}\",[]]Aj[\"{{{AHO`{BIAb}}}KCb}\",[]]Aj[\"{{{AHO`{BIAl}}}KCb}\",[]]o[\"{AAjKCb}\",[]]n[\"{{}NIl}\",[]]0A`[\"{A@hAAOf}\",[]]111Ba[\"{{AHO`{AHO`{c}}}ABBj{}}\",[\"K\"]]0000000000000000000Cg[\"{{{AHO`{AB@h}}AJ@f{AHO`{CMl}}}{{KGd{AIKnB@M`}}}}\",[]]Dl[\"{{{AHO`{CGbAB@j}}NK`B@KlAJ@f{AHO`{CMl}}}{{KGd{{KC`{BEGbAILd}}B@Mb}}}}\",[]]Bd[\"{{{AHO`{ABA`}}}{{KFl{BEGf}}}}\",[]]Dm[\"{{{AHO`{AHDj}}KFnCNb{AHO`{{K@j{Oh}}}}{AHO`{CGbAAOb}}}{{KGd{ICjAOJj}}}}\",[]]Ca[\"{{{AHO`{AHCl}}KCb{AHO`{CGb{K@j{Oh}}}}}ICj}\",[]]Bd[\"{{{AHO`{ABAb}}}{{KFl{BEGh}}}}\",[]]Dm[\"{{{AHO`{AHDl}}KG`CNb{AHO`{{K@j{Oh}}}}{AHO`{CGbAAOb}}}{{KGd{ICjAOJl}}}}\",[]]Ca[\"{{{AHO`{AHCn}}KCb{AHO`{CGb{K@j{Oh}}}}}ICj}\",[]]o[\"{{}AONf}\",[]]n[\"{{}IHj}\",[]]o[\"{{}AOJ`}\",[]]o[\"{{}BANd}\",[]]o[\"{{}BAOf}\",[]]n[\"{{}KI`}\",[]]n[\"{{}KFn}\",[]]o[\"{{}AINj}\",[]]o[\"{{}AIKd}\",[]]o[\"{{}ABDn}\",[]]o[\"{{}AILn}\",[]]o[\"{{}AHEl}\",[]]o[\"{{}BCMf}\",[]]o[\"{{}B@Ld}\",[]]o[\"{{}BCOl}\",[]]o[\"{{}BFGh}\",[]]o[\"{{}BFIf}\",[]]o[\"{{}AOL`}\",[]]o[\"{{}BBMh}\",[]]o[\"{{}BH@l}\",[]]o[\"{{}BEJj}\",[]]o[\"{{}AIJl}\",[]]o[\"{{}AOJn}\",[]]o[\"{{}BBAl}\",[]]o[\"{{}BFBj}\",[]]o[\"{{}BANf}\",[]]o[\"{{}BIAb}\",[]]n[\"{{}KIb}\",[]]n[\"{{}KG`}\",[]]o[\"{{}AINl}\",[]]o[\"{{}AIKj}\",[]]o[\"{{}ABE`}\",[]]o[\"{{}AIM`}\",[]]o[\"{{}AHEn}\",[]]o[\"{{}BCMh}\",[]]o[\"{{}BC@j}\",[]]o[\"{{}B@Ll}\",[]]o[\"{{}BCOn}\",[]]o[\"{{}BFGj}\",[]]o[\"{{}BFIh}\",[]]o[\"{{}AOLb}\",[]]o[\"{{}BBMj}\",[]]o[\"{{}BH@n}\",[]]o[\"{{}BIAl}\",[]]o[\"{{}BEJl}\",[]]o[\"{{}AIJn}\",[]]o[\"{{}AOK`}\",[]]o[\"{{}BBAn}\",[]]o[\"{{}BFBl}\",[]]o[\"{{}ABC`}\",[]]n[\"{{}IHl}\",[]]o[\"{{}BCN`}\",[]]o[\"{{}BAOd}\",[]]Ak[\"{{{AHO`{BBBb}}}B@Mh}\",[]]Ak[\"{{{AHO`{BCNh}}}B@Mh}\",[]]Aa[\"{BHE`B@Mh}\",[]]Aa[\"{BFLbB@Mh}\",[]]A`[\"{D@hB@Mh}\",[]]A`[\"{IH`B@Mh}\",[]]A`[\"{IHdB@Mh}\",[]]Aa[\"{BEKfB@Mh}\",[]]Cg[\"{{{AHO`{AHDj}}KFnCNb{AHO`{CGb{K@j{Oh}}}}KCb}ICj}\",[]]Ca[\"{{{AHO`{AHCl}}KCb{AHO`{CGb{K@j{Oh}}}}}ICj}\",[]]Cg[\"{{{AHO`{AHDl}}KG`CNb{AHO`{CGb{K@j{Oh}}}}KCb}ICj}\",[]]Ca[\"{{{AHO`{AHCn}}KCb{AHO`{CGb{K@j{Oh}}}}}ICj}\",[]]Aj[\"{{{AHO`{AIOd}}}DC`}\",[]]Aj[\"{{{AHO`{BGB`}}}DC`}\",[]]Ak[\"{{{AHO`{AILf}}}BFBj}\",[]]Aj[\"{{{AHO`{CNj}}}BFBj}\",[]]Aj[\"{{{AHO`{IDh}}}BFBj}\",[]]Aj[\"{{{AHO`{NKl}}}BFBj}\",[]]Ak[\"{{{AHO`{AILh}}}BFBl}\",[]]Aj[\"{{{AHO`{CNl}}}BFBl}\",[]]Aj[\"{{{AHO`{IDj}}}BFBl}\",[]]Aj[\"{{{AHO`{NKn}}}BFBl}\",[]]Bn[\"{{{AHO`{CGbBCOl}}CMn}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}CMn}{{AHO`{CGbBCOn}}}}\",[]]Bf[\"{{{AHO`{CGbAILf}}NK`CNbCNb}ICj}\",[]]Bf[\"{{{AHO`{CGbAILh}}NK`CNbCNb}ICj}\",[]]Aj[\"{{{AHO`{AILb}}}KFf}\",[]]Bc[\"{{{AHO`{ABBh}}}{{KFl{KFn}}}}\",[]]Aa[\"{BHE`B@Mh}\",[]]Aa[\"{BFLbB@Mh}\",[]]A`[\"{D@hB@Mh}\",[]]A`[\"{IH`B@Mh}\",[]]Aa[\"{BEKfB@Mh}\",[]]Aj[\"{{{AHO`{AJ@`}}}NMn}\",[]]Bn[\"{{{AHO`{AJ@b}}}{{KGd{{KFl{KIb}}B@Ll}}}}\",[]]o[\"{AAjKCb}\",[]]o[\"{IHjKCb}\",[]]Am[\"{{{AHO`{CGbAILd}}}NNf}\",[]]Aj[\"{{{AHO`{AHDj}}}KCb}\",[]]Aj[\"{{{AHO`{AHDl}}}KCb}\",[]]A`[\"{AHO`KHn}\",[]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bd[\"{{{AHO`{BGJ`}}}{{KFl{AILb}}}}\",[]]Bc[\"{{{AHO`{BCAf}}}{{KFl{DA`}}}}\",[]]Bn[\"{{{AHO`{CGbB@Kh}}CN`}{{AHO`{CGbB@Kh}}}}\",[]]Bn[\"{{{AHO`{CGbB@Kl}}CN`}{{AHO`{CGbB@Kl}}}}\",[]]A`[\"{BHE`CN`}\",[]]A`[\"{BFLbCN`}\",[]]o[\"{D@hCN`}\",[]]o[\"{IH`CN`}\",[]]A`[\"{AHAlKFl}\",[]]Aa[\"{ABDlAHO`}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Aa[\"{BC@lAB@d}\",[]]A`[\"{KDlBC@j}\",[]]Aa[\"{AHElAILn}\",[]]Aa[\"{BCMfAILn}\",[]]Aa[\"{AHEnAIM`}\",[]]Aa[\"{BCMhAIM`}\",[]]3210Bh[\"{{{AHO`{CGbc}}KCb}B@Mf{A@fIHh}}\",[\"\"]]Bh[\"{{{AHO`{CGbc}}KCb}B@Mh{A@fIHh}}\",[\"\"]]o[\"{{}BCNj}\",[]]o[\"{{}BCNl}\",[]]A`[\"{CNbBIAb}\",[]]Cd[\"{{{AHO`{CMl}}}{{KGh{AOMfc}}}{}}\",[\"FromStr::Err\"]]Cd[\"{{{AHO`{CMl}}}{{KGh{AOInc}}}{}}\",[\"FromStr::Err\"]]o[\"{CN`KI`}\",[]]o[\"{CN`KIb}\",[]]Al[\"{CNb{{KGd{KI`BGIj}}}}\",[]]Al[\"{CNb{{KGd{KIbBGIl}}}}\",[]]Aa[\"{BGGnAOJh}\",[]]Aa[\"{BIEbBB@b}\",[]]Aa[\"{BGH`AOJh}\",[]]Aa[\"{BIEdBB@b}\",[]]Af[\"{{}{{KFl{Ab}}}}\",[]]0000Aj[\"{{{AHO`{AILd}}}DC`}\",[]]Bo[\"{{{AHO`{CGbBFIf}}AB@d}{{AHO`{CGbBFIf}}}}\",[]]Bo[\"{{{AHO`{CGbBFIh}}AB@d}{{AHO`{CGbBFIh}}}}\",[]]Bb[\"{{{A@d{AILf}}}{{A@d{A@`}}}}\",[]]Ba[\"{{{A@d{CNj}}}{{A@d{A@`}}}}\",[]]Ba[\"{{{A@d{IDh}}}{{A@d{A@`}}}}\",[]]Ba[\"{{{A@d{NKl}}}{{A@d{A@`}}}}\",[]]Bb[\"{{{A@d{AILh}}}{{A@d{A@`}}}}\",[]]Ba[\"{{{A@d{CNl}}}{{A@d{A@`}}}}\",[]]Ba[\"{{{A@d{IDj}}}{{A@d{A@`}}}}\",[]]Ba[\"{{{A@d{NKn}}}{{A@d{A@`}}}}\",[]]Aj[\"{{{AHO`{BGJ`}}}DC`}\",[]]Bo[\"{{{AHO`{CGbBH@l}}AB@d}{{AHO`{CGbBH@l}}}}\",[]]Bo[\"{{{AHO`{CGbBH@n}}AB@d}{{AHO`{CGbBH@n}}}}\",[]]Bc[\"{{{AHO`{AIKn}}}{{KFl{KFb}}}}\",[]]Bc[\"{{{AHO`{ABA`}}}{{KFl{KFb}}}}\",[]]Bm[\"{{{AHO`{DA`}}}{{KGd{{KFl{KFb}}AIKd}}}}\",[]]Bc[\"{{{AHO`{AHCb}}}{{KFl{KFb}}}}\",[]]Bc[\"{{{AHO`{ABAb}}}{{KFl{KFb}}}}\",[]]Bc[\"{{{AHO`{AHCd}}}{{KFl{KFb}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Bc[\"{{{AHO`{AHBb}}}{{KFl{KCb}}}}\",[]]Am[\"{{{AHO`{AONh}}KCb}ICj}\",[]]Aj[\"{{{AHO`{AILb}}}NLb}\",[]]Bg[\"{{{AHO`{AJ@`}}}{{KGd{CElB@Ld}}}}\",[]]Bg[\"{{{AHO`{AJ@b}}}{{KGd{CElB@Ll}}}}\",[]]A`[\"{BAOjKFl}\",[]]A`[\"{BAOlKFl}\",[]]A`[\"{AHAlKFl}\",[]]A`[\"{AOIjKFl}\",[]]Ck[\"{{{AHO`{CGbBBMh}}{KFl{{IIh{Oh}}}}}{{AHO`{CGbBBMh}}}}\",[]]Ck[\"{{{AHO`{CGbBBMj}}{KFl{{IIh{Oh}}}}}{{AHO`{CGbBBMj}}}}\",[]]A`[\"{BFBjKFl}\",[]]A`[\"{BFBlKFl}\",[]]Ac[\"{AHO`c{}}\",[\"T\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Ao[\"{c{{KGd{e}}}{}{}}\",[\"U\",\"T\"]]000000000000000000000000000000000000000000000000000000Al[\"{CNb{{KGd{KI`BGIj}}}}\",[]]Al[\"{ICh{{KGd{KI`BGIj}}}}\",[]]Al[\"{KCb{{KGd{KI`BGIj}}}}\",[]]3333333333333333333Aj[\"{AB@d{{KGd{AOL`}}}}\",[]]444444Ai[\"{CNb{{KGd{AHCf}}}}\",[]]55555555555555555555555555555Bc[\"{{{A@b{B@Kf}}}{{KGd{BEJb}}}}\",[]]Aj[\"{B@Kf{{KGd{BEJb}}}}\",[]]77Bc[\"{{{A@b{BABb}}}{{KGd{BEJf}}}}\",[]]Aj[\"{BABb{{KGd{BEJf}}}}\",[]]999999999999999999999Al[\"{ICh{{KGd{KIbBGIl}}}}\",[]]Al[\"{KCb{{KGd{KIbBGIl}}}}\",[]];Al[\"{CNb{{KGd{KIbBGIl}}}}\",[]]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Bm[\"{B@Kf{{KGd{BEJdc}}}{}}\",[\"TryFrom::Error\"]]Cf[\"{{{A@b{B@Kf}}}{{KGd{BEJdc}}}{}}\",[\"TryFrom::Error\"]]Ao[\"{c{{KGd{e}}}{}{}}\",[\"U\",\"T\"]]00Cf[\"{{{A@b{BABb}}}{{KGd{BEJhc}}}{}}\",[\"TryFrom::Error\"]]Bm[\"{BABb{{KGd{BEJhc}}}{}}\",[\"TryFrom::Error\"]]22222222222222222222222Aj[\"{{}{{KGd{c}}}{}}\",[\"U\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Cd[\"{{{ABb{{AHO`{CGbc}}}}{AHO`{CGbNIn}}}DAh{}}\",[\"F\"]]000000000Cc[\"{{{AHO`{BCAd}}AJAj{AHO`{ABDl}}}{{KGf{ICj}}}}\",[]]Bk[\"{{{AHO`{BGOj}}B@Mf}{{KGd{ICjAIMd}}}}\",[]]Bk[\"{{{AHO`{BGOl}}B@Mh}{{KGd{ICjAIMf}}}}\",[]]0Bk[\"{{{AHO`{BIAb}}B@Mh}{{KGd{ICjAIMf}}}}\",[]]Ak[\"{{{AHO`{AILb}}}AAO`}\",[]]Bi[\"{{{AHO`{CGbBACd}}}{{KGd{KI`IF`}}}}\",[]]Bi[\"{{{AHO`{CGbBACd}}}{{KGd{DAlIF`}}}}\",[]]Bi[\"{{{AHO`{CGbBACd}}}{{KGd{A@jIF`}}}}\",[]]Bj[\"{{{AHO`{CGbBACd}}}{{KGd{ABCdIF`}}}}\",[]]Bf[\"{{{AHO`{CGbBACd}}}{{KGj{KIb}}}}\",[]]Bf[\"{{{AHO`{CGbBACd}}}{{KGj{DAn}}}}\",[]]Bf[\"{{{AHO`{CGbBACd}}}{{KGj{A@l}}}}\",[]]Bg[\"{{{AHO`{CGbBACd}}}{{KGj{ABCf}}}}\",[]]Aa[\"{AOInBGIh}\",[]]Bd[\"{{{AHO`{AILf}}}{{A@d{AILf}}}}\",[]]Bc[\"{{{AHO`{CNj}}}{{A@d{AILf}}}}\",[]]Bc[\"{{{AHO`{IDh}}}{{A@d{AILf}}}}\",[]]Bc[\"{{{AHO`{NKl}}}{{A@d{AILf}}}}\",[]]Bd[\"{{{AHO`{AILh}}}{{A@d{AILh}}}}\",[]]Bc[\"{{{AHO`{CNl}}}{{A@d{AILh}}}}\",[]]Bc[\"{{{AHO`{IDj}}}{{A@d{AILh}}}}\",[]]Bc[\"{{{AHO`{NKn}}}{{A@d{AILh}}}}\",[]]A`[\"{ABDnCNb}\",[]]An[\"{{{AHO`{CGbAILd}}}AHBb}\",[]]A`[\"{ABE`CNb}\",[]]C`[\"{{{AHO`{CGbB@Mf}}}{{AHO`{CGb{K@j{Oh}}}}}}\",[]]C`[\"{{{AHO`{CGbB@Mh}}}{{AHO`{CGb{K@j{Oh}}}}}}\",[]]Ah[\"{Oh{{KFl{B@N`}}}}\",[]]Ah[\"{Oh{{KFl{B@Nb}}}}\",[]]Ah[\"{Oh{{KFl{B@Nd}}}}\",[]]A`[\"{ABCdDAl}\",[]]A`[\"{ABCfDAn}\",[]]Bi[\"{AIKn{{KGd{{KC`{AILbBD@f}}AIKn}}}}\",[]]Aa[\"{{}c{}}\",[\"I\"]]o[\"{DAlDC`}\",[]]o[\"{DAnDC`}\",[]]Aj[\"{{{AHO`{AILd}}}DC`}\",[]]21Aj[\"{{{AHO`{ABA`}}}DC`}\",[]]Aj[\"{{{AHO`{ABAb}}}DC`}\",[]]Bn[\"{{{AHO`{CGbBABd}}DC`}{{AHO`{CGbBABd}}}}\",[]]Bn[\"{{{AHO`{CGbBABh}}DC`}{{AHO`{CGbBABh}}}}\",[]]Bm[\"{{{AHO`{NMd}}NK`}{{ABb{{A@d{AIJd}}}}}}\",[]]Bn[\"{{{AHO`{BACb}}NK`}{{ABb{{A@d{AIJd}}}}}}\",[]]Bn[\"{{{AHO`{AOMl}}NK`}{{ABb{{A@d{AIJd}}}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Ak[\"{{{AHO`{AILb}}}ABBf}\",[]]Bo[\"{{{AHO`{AILb}}cAINj}ABBh{{D@b{AHCb}}}}\",[\"\"]]Ce[\"{{{AHO`{CGbAILd}}AHCdAINlNK`}{{KGd{KG`AHEb}}}}\",[]]43Bm[\"{{{AHO`{CGbAILd}}KG`}{{KGd{ICjAIKj}}}}\",[]]E`[\"{{{ABb{{AHO`{CGbBGIb}}}}{AHO`{CGbNIn}}}{{DAh{{KFl{c}}}}}{}}\",[\"Stream::Item\"]]E`[\"{{{ABb{{AHO`{CGbBEFb}}}}{AHO`{CGbNIn}}}{{DAh{{KFl{c}}}}}{}}\",[\"Stream::Item\"]]E`[\"{{{ABb{{AHO`{CGbAINh}}}}{AHO`{CGbNIn}}}{{DAh{{KFl{c}}}}}{}}\",[\"Stream::Item\"]]E`[\"{{{ABb{{AHO`{CGbBGA`}}}}{AHO`{CGbNIn}}}{{DAh{{KFl{c}}}}}{}}\",[\"Stream::Item\"]]E`[\"{{{ABb{{AHO`{CGbAIOd}}}}{AHO`{CGbNIn}}{AHO`{CGbNLn}}}{{DAh{{KGf{ICj}}}}}}\",[]]Ec[\"{{{AHO`{CGbAIOd}}{AHO`{CGbNIn}}{AHO`{CGb{K@j{Oh}}}}}{{DAh{{KGd{KCbAHFb}}}}}}\",[]]Ef[\"{{{ABb{{AHO`{CGbAIOd}}}}{AHO`{CGbNIn}}{AHO`{CGb{K@j{Oh}}}}}{{DAh{{KGf{KCb}}}}}}\",[]]Fg[\"{{{AHO`{CGbBBL`}}{AHO`{CGbNIn}}{AHO`{CGb{K@j{AIMh}}}}{AHO`{CGb{K@j{ABC`}}}}}{{DAh{{KGf{KCb}}}}}}\",[]]Dn[\"{{{ABb{{AHO`{CGbAHFj}}}}{AHO`{ABDl}}{AHO`{CGbNIn}}}{{DAh{{KGf{ICj}}}}}}\",[]]>=Ce[\"{{{AHO`{CGbBBMh}}{A@b{NJj}}}{{AHO`{CGbBBMh}}}}\",[]]Ce[\"{{{AHO`{CGbBBMj}}{A@b{NJl}}}{{AHO`{CGbBBMj}}}}\",[]]Cj[\"{{{AHO`{BABf}}CN`B@Mf{AHO`{{K@j{Oh}}}}}{{IIh{Oh}}}}\",[]]Cj[\"{{{AHO`{BEJf}}CN`B@Mf{AHO`{{K@j{Oh}}}}}{{IIh{Oh}}}}\",[]]Cj[\"{{{AHO`{BABj}}CN`B@Mh{AHO`{{K@j{Oh}}}}}{{IIh{Oh}}}}\",[]]Cj[\"{{{AHO`{BEJh}}CN`B@Mh{AHO`{{K@j{Oh}}}}}{{IIh{Oh}}}}\",[]]Cj[\"{{{AHO`{BHKd}}CN`B@Md{AHO`{{K@j{Oh}}}}}{{IIh{Oh}}}}\",[]]Bb[\"{KCb{{KC`{KCb{KFl{KCb}}}}}}\",[]]000Aj[\"{{{AHO`{AILb}}}KCb}\",[]]A`[\"{AHO`KHl}\",[]]0000000000000000000000000000000000000000000000000000000000000000000Cf[\"{{{AHO`{CGbBABd}}{A@b{BFBn}}}{{AHO`{CGbBABd}}}}\",[]]Cf[\"{{{AHO`{CGbBABh}}{A@b{BFC`}}}{{AHO`{CGbBABh}}}}\",[]]A`[\"{BHE`IGf}\",[]]A`[\"{BABdA@b}\",[]]A`[\"{BABhA@b}\",[]]Aj[\"{{{AHO`{AB@h}}}ICj}\",[]]Ck[\"{{{AHO`{CGbAJ@`}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjAJAl}}}}\",[]]n[\"{{}KCb}\",[]]Ak[\"{{{AHO`{AILb}}}AH@h}\",[]]Ba[\"{{{AHO`{CGbABDn}}ABDn}ICj}\",[]]Ba[\"{{{AHO`{CGbAILn}}AILn}ICj}\",[]]Ba[\"{{{AHO`{CGbBCMf}}AHEl}ICj}\",[]]Ba[\"{{{AHO`{CGbABE`}}ABE`}ICj}\",[]]Ba[\"{{{AHO`{CGbAIM`}}AIM`}ICj}\",[]]Ba[\"{{{AHO`{CGbBCMh}}AHEn}ICj}\",[]]Bn[\"{{{AHO`{CGbBCOl}}DC`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}DC`}{{AHO`{CGbBCOn}}}}\",[]]Bi[\"{{{AHO`{CGb}}}{{AHO`{CGbc}}}{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bg[\"{{{AHO`{AIOd}}}{{KGd{CNbB@Ld}}}}\",[]]Bg[\"{{{AHO`{AIOf}}}{{KGd{CNbB@Ll}}}}\",[]]Bc[\"{{AHO`{AHO`{CGbc}}}ICj{}}\",[\"T\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Cc[\"{{{AHO`{CGbAILd}}NK`KG`KIb}{{KGd{ICjBBLf}}}}\",[]]A`[\"{AHAlDC`}\",[]]B`[\"{{AHO`{AHO`{c}}}DC`{}}\",[\"K\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Aa[\"{BCLhBFKn}\",[]]A`[\"{BEFfKI`}\",[]]Aa[\"{BCLlBFL`}\",[]]A`[\"{BEFjKIb}\",[]]A`[\"{BFAnKIb}\",[]]A`[\"{BCNnKIb}\",[]]o[\"{NMjKIb}\",[]]Aa[\"{BCLhAIMj}\",[]]Aa[\"{BCLlAIMl}\",[]]o[\"{KI`CNb}\",[]]o[\"{KIbCNb}\",[]]Aj[\"{{{AHO`{AILd}}}DC`}\",[]]Aj[\"{{{AHO`{BAOd}}}DC`}\",[]]Bd[\"{{{AHO`{BBL`}}}{{KGf{AJ@f}}}}\",[]]Bd[\"{{{AHO`{AB@h}}}{{KGf{AJ@f}}}}\",[]]A`[\"{AHElCNb}\",[]]A`[\"{BCMfCNb}\",[]]A`[\"{AHEnCNb}\",[]]A`[\"{BCMhCNb}\",[]]Cc[\"{{{AHO`{BCN`}}{AHO`{AIOn}}CNb}{{A@b{AONh}}}}\",[]]Bn[\"{{{AHO`{CGbBCOl}}DC`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}DC`}{{AHO`{CGbBCOn}}}}\",[]]Bg[\"{{{AHO`{AILb}}KFn}{{KFl{AHEl}}}}\",[]]o[\"{KEdABh}\",[]]A`[\"{AHBfA@d}\",[]]Bj[\"{{{AHO`{CGbAILd}}KG`}{{KFl{AHEn}}}}\",[]]A`[\"{AHBhA@d}\",[]]Db[\"{{{ABb{{AHO`{CGbAJ@`}}}}{AHO`{CGbNIn}}}{{DAh{{KGf{ICj}}}}}}\",[]]00Eg[\"{{{ABb{{AHO`{CGbAJ@`}}}}{AHO`{CGbNIn}}{AHO`{{K@j{Oh}}}}}{{DAh{{KGd{KCbAJAl}}}}}}\",[]]Ec[\"{{{ABb{{AHO`{CGbAJ@`}}}}{AHO`{CGbNIn}}{AHO`{{K@j{Oh}}}}}{{DAh{{KGf{KCb}}}}}}\",[]]0Bm[\"{{{AHO`{IHl}}{AHO`{D@n}}}{{KGf{ICj}}}}\",[]]Cd[\"{{{AHO`{CGbAIOd}}KCb}{{KGd{{KFl{ICn}}AHFb}}}}\",[]]Cd[\"{{{AHO`{CGbBGB`}}KCb}{{KGd{{KFl{ID`}}AHFb}}}}\",[]]Cn[\"{{{AHO`{CGbAIOd}}{AHO`{CGb{K@j{Oh}}}}}{{KGd{ICjBC@f}}}}\",[]]Bk[\"{{{AHO`{DA`}}AINj}{{KGd{AINjBFKd}}}}\",[]]Bn[\"{{{AHO`{CGbAINn}}NK`}{{AHO`{CGbAINn}}}}\",[]]Bn[\"{{{AHO`{CGbAIO`}}NK`}{{AHO`{CGbAIO`}}}}\",[]]A`[\"{BC@lAG`}\",[]]o[\"{IHjCNb}\",[]]o[\"{{}AJ@f}\",[]]0o[\"{{}AHO`}\",[]]Bk[\"{{ABA`{A@b{BABd}}}{{KGd{AIKnBCLn}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Bh[\"{{{AHO`{AHAl}}AJ@f}{{KGh{AHFl}}}}\",[]]A`[\"{AHElCMn}\",[]]Aj[\"{{{AHO`{AILd}}}CMn}\",[]]A`[\"{AHEnCMn}\",[]]Ce[\"{{{AHO`{CGbAINn}}{KFl{KHl}}}{{AHO`{CGbAINn}}}}\",[]]Ce[\"{{{AHO`{CGbAIO`}}{KFl{KHl}}}{{AHO`{CGbAIO`}}}}\",[]]Aa[\"{ABDhAJ@f}\",[]]Aa[\"{ABDjAJ@f}\",[]]Aa[\"{ABDlAJ@f}\",[]]Aa[\"{AJ@fAHCb}\",[]]Aa[\"{AJ@fAHCd}\",[]]o[\"{AAjCMn}\",[]]Bn[\"{{{AHO`{CGbBCOl}}CMn}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}CMn}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHAlCMn}\",[]]Bo[\"{{{AHO`{CGbBCOl}}AB@d}{{AHO`{CGbBCOl}}}}\",[]]Bo[\"{{{AHO`{CGbBCOn}}AB@d}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHAlKFl}\",[]]k[\"{{}}\",[]]0000000000Bh[\"{ABA`c{}}\",[\"IntoFuture::IntoFuture\"]]11Aj[\"{{{AHO`{BAOd}}}DC`}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]o[\"{AAjKCb}\",[]]Ao[\"{{AHO`c}{{NKf{c}}}{}}\",[\"S\"]]0C`[\"{{{AHO`{CGbAILf}}NK`CNbDC`{KFl{CNb}}}ICj}\",[]]Bo[\"{{{AHO`{CGbCNj}}NK`CNbDC`{KFl{CNb}}}ICj}\",[]]C`[\"{{{AHO`{CGbAILh}}NK`CNbDC`{KFl{CNb}}}ICj}\",[]]Bo[\"{{{AHO`{CGbCNl}}NK`CNbDC`{KFl{CNb}}}ICj}\",[]]Bk[\"{{{AHO`{CGbIHl}}NK`{AHO`{BCN`}}}ICj}\",[]]A`[\"{BFBjKFl}\",[]]A`[\"{BFBlKFl}\",[]]Bn[\"{{{AHO`{KI`}}{AHO`{KI`}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{KFn}}{AHO`{KFn}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{AOL`}}{AHO`{AOL`}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{B@Mf}}{AHO`{B@Mf}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{DAl}}{AHO`{DAl}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{A@j}}{AHO`{A@j}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{ABCd}}{AHO`{ABCd}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{KIb}}{AHO`{KIb}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{BFDf}}{AHO`{BFDf}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{KG`}}{AHO`{KG`}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{AHFd}}{AHO`{AHFd}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{BBBl}}{AHO`{BBBl}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{AJAn}}{AHO`{AJAn}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{AOLb}}{AHO`{AOLb}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{BEGb}}{AHO`{BEGb}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{BFCl}}{AHO`{BFCl}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{B@Mh}}{AHO`{B@Mh}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{DAn}}{AHO`{DAn}}}{{KFl{ABBj}}}}\",[]]Bn[\"{{{AHO`{A@l}}{AHO`{A@l}}}{{KFl{ABBj}}}}\",[]]C`[\"{{{AHO`{ABCf}}{AHO`{ABCf}}}{{KFl{ABBj}}}}\",[]]Ak[\"{{{AHO`{AILb}}}AINh}\",[]]Bk[\"{{{AHO`{AILd}}KG`}{{KGd{AINlAIKj}}}}\",[]]Cc[\"{{{AHO`{CGbAIOd}}KCb}{{KGd{{ABh{Oh}}BC@h}}}}\",[]]Bb[\"{{{AHO`{CGbAILd}}ABCf}AIOf}\",[]]Aj[\"{{{AHO`{AHCl}}}KCb}\",[]]Aj[\"{{{AHO`{AHCn}}}KCb}\",[]]Bb[\"{{{AHO`{CGbAILd}}ABCf}AJ@b}\",[]]Bn[\"{{{AHO`{CGbBCOl}}CNb}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}CNb}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHAlKFl}\",[]]A`[\"{BAOjKFl}\",[]]A`[\"{BAOlKFl}\",[]]Cf[\"{{{AHO`{CGbBABd}}{A@b{AJ@n}}}{{AHO`{CGbBABd}}}}\",[]]Cf[\"{{{AHO`{CGbBABh}}{A@b{AJA`}}}{{AHO`{CGbBABh}}}}\",[]]Cf[\"{{{AHO`{CGbB@Kh}}{A@b{AJAf}}}{{AHO`{CGbB@Kh}}}}\",[]]Cf[\"{{{AHO`{CGbB@Kl}}{A@b{AJAh}}}{{AHO`{CGbB@Kl}}}}\",[]]o[\"{AAjCNb}\",[]]Bn[\"{{{AHO`{CGbBFIf}}CMn}{{AHO`{CGbBFIf}}}}\",[]]Bn[\"{{{AHO`{CGbBFIh}}CMn}{{AHO`{CGbBFIh}}}}\",[]]Ak[\"{{{AHO`{AILb}}}BGJ`}\",[]]Aj[\"{{{AHO`{DA`}}}BCAf}\",[]]Aj[\"{{{A@b{BABf}}}BABd}\",[]]Aj[\"{{{A@b{BABj}}}BABh}\",[]]Aj[\"{{BCNjc}BCNjABCn}\",[\"\"]]Aj[\"{{BCNlc}BCNlABCn}\",[\"\"]]Bm[\"{{{AHO`{CGbAJ@`}}ICn}{{KGd{ICjAJAl}}}}\",[]]n[\"{{}KFb}\",[]]o[\"{NIlNIl}\",[]]0000Bd[\"{{{AHO`{BGOj}}}{{KFl{AB@d}}}}\",[]]Bd[\"{{{AHO`{BGOl}}}{{KFl{AB@d}}}}\",[]]Bd[\"{{{AHO`{BIAb}}}{{KFl{AB@d}}}}\",[]]Bd[\"{{{AHO`{BIAl}}}{{KFl{AB@d}}}}\",[]]Bd[\"{{{AHO`{AILb}}}{{KFl{BCLn}}}}\",[]]Ck[\"{{{AHO`{AB@h}}B@KhAJ@f{AHO`{CMl}}}{{KGd{AIKnB@M`}}}}\",[]]Ak[\"{{{AHO`{BAAl}}}AB@d}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]An[\"{{AIObDC`}{{KGh{KCb}}}}\",[]]Cl[\"{{{AHO`{NMf}}}{{KFl{{KC`{{A@d{AHCl}}{A@d{AHDj}}}}}}}}\",[]]Cm[\"{{{AHO`{AJAb}}}{{KFl{{KC`{{A@d{AHCl}}{A@d{AHDj}}}}}}}}\",[]]Cl[\"{{{AHO`{NMh}}}{{KFl{{KC`{{A@d{AHCn}}{A@d{AHDl}}}}}}}}\",[]]Cm[\"{{{AHO`{AJAd}}}{{KFl{{KC`{{A@d{AHCn}}{A@d{AHDl}}}}}}}}\",[]]A`[\"{BGGnABh}\",[]]A`[\"{BIEbABh}\",[]]A`[\"{BGH`ABh}\",[]]A`[\"{BIEdABh}\",[]]An[\"{{{AHO`{CGbBGOj}}}B@Mf}\",[]]An[\"{{{AHO`{CGbBGOl}}}B@Mh}\",[]]An[\"{{{AHO`{CGbBIAb}}}B@Mh}\",[]]An[\"{{{AHO`{CGbBIAl}}}B@Mh}\",[]]Ak[\"{{{AHO`{BCAd}}}B@Oj}\",[]]Ba[\"{{{AHO`{CGbAILd}}BCMd}ICj}\",[]]Bo[\"{{{AHO`{CGbAB@j}}BEGbBAOd}{{KFl{BCMd}}}}\",[]]B`[\"{{{AHO`{NMf}}B@MfDAl}D@d}\",[]]Bn[\"{{{AHO`{BABf}}CN`B@Mf}{{KGd{D@dBFLf}}}}\",[]]Ba[\"{{{AHO`{AJAb}}B@MfDAl}D@d}\",[]]Bn[\"{{{AHO`{BEJf}}CN`B@Mf}{{KGd{D@dBFLf}}}}\",[]]B`[\"{{{AHO`{NMh}}B@MhDAn}D@f}\",[]]Bn[\"{{{AHO`{BABj}}CN`B@Mh}{{KGd{D@fBFLl}}}}\",[]]Ba[\"{{{AHO`{AJAd}}B@MhDAn}D@f}\",[]]Bn[\"{{{AHO`{BEJh}}CN`B@Mh}{{KGd{D@fBFLl}}}}\",[]]Bn[\"{{{AHO`{BHKd}}CN`B@Md}{{KGd{D@fBFLl}}}}\",[]]Aj[\"{{}{{NM`{c}}}{}}\",[\"T\"]]0A`[\"{AHElCNb}\",[]]A`[\"{BCMfCNb}\",[]]A`[\"{AHEnCNb}\",[]]A`[\"{BCMhCNb}\",[]]Bn[\"{{{AHO`{CGbBABd}}KCb}{{AHO`{CGbBABd}}}}\",[]]Bn[\"{{{AHO`{CGbBABh}}KCb}{{AHO`{CGbBABh}}}}\",[]]Aj[\"{{{AHO`{BBL`}}}DC`}\",[]]0Aj[\"{{{AHO`{BCAd}}}DC`}\",[]]Bg[\"{{{AHO`{DA`}}}{{KGd{AHCbAIKd}}}}\",[]]Bk[\"{{{AHO`{AILd}}KG`}{{KGd{AHCdAIKj}}}}\",[]]Cc[\"{{{AHO`{BCN`}}{AHO`{AIOb}}CNb}{{A@b{AONh}}}}\",[]]Ak[\"{{{AHO`{ABA`}}}B@Mf}\",[]]Ak[\"{{{AHO`{ABAb}}}B@Mh}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Bf[\"{{{AHO`{CGbAILd}}}{{KFl{NK`}}}}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{A@b{AOLn}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{A@b{AOM`}}}{{AHO`{CGbBCOn}}}}\",[]]43A`[\"{AAjAOMf}\",[]]A`[\"{ABDhKFl}\",[]]A`[\"{ABDjKFl}\",[]]A`[\"{ABDlKFl}\",[]]A`[\"{BFBjKFl}\",[]]A`[\"{BFBlKFl}\",[]]Ai[\"{{{AHO`{NNf}}}KCb}\",[]]Bo[\"{{{AHO`{CGbBIAb}}AB@d}{{AHO`{CGbBIAb}}}}\",[]]Bo[\"{{{AHO`{CGbBIAl}}AB@d}{{AHO`{CGbBIAl}}}}\",[]]Bj[\"{{{AHO`{AJ@`}}CEl}{{KGd{ICjB@Ld}}}}\",[]]Bm[\"{{{AHO`{CGbAJ@b}}CEl}{{KGd{ICjB@Ll}}}}\",[]]?>A`[\"{IHjAONf}\",[]]A`[\"{AOJ`IHj}\",[]]Bj[\"{{{A@b{B@Kf}}IHn}{{KGd{BEJbBGHn}}}}\",[]]Bj[\"{{{A@b{BABb}}IHn}{{KGd{BEJfBGHn}}}}\",[]]Bj[\"{{{A@b{B@Kf}}IHn}{{KGd{BEJdBGI`}}}}\",[]]Bj[\"{{{A@b{BABb}}IHn}{{KGd{BEJhBGI`}}}}\",[]]Dj[\"{{{AHO`{CGbAJ@b}}{AHO`{CGb{AHO`{CGb{K@j{ICn}}}}}}}{{KGd{KCbAJAn}}}}\",[]]Aj[\"{{{AHO`{AILd}}}DC`}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]A`[\"{AHAlDC`}\",[]]Cd[\"{{{AHO`{CGbBBMh}}{A@b{A`}}}{{AHO`{CGbBBMh}}}}\",[]]Cd[\"{{{AHO`{CGbBBMj}}{A@b{A`}}}{{AHO`{CGbBBMj}}}}\",[]]Bk[\"{{{AHO`{BBL`}}}{{ABb{{A@d{AHFj}}}}}}\",[]]o[\"{AAjCNb}\",[]]Aa[\"{AONfAHD`}\",[]]76Aa[\"{AIOjABA`}\",[]]Aa[\"{AIOlABAb}\",[]]Cf[\"{{{AHO`{CGbBFGh}}{KFl{AB@d}}}{{AHO`{CGbBFGh}}}}\",[]]Cf[\"{{{AHO`{CGbBFGj}}{KFl{AB@d}}}{{AHO`{CGbBFGj}}}}\",[]]9;:An[\"{{{AHO`{AONh}}AB@d}ICj}\",[]]B`[\"{{{AHO`{CGbAILf}}CMn}ICj}\",[]]Ao[\"{{{AHO`{CGbCNj}}CMn}ICj}\",[]]Ao[\"{{{AHO`{CGbIDh}}CMn}ICj}\",[]]Ao[\"{{{AHO`{CGbNKl}}CMn}ICj}\",[]]B`[\"{{{AHO`{CGbAILh}}CMn}ICj}\",[]]Ao[\"{{{AHO`{CGbCNl}}CMn}ICj}\",[]]Ao[\"{{{AHO`{CGbIDj}}CMn}ICj}\",[]]Ao[\"{{{AHO`{CGbNKn}}CMn}ICj}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]10Bj[\"{{{AHO`{AILb}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bi[\"{{{AHO`{NMf}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bj[\"{{{AHO`{AJAb}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bi[\"{{{AHO`{NMh}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bj[\"{{{AHO`{AJAd}}}{{KFl{{A@d{A@`}}}}}}\",[]]Db[\"{{{ABb{{AHO`{CGbAJ@`}}}}{AHO`{CGbNIn}}}{{DAh{{KGf{ICj}}}}}}\",[]]Db[\"{{{AHO`{CGbAILd}}NK`B@Oj{AHO`{CGb{ABh{Oh}}}}}{{KFl{ABDj}}}}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{AHO`{CMl}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{AHO`{CMl}}}{{AHO`{CGbBCOn}}}}\",[]]Ak[\"{{{AHO`{AILb}}}BAA`}\",[]]Bj[\"{{{AHO`{AILb}}ICn}{{KGd{ICjBFDd}}}}\",[]]Bn[\"{{{AHO`{CGbBCOl}}DC`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}DC`}{{AHO`{CGbBCOn}}}}\",[]]Ai[\"{{{AHO`{NJj}}}KCb}\",[]]Ai[\"{{{AHO`{NJl}}}KCb}\",[]]Dh[\"{{{AHO`{B@Kj}}CN`{AHO`{CMl}}{AHO`{BGAn}}}{{KGd{{A@d{NMf}}B@M`}}}}\",[]]Cb[\"{{{AHO`{BABf}}CN`{AHO`{BGAn}}}{{A@d{NMf}}}}\",[]]Dh[\"{{{AHO`{BEJb}}CN`{AHO`{CMl}}{AHO`{BGAn}}}{{KGd{{A@d{NMf}}B@M`}}}}\",[]]Cb[\"{{{AHO`{BEJf}}CN`{AHO`{BGAn}}}{{A@d{NMf}}}}\",[]]Dh[\"{{{AHO`{B@Kn}}CN`{AHO`{CMl}}{AHO`{BGAn}}}{{KGd{{A@d{NMh}}B@Mb}}}}\",[]]Cb[\"{{{AHO`{BABj}}CN`{AHO`{BGAn}}}{{A@d{NMh}}}}\",[]]Dh[\"{{{AHO`{BEJd}}CN`{AHO`{CMl}}{AHO`{BGAn}}}{{KGd{{A@d{NMh}}B@Mb}}}}\",[]]Cb[\"{{{AHO`{BEJh}}CN`{AHO`{BGAn}}}{{A@d{NMh}}}}\",[]]Dh[\"{{{AHO`{BHKb}}CN`{AHO`{CMl}}{AHO`{BGAn}}}{{KGd{{A@d{NMh}}B@Ln}}}}\",[]]Cb[\"{{{AHO`{BHKd}}CN`{AHO`{BGAn}}}{{A@d{NMh}}}}\",[]]Dd[\"{{{ABb{{AHO`{CGbc}}}}{AHO`{CGbNIn}}}{{DAh{{KFl{KGd}}}}}{}}\",[\"S\"]]0Bn[\"{{AJ@fBBLbAAj}{{KGh{{KC`{AB@fAIL`}}}}}}\",[]]Bd[\"{{{AHO`{AILd}}}{{AHO`{NMh}}}}\",[]]A`[\"{AOJ`IHj}\",[]]Cb[\"{{{AHO`{AOLn}}DAlAJ@fB@MfNK`}{{KFl{AINn}}}}\",[]]Cb[\"{{{AHO`{BCNj}}DAlAJ@fB@MfNK`}{{KFl{AINn}}}}\",[]]Cb[\"{{{AHO`{AOM`}}DAnAJ@fB@MhNK`}{{KFl{AIO`}}}}\",[]]Cb[\"{{{AHO`{BCNl}}DAnAJ@fB@MhNK`}{{KFl{AIO`}}}}\",[]]B`[\"{{{AHO`{CGbAILd}}NK`}ICj}\",[]]Ca[\"{{{AHO`{CGbAIKn}}}{{KGd{{A@d{A@`}}BCLn}}}}\",[]]Bj[\"{{{AHO`{AILb}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bi[\"{{{AHO`{NMf}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bj[\"{{{AHO`{AJAb}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bi[\"{{{AHO`{NMh}}}{{KFl{{A@d{A@`}}}}}}\",[]]Bj[\"{{{AHO`{AJAd}}}{{KFl{{A@d{A@`}}}}}}\",[]]o[\"{AILnOh}\",[]]o[\"{AIM`Oh}\",[]]Ai[\"{{{AHO`{CMl}}}DC`}\",[]]Aj[\"{{{AHO`{AILf}}}CNb}\",[]]Ai[\"{{{AHO`{CNj}}}CNb}\",[]]Bn[\"{{{AHO`{CGbAIJl}}CNb}{{AHO`{CGbAIJl}}}}\",[]]Ai[\"{{{AHO`{IDh}}}CNb}\",[]]Bn[\"{{{AHO`{CGbAOJn}}CNb}{{AHO`{CGbAOJn}}}}\",[]]Ai[\"{{{AHO`{NKl}}}CNb}\",[]]Bn[\"{{{AHO`{CGbBBAl}}CNb}{{AHO`{CGbBBAl}}}}\",[]]Aj[\"{{{AHO`{AILh}}}CNb}\",[]]Ai[\"{{{AHO`{CNl}}}CNb}\",[]]Bn[\"{{{AHO`{CGbAIJn}}CNb}{{AHO`{CGbAIJn}}}}\",[]]Ai[\"{{{AHO`{IDj}}}CNb}\",[]]Bn[\"{{{AHO`{CGbAOK`}}CNb}{{AHO`{CGbAOK`}}}}\",[]]Ai[\"{{{AHO`{NKn}}}CNb}\",[]]Bn[\"{{{AHO`{CGbBBAn}}CNb}{{AHO`{CGbBBAn}}}}\",[]]Aa[\"{AIOdBGB`}\",[]]Ai[\"{{{AHO`{NMf}}}DC`}\",[]]Aj[\"{{{AHO`{AJAb}}}DC`}\",[]]Aj[\"{{{AHO`{AILd}}}DC`}\",[]]Ai[\"{{{AHO`{NMh}}}DC`}\",[]]Aj[\"{{{AHO`{AJAd}}}DC`}\",[]]Co[\"{{{AHO`{NMf}}B@Mf{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}DC`}\",[]]D`[\"{{{AHO`{AJAb}}B@Mf{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}DC`}\",[]]Co[\"{{{AHO`{NMh}}B@Mh{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}DC`}\",[]]D`[\"{{{AHO`{AJAd}}B@Mh{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}DC`}\",[]]Bn[\"{{{AHO`{CGbBFIf}}CMn}{{AHO`{CGbBFIf}}}}\",[]]Bn[\"{{{AHO`{CGbBFIh}}CMn}{{AHO`{CGbBFIh}}}}\",[]]Cd[\"{{{AHO`{CGbNMf}}}{{KFl{{NKb{{A@d{AHDj}}}}}}}}\",[]]Ce[\"{{{AHO`{CGbAJAb}}}{{KFl{{NKb{{A@d{AHDj}}}}}}}}\",[]]Cd[\"{{{AHO`{CGbNMh}}}{{KFl{{NKd{{A@d{AHDl}}}}}}}}\",[]]Ce[\"{{{AHO`{CGbAJAd}}}{{KFl{{NKd{{A@d{AHDl}}}}}}}}\",[]]Bf[\"{{{AHO`{CGbAILf}}CMnCNbNK`}ICj}\",[]]Be[\"{{{AHO`{CGbCNj}}CMnCNbNK`}ICj}\",[]]Bf[\"{{{AHO`{CGbAILh}}CMnCNbNK`}ICj}\",[]]Be[\"{{{AHO`{CGbCNl}}CMnCNbNK`}ICj}\",[]]Bf[\"{{{AHO`{CGbAILf}}NK`CMnCNb}ICj}\",[]]Be[\"{{{AHO`{CGbCNj}}NK`CMnCNb}ICj}\",[]]Bf[\"{{{AHO`{CGbAILh}}NK`CMnCNb}ICj}\",[]]Be[\"{{{AHO`{CGbCNl}}NK`CMnCNb}ICj}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]De[\"{{{AHO`{CGbBCOl}}c{AHO`{CMl}}}{{AHO`{CGbBCOl}}}{{ICl{D@n}}}}\",[\"\"]]De[\"{{{AHO`{CGbBCOn}}c{AHO`{CMl}}}{{AHO`{CGbBCOn}}}{{ICl{D@n}}}}\",[\"\"]]Ci[\"{{{AHO`{CGbNMf}}{AHO`{{K@j{Oh}}}}}{{KGd{DC`IEj}}}}\",[]]Cj[\"{{{AHO`{CGbAJAb}}{AHO`{{K@j{Oh}}}}}{{KGd{DC`IEj}}}}\",[]]Cj[\"{{{AHO`{CGbNMh}}{AHO`{{K@j{Oh}}}}}{{KGd{DC`BCAb}}}}\",[]]Ck[\"{{{AHO`{CGbAJAd}}{AHO`{{K@j{Oh}}}}}{{KGd{DC`BCAb}}}}\",[]]o[\"{AAjDC`}\",[]]Bn[\"{{{AHO`{CGbBCOl}}KI`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}KIb}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHAlKFl}\",[]]Ca[\"{{{AHO`{CGbAIOd}}}{{KGd{{KFl{KI`}}AIOh}}}}\",[]]Ca[\"{{{AHO`{CGbBGB`}}}{{KGd{{KFl{KI`}}AIOh}}}}\",[]]Ca[\"{{{AHO`{CGbAIOf}}}{{KGd{{KFl{KIb}}B@Ll}}}}\",[]]Ak[\"{{{AHO`{AIKn}}}AJ@f}\",[]]Ak[\"{{{AHO`{ABA`}}}AJ@f}\",[]]Bg[\"{{{AHO`{DA`}}}{{KGd{AJ@fAIKd}}}}\",[]]Ak[\"{{{AHO`{ABAb}}}AJ@f}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]B`[\"{{KI`c}KI`{{D@b{KI`}}}}\",[\"\"]]B`[\"{{KFnc}KFn{{D@b{KFn}}}}\",[\"\"]]B`[\"{{KIbc}KIb{{D@b{KIb}}}}\",[\"\"]]B`[\"{{KG`c}KG`{{D@b{KG`}}}}\",[\"\"]]20Ae[\"{{AB@dCNb}AG`}\",[]]Bn[\"{{{AHO`{CGbBCOl}}AFn}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}AFn}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{IHjAB@d}\",[]]B`[\"{{}{{AHO`{{K@j{BG@n}}}}}}\",[]]An[\"{{AHO`{ONf{CGbOh}}}ICj}\",[]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000An[\"{{}{{KFl{{A@b{NMd}}}}}}\",[]]Bn[\"{{{AHO`{CGbBBMh}}DC`}{{AHO`{CGbBBMh}}}}\",[]]Bn[\"{{{AHO`{CGbBBMj}}DC`}{{AHO`{CGbBBMj}}}}\",[]]Aj[\"{{{AHO`{AHDj}}}CNb}\",[]]Aj[\"{{{AHO`{AHDl}}}CNb}\",[]]A`[\"{ABC`KFl}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]10Bf[\"{{{AHO`{CMl}}}{{KGh{CNbBBB`}}}}\",[]]Bn[\"{{{AHO`{AB@h}}{A@d{BBL`}}}{{KGf{ICj}}}}\",[]]Ai[\"{AIL`{{KGh{ICj}}}}\",[]]Cj[\"{{{AHO`{NMb}}BBLbBBBj{AHO`{AAj}}}{{KC`{AJ@fAB@f}}}}\",[]]Cb[\"{{{AHO`{CGbAILd}}KG`AINl}{{KGd{AINlBFKf}}}}\",[]]Am[\"{{{AHO`{AILb}}CNb}ICj}\",[]]B`[\"{{{AHO`{CGbAILd}}CNb}ICj}\",[]]A`[\"{BC@jDC`}\",[]]B`[\"{{{AHO`{CGbIHj}}BC@l}ICj}\",[]]Aa[\"{AONfAHD`}\",[]]Bn[\"{{{AHO`{NMd}}AHFl}{{KGf{{A@d{BBL`}}}}}}\",[]]Bo[\"{{{AHO`{BACb}}AHFl}{{KGf{{A@d{BBL`}}}}}}\",[]]Bo[\"{{{AHO`{AOMl}}AHFl}{{KGf{{A@d{BBL`}}}}}}\",[]]Ci[\"{{{AHO`{CGbNMf}}{AHO`{CGb{ABh{Oh}}}}}{{KFl{D@d}}}}\",[]]Cj[\"{{{AHO`{CGbAJAb}}{AHO`{CGb{ABh{Oh}}}}}{{KFl{D@d}}}}\",[]]Ci[\"{{{AHO`{CGbNMh}}{AHO`{CGb{ABh{Oh}}}}}{{KFl{D@f}}}}\",[]]Cj[\"{{{AHO`{CGbAJAd}}{AHO`{CGb{ABh{Oh}}}}}{{KFl{D@f}}}}\",[]]Cb[\"{{{AHO`{ABD`}}IChAJ@lAB@d}{{KGd{ICjBCOf}}}}\",[]]Cb[\"{{{AHO`{BANd}}IChAJ@lAB@d}{{KGd{ICjBCOf}}}}\",[]]Cb[\"{{{AHO`{B@Ol}}IChAJ@lAB@d}{{KGd{ICjBCOf}}}}\",[]]Cb[\"{{{AHO`{ABDb}}IChAJ@lAB@d}{{KGd{ICjBCOh}}}}\",[]]Cb[\"{{{AHO`{BANf}}IChAJ@lAB@d}{{KGd{ICjBCOh}}}}\",[]]Cb[\"{{{AHO`{B@On}}IChAJ@lAB@d}{{KGd{ICjBCOh}}}}\",[]]Al[\"{{}{{KGd{B@KdBGI`}}}}\",[]]Bn[\"{{{AHO`{AILb}}KFn}{{KFl{{A@d{AILf}}}}}}\",[]]Bo[\"{{{AHO`{AILd}}KG`}{{KFl{{AHO`{AILh}}}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Aj[\"{{{AHO`{AILb}}}ICj}\",[]]Am[\"{{{AHO`{CGbAILd}}}ICj}\",[]]Bf[\"{{{AHO`{AIJb}}}{{KGd{AAjIEn}}}}\",[]]Bf[\"{{{AHO`{AIJb}}}{{KGd{A@hIEn}}}}\",[]]Bg[\"{{{AHO`{AIJb}}}{{KGd{AAOfIEn}}}}\",[]]Bg[\"{{{AHO`{AIJb}}}{{KGd{AHAlIEn}}}}\",[]]Bf[\"{{{AHO`{AIJb}}}{{KGd{AAlIEn}}}}\",[]]Bf[\"{{{AHO`{AIJb}}}{{KGd{AAnIEn}}}}\",[]]Ak[\"{{{AHO`{BCAd}}}B@Oj}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{KFl{AOL`}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{KFl{AOLb}}}{{AHO`{CGbBCOn}}}}\",[]]<;Aj[\"{{{AHO`{AB@h}}}KCb}\",[]]Aj[\"{{{AHO`{AB@j}}}KCb}\",[]]Bo[\"{{{AHO`{AILb}}cAINj}ABBh{{D@b{AHCb}}}}\",[\"\"]]Co[\"{{{AHO`{CGbAILd}}AHCdAINlNK`}{{KGd{{KC`{KG`DC`}}AHEb}}}}\",[]]Bn[\"{{{AHO`{CGbBCOl}}CN`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}CN`}{{AHO`{CGbBCOn}}}}\",[]]Df[\"{{{AHO`{CGbAIOd}}{AHO`{CGb{K@j{ICn}}}}}{{KGd{{KFl{KCb}}AHFb}}}}\",[]]Ai[\"{AIL`{{KGh{ICj}}}}\",[]]Bg[\"{{{AHO`{BCAd}}AJAj}{{KGf{KCb}}}}\",[]]A`[\"{AHAlCNb}\",[]]10Aj[\"{{{AHO`{AAj}}}BCOj}\",[]]Cf[\"{{{AHO`{CGbBABd}}{A@b{BCOl}}}{{AHO`{CGbBABd}}}}\",[]]Cf[\"{{{AHO`{CGbB@Kh}}{A@b{BCOl}}}{{AHO`{CGbB@Kh}}}}\",[]]Cf[\"{{{AHO`{CGbBABh}}{A@b{BCOn}}}{{AHO`{CGbBABh}}}}\",[]]Cf[\"{{{AHO`{CGbB@Kl}}{A@b{BCOn}}}{{AHO`{CGbB@Kl}}}}\",[]]Aa[\"{BABdBH@l}\",[]]Aa[\"{BABhBH@n}\",[]]Bj[\"{{{ABh{BBLb}}BBBj}{{KGd{BABdIFb}}}}\",[]]Bj[\"{{{ABh{BBLb}}BBBj}{{KGd{BABhIFd}}}}\",[]]Co[\"{{{AHO`{CGbAJ@`}}{AHO`{CGb{K@j{ICn}}}}}{{KGd{ICjAJAl}}}}\",[]]A`[\"{AHElCNb}\",[]]A`[\"{AHEnCNb}\",[]]A`[\"{BFBjCNb}\",[]]A`[\"{BFBlCNb}\",[]]Bc[\"{{{AHO`{AILb}}}{{KFl{KCb}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]10Aj[\"{{{AHO`{AHBb}}}KCb}\",[]]Be[\"{{{AHO`{AB@h}}{KFl{BABd}}}ICj}\",[]]Bo[\"{{{AHO`{CGbAB@j}}{KFl{{A@b{BABh}}}}}ICj}\",[]]Bd[\"{{{AHO`{BG@n}}}{{KFl{BBBh}}}}\",[]]Aj[\"{{{AHO`{AB@h}}}ICj}\",[]]Dj[\"{{{AHO`{CGbAJ@`}}{AHO`{CGb{AHO`{CGb{K@j{ICn}}}}}}}{{KGd{KCbAJAl}}}}\",[]]o[\"{{}AHO`}\",[]]n[\"{{}NIl}\",[]]0000Bn[\"{{{AHO`{CGbBCOl}}KCb}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}KCb}{{AHO`{CGbBCOn}}}}\",[]]o[\"{CNbKI`}\",[]]o[\"{CNbKIb}\",[]]A`[\"{BAOfCNb}\",[]]Bf[\"{{{AHO`{BGJ`}}{AHO`{BGJ`}}}DC`}\",[]]Bo[\"{{{AHO`{CGbBBMh}}AB@d}{{AHO`{CGbBBMh}}}}\",[]]Bo[\"{{{AHO`{CGbBBMj}}AB@d}{{AHO`{CGbBBMj}}}}\",[]]Ae[\"{{KCbCNb}BANd}\",[]]Ae[\"{{KCbCNb}BANf}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]7Bg[\"{{{AHO`{AILd}}KG`}{{KFl{AINl}}}}\",[]]An[\"{{{AHO`{AILb}}ICn}BAAn}\",[]]Bm[\"{{{AHO`{CGbBEJb}}{ABh{{ABh{Oh}}}}}ICj}\",[]]Bm[\"{{{AHO`{CGbBEJf}}{ABh{{ABh{Oh}}}}}ICj}\",[]]Bm[\"{{{AHO`{CGbBEJd}}{ABh{{ABh{Oh}}}}}ICj}\",[]]Bm[\"{{{AHO`{CGbBEJh}}{ABh{{ABh{Oh}}}}}ICj}\",[]]Am[\"{{{AHO`{AILb}}KI`}ICj}\",[]]B`[\"{{{AHO`{CGbAILd}}KIb}ICj}\",[]]Ce[\"{{{AHO`{CGbBBMh}}{ABh{CN`}}}{{AHO`{CGbBBMh}}}}\",[]]Ce[\"{{{AHO`{CGbBBMj}}{ABh{CN`}}}{{AHO`{CGbBBMj}}}}\",[]]Fg[\"{{{AHO`{BHEb}}{AHO`{BBLb}}{AHO`{{K@j{BBLb}}}}{AHO`{AJ@d}}{AHO`{{K@j{Oh}}}}ABEb}{{KGd{BFK`IEl}}}}\",[]]Af[\"{{BCNjNK`}BCNj}\",[]]Af[\"{{BCNlNK`}BCNl}\",[]]A`[\"{BAOfCNb}\",[]]Al[\"{BACd{{KGd{DAlIF`}}}}\",[]]Al[\"{BACd{{KGd{A@jIF`}}}}\",[]]Ai[\"{BACd{{KGj{DAn}}}}\",[]]Ai[\"{BACd{{KGj{A@l}}}}\",[]]o[\"{NIlNIl}\",[]]Bo[\"{{{AHO`{CGbBFIf}}AB@d}{{AHO`{CGbBFIf}}}}\",[]]Bo[\"{{{AHO`{CGbBFIh}}AB@d}{{AHO`{CGbBFIh}}}}\",[]]Bb[\"{{{AHO`{NMf}}}{{KFl{DC`}}}}\",[]]Bc[\"{{{AHO`{AJAb}}}{{KFl{DC`}}}}\",[]]Bb[\"{{{AHO`{NMh}}}{{KFl{DC`}}}}\",[]]Bc[\"{{{AHO`{AJAd}}}{{KFl{DC`}}}}\",[]]Bg[\"{{{AHO`{AILb}}}{{KGd{ICjBCLn}}}}\",[]]Ba[\"{{{AHO`{BFCf}}KFnAHCb}DC`}\",[]]Ba[\"{{{AHO`{BFCh}}KG`AHCd}DC`}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{KFl{AB@d}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{KFl{AB@d}}}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHElCNb}\",[]]A`[\"{AHEnCNb}\",[]]Bo[\"{{{AHO`{CGbAILf}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bn[\"{{{AHO`{CGbCNj}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bn[\"{{{AHO`{CGbIDh}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bn[\"{{{AHO`{CGbNKl}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bo[\"{{{AHO`{CGbAILh}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bn[\"{{{AHO`{CGbCNl}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bn[\"{{{AHO`{CGbIDj}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]Bn[\"{{{AHO`{CGbNKn}}NK`NK`DC`DC`CNbCNb}ICj}\",[]]A`[\"{BAOfCNb}\",[]]Al[\"{{{AHO`{NNf}}A@l}CNb}\",[]]Bi[\"{{{AHO`{CGbAIOn}}CNb}{{KGh{ICj}}}}\",[]]<;A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]10Cf[\"{{{AHO`{CGbBCOl}}{KFl{BFGh}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{KFl{BFGj}}}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHElCNb}\",[]]A`[\"{AHEnCNb}\",[]]Bi[\"{{{AHO`{CGbAIJb}}}{{KGd{AAjIEn}}}}\",[]]Bi[\"{{{AHO`{CGbAIJb}}}{{KGd{A@hIEn}}}}\",[]]Bj[\"{{{AHO`{CGbAIJb}}}{{KGd{AAOfIEn}}}}\",[]]Bj[\"{{{AHO`{CGbAIJb}}}{{KGd{AHAlIEn}}}}\",[]]Bi[\"{{{AHO`{CGbAIJb}}}{{KGd{AAlIEn}}}}\",[]]Bi[\"{{{AHO`{CGbAIJb}}}{{KGd{AAnIEn}}}}\",[]]Cd[\"{{{A@b{AIL`}}CNbDC`}{{KGh{{KC`{BC@lBC@l}}}}}}\",[]]Bn[\"{{{AHO`{CGbBABd}}CNb}{{AHO`{CGbBABd}}}}\",[]]Bn[\"{{{AHO`{CGbBABh}}CNb}{{AHO`{CGbBABh}}}}\",[]]Aj[\"{{{AHO`{AILb}}}DC`}\",[]]Ak[\"{{{AHO`{BBL`}}}B@Oj}\",[]]0Ci[\"{{{AHO`{CGbBBMh}}CMn}{{KGd{{AHO`{CGbBBMh}}AOJd}}}}\",[]]Ci[\"{{{AHO`{CGbBBMj}}CMn}{{KGd{{AHO`{CGbBBMj}}AOJf}}}}\",[]]A`[\"{AHAlCMn}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{KFl{BFIf}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{KFl{BFIh}}}{{AHO`{CGbBCOn}}}}\",[]]Bg[\"{{{AHO`{CGbAILd}}}{{KFl{BAOd}}}}\",[]]Cf[\"{{{AHO`{CGbBABd}}{KFl{BABl}}}{{AHO`{CGbBABd}}}}\",[]]Cf[\"{{{AHO`{CGbBABh}}{KFl{BABl}}}{{AHO`{CGbBABh}}}}\",[]]Cf[\"{{{AHO`{CGbBABd}}{KFl{BABn}}}{{AHO`{CGbBABd}}}}\",[]]Cf[\"{{{AHO`{CGbBABh}}{KFl{BABn}}}{{AHO`{CGbBABh}}}}\",[]]Cd[\"{{}{{KGd{{KC`{{ABh{BBLb}}BBBj}}{A@d{IEd}}}}}}\",[]]Bn[\"{{{AHO`{CGbBFGh}}KI`}{{AHO`{CGbBFGh}}}}\",[]]Bn[\"{{{AHO`{CGbBFGj}}KIb}{{AHO`{CGbBFGj}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Bo[\"{{{AHO`{CGbBABd}}AB@d}{{AHO`{CGbBABd}}}}\",[]]Bo[\"{{{AHO`{CGbBABh}}AB@d}{{AHO`{CGbBABh}}}}\",[]]Ci[\"{{{AHO`{DA`}}{KFl{AB@d}}}{{KGd{{KFl{AB@d}}AIKd}}}}\",[]]Bj[\"{{{AHO`{BCAd}}AJAjKCb}{{KGf{ICj}}}}\",[]]054Bm[\"{{{AHO`{NMf}}}{{KGd{{KFl{BGAn}}IEj}}}}\",[]]Bn[\"{{{AHO`{AJAb}}}{{KGd{{KFl{BGAn}}IEj}}}}\",[]]Bn[\"{{{AHO`{NMh}}}{{KGd{{KFl{BGAn}}BCAb}}}}\",[]]Bo[\"{{{AHO`{AJAd}}}{{KGd{{KFl{BGAn}}BCAb}}}}\",[]]Aj[\"{{{AHO`{AHDj}}}CNb}\",[]]Aj[\"{{{AHO`{AHDl}}}CNb}\",[]]Bl[\"{{{AHO`{AB@h}}{KFl{{A@b{BFCf}}}}}ICj}\",[]]Cc[\"{{{AHO`{CGbAILd}}{KFl{{AHO`{BFCh}}}}NK`}ICj}\",[]]Aj[\"{{{AHO`{AB@j}}}CNb}\",[]]Bn[\"{{{AHO`{CGbBBAl}}AFn}{{AHO`{CGbBBAl}}}}\",[]]Bn[\"{{{AHO`{CGbBBAn}}AFn}{{AHO`{CGbBBAn}}}}\",[]]Ak[\"{{{AHO`{AHFj}}}B@Oj}\",[]]0Ak[\"{{{AHO`{AILb}}}BGA`}\",[]]Cb[\"{{{AHO`{AILd}}KG`}{{KGd{{KFl{AJ@f}}AIKj}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]Bn[\"{{{AHO`{CGbBCOl}}KI`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}KIb}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{AHAlKFl}\",[]]Bo[\"{{{AHO`{AHAl}}{AHO`{CMl}}}{{KGf{BCOj}}}}\",[]]Em[\"{{{AHO`{AILb}}{AHO`{CGb{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjBHLn}}}}\",[]]El[\"{{{AHO`{NMf}}{AHO`{CGb{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjBHLn}}}}\",[]]Em[\"{{{AHO`{AJAb}}{AHO`{CGb{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjBHLn}}}}\",[]]El[\"{{{AHO`{NMh}}{AHO`{CGb{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjBHM`}}}}\",[]]Em[\"{{{AHO`{AJAd}}{AHO`{CGb{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}{AHO`{{K@j{Oh}}}}}{{KGd{ICjBHM`}}}}\",[]]Am[\"{{{AHO`{CGbAHB`}}}ICj}\",[]]0Am[\"{{{AHO`{AILd}}A@l}CNb}\",[]]Ak[\"{{{AHO`{AILb}}}BGIb}\",[]]Bg[\"{{{AHO`{DA`}}}{{KGd{BEFbAIKd}}}}\",[]]>=E`[\"{{{AHO`{BHEb}}{AHO`{{K@j{Oh}}}}{AHO`{BBLb}}{AHO`{BGOn}}}{{KGd{BHGhIEl}}}}\",[]]0Bg[\"{{{A@b{BBCb}}}{{KGd{B@KhBGIn}}}}\",[]]Bg[\"{{{A@b{BBCb}}}{{KGd{B@KlBGIn}}}}\",[]]Bn[\"{{{AHO`{CGbBFGh}}KI`}{{AHO`{CGbBFGh}}}}\",[]]Bn[\"{{{AHO`{CGbBFGj}}KIb}{{AHO`{CGbBFGj}}}}\",[]]o[\"{NIlNIl}\",[]]0000Aj[\"{{{AHO`{AILd}}}DC`}\",[]]0Bd[\"{{{AHO`{CGbAILf}}CNbAB@d}ICj}\",[]]Bc[\"{{{AHO`{CGbCNj}}CNbAB@d}ICj}\",[]]Bd[\"{{{AHO`{CGbAILh}}CNbAB@d}ICj}\",[]]Bc[\"{{{AHO`{CGbCNl}}CNbAB@d}ICj}\",[]]Ci[\"{{{AHO`{DA`}}{KFl{AB@d}}}{{KGd{{KFl{AB@d}}AIKd}}}}\",[]]Cd[\"{{{AHO`{CGbAAj}}{AHO`{AIJb}}}{{KGd{ICjIEn}}}}\",[]]Cd[\"{{{AHO`{CGbA@h}}{AHO`{AIJb}}}{{KGd{ICjIEn}}}}\",[]]Ce[\"{{{AHO`{CGbAAOf}}{AHO`{AIJb}}}{{KGd{ICjIEn}}}}\",[]]Ce[\"{{{AHO`{CGbAHAl}}{AHO`{AIJb}}}{{KGd{ICjIEn}}}}\",[]]Cd[\"{{{AHO`{CGbAAl}}{AHO`{AIJb}}}{{KGd{ICjIEn}}}}\",[]]Cd[\"{{{AHO`{CGbAAn}}{AHO`{AIJb}}}{{KGd{ICjIEn}}}}\",[]]Bo[\"{{{AHO`{CGbBABd}}BH@l}{{AHO`{CGbBABd}}}}\",[]]Bo[\"{{{AHO`{CGbBABh}}BH@n}{{AHO`{CGbBABh}}}}\",[]]Aj[\"{{{AHO`{BBMh}}}CNb}\",[]]Aj[\"{{{AHO`{BBMj}}}CNb}\",[]]Cd[\"{{{AHO`{CGbB@Kh}}{A@b{A`}}}{{AHO`{CGbB@Kh}}}}\",[]]Cd[\"{{{AHO`{CGbB@Kl}}{A@b{A`}}}{{AHO`{CGbB@Kl}}}}\",[]]Cl[\"{{BBMh{KFl{BABd}}{A@d{BBL`}}{A@b{NMd}}}{{KGf{AB@h}}}}\",[]]Aj[\"{{{AHO`{ABA`}}}DC`}\",[]]Aj[\"{{{AHO`{ABAb}}}DC`}\",[]]Ca[\"{{{AHO`{CGbAILd}}NK`AIMn}{{KGd{ICjBHDj}}}}\",[]]Bd[\"{{{AHO`{BHEb}}}{{ABh{BCOb}}}}\",[]]Bj[\"{{{AHO`{AILb}}AJ@f}{{KGd{ICjIFh}}}}\",[]]Bm[\"{{{AHO`{CGbAILd}}AJ@f}{{KGd{ICjIFh}}}}\",[]]Bn[\"{{{AHO`{CGbBCOl}}KCb}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}KCb}{{AHO`{CGbBCOn}}}}\",[]]Bm[\"{{}{{KGd{{KC`{BBLbBFJb}}{A@d{IEd}}}}}}\",[]]Ai[\"{AIL`{{KGh{ICj}}}}\",[]]A`[\"{AILnCNb}\",[]]A`[\"{AIM`CNb}\",[]]An[\"{{{AHO`{AB@h}}B@Kh}ICj}\",[]]Dc[\"{{{AHO`{CGbAILd}}NK`KG`{KFl{AB@d}}}{{KGd{{KFl{AB@d}}AIKj}}}}\",[]]o[\"{{}AHO`}\",[]]Aj[\"{{{AHO`{AILb}}}KCb}\",[]]Bn[\"{{{AHO`{CGbBABd}}CNb}{{AHO`{CGbBABd}}}}\",[]]Bn[\"{{{AHO`{CGbBABh}}CNb}{{AHO`{CGbBABh}}}}\",[]]Bn[\"{{{AHO`{CGbBCOl}}KI`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}KIb}{{AHO`{CGbBCOn}}}}\",[]]:Bn[\"{{{AHO`{CGbBCOl}}DC`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}DC`}{{AHO`{CGbBCOn}}}}\",[]]Bc[\"{{{AHO`{CGbAILd}}A@lKIb}ICj}\",[]]A`[\"{AHElCNb}\",[]]A`[\"{AHEnCNb}\",[]]Ak[\"{{}{{KGd{B@KhIFb}}}}\",[]]Ak[\"{{}{{KGd{B@KlIFd}}}}\",[]]6587Cg[\"{{{AHO`{CGbAAj}}{AHO`{CGbAIJb}}}{{KGd{ICjIEn}}}}\",[]]Cg[\"{{{AHO`{CGbA@h}}{AHO`{CGbAIJb}}}{{KGd{ICjIEn}}}}\",[]]Ch[\"{{{AHO`{CGbAAOf}}{AHO`{CGbAIJb}}}{{KGd{ICjIEn}}}}\",[]]Ch[\"{{{AHO`{CGbAHAl}}{AHO`{CGbAIJb}}}{{KGd{ICjIEn}}}}\",[]]Cg[\"{{{AHO`{CGbAAl}}{AHO`{CGbAIJb}}}{{KGd{ICjIEn}}}}\",[]]Cg[\"{{{AHO`{CGbAAn}}{AHO`{CGbAIJb}}}{{KGd{ICjIEn}}}}\",[]]n[\"{{}ICj}\",[]]Ce[\"{{{AHO`{CGbBCOl}}{KFl{KCb}}}{{AHO`{CGbBCOl}}}}\",[]]Ce[\"{{{AHO`{CGbBCOn}}{KFl{KCb}}}{{AHO`{CGbBCOn}}}}\",[]]Bn[\"{{{AHO`{AILb}}}{{KGd{{ABh{AJ@f}}IFh}}}}\",[]]Cd[\"{{{AHO`{CGbAILd}}NK`}{{KGd{{ABh{AJ@f}}IFh}}}}\",[]]Am[\"{{{AHO`{CGbAILf}}}ICj}\",[]]Al[\"{{{AHO`{CGbCNj}}}ICj}\",[]]Al[\"{{{AHO`{CGbIDh}}}ICj}\",[]]Am[\"{{{AHO`{CGbAILh}}}ICj}\",[]]Al[\"{{{AHO`{CGbCNl}}}ICj}\",[]]Al[\"{{{AHO`{CGbIDj}}}ICj}\",[]]Ai[\"{AIL`{{KGh{ICj}}}}\",[]]Bj[\"{{{AHO`{AILb}}AJ@f}{{KGd{ICjIFh}}}}\",[]]Bm[\"{{{AHO`{CGbAILd}}AJ@f}{{KGd{ICjIFh}}}}\",[]]D`[\"{{{AHO`{CGbAILd}}KG`{KFl{AB@d}}}{{KGd{{KFl{AB@d}}AIKj}}}}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{A@b{BFBf}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{A@b{BFBh}}}{{AHO`{CGbBCOn}}}}\",[]]Cf[\"{{{AHO`{CGbBCOl}}{KFl{AB@d}}}{{AHO`{CGbBCOl}}}}\",[]]Cf[\"{{{AHO`{CGbBCOn}}{KFl{AB@d}}}{{AHO`{CGbBCOn}}}}\",[]]Ce[\"{{{AHO`{CGbBCOl}}{KFl{CNb}}}{{AHO`{CGbBCOl}}}}\",[]]Ce[\"{{{AHO`{CGbBCOn}}{KFl{CNb}}}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{BAOjKFl}\",[]]A`[\"{BAOlKFl}\",[]];Bn[\"{{{AHO`{CGbBCOl}}DC`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}DC`}{{AHO`{CGbBCOn}}}}\",[]]Am[\"{{{AHO`{AILb}}KI`}ICj}\",[]]o[\"{NIlNIl}\",[]]Bn[\"{{{AHO`{CGbBCOl}}CN`}{{AHO`{CGbBCOl}}}}\",[]]Bn[\"{{{AHO`{CGbBCOn}}CN`}{{AHO`{CGbBCOn}}}}\",[]]310;:54Bn[\"{{{AHO`{AILb}}}{{KGd{{ABh{AJ@f}}IFh}}}}\",[]]Bn[\"{{{AHO`{AILd}}}{{KGd{{ABh{AJ@f}}IFh}}}}\",[]]10Bm[\"{{{AHO`{CGbBCOl}}Oh}{{AHO`{CGbBCOl}}}}\",[]]Bm[\"{{{AHO`{CGbBCOn}}Oh}{{AHO`{CGbBCOn}}}}\",[]]A`[\"{BGAnKFl}\",[]]") \ No newline at end of file diff --git a/pr/707/docs/search.index/path/a96c5934a8c0.js b/pr/707/docs/search.index/path/813227ed5be5.js similarity index 96% rename from pr/707/docs/search.index/path/a96c5934a8c0.js rename to pr/707/docs/search.index/path/813227ed5be5.js index 6ef62d487..268bb3306 100644 --- a/pr/707/docs/search.index/path/a96c5934a8c0.js +++ b/pr/707/docs/search.index/path/813227ed5be5.js @@ -1 +1 @@ -rd_("f[1,\"\"]0Al[10,\"core::ops\",\"core::ops\"]Ce[5,\"clap_builder::util::id\",\"clap_builder::util::id\"]22Ag[10,\"core::ops::arith\"]A`[10,\"core::any\"]Ao[5,\"alloc::sync\",\"alloc::sync\"]Ba[5,\"alloc::boxed\",\"alloc::boxed\"]Cb[10,\"bytes::buf::buf_impl\",\"bytes::buf::buf_impl\"]Ai[5,\"noq_perf\",\"noq_perf\"]i[6,\"noq\"]Ak[6,\"noq_proto\",\"noq_proto\"]Ae[10,\"core::ops::bit\"]Ac[5,\"bench\",\"bench\"]Ba[5,\"perf::client\",\"perf::client\"]Ba[5,\"perf::server\",\"perf::server\"]A`[10,\"core::cmp\"]Ak[5,\"core::pin\",\"core::pin\"]Am[5,\"alloc::vec\",\"alloc::vec\"]f[1,\"\"]00m[2,\"noq_udp\"]f[0,\"\"]f[3,\"\"]3333Ak[10,\"clap_builder::derive\"]Bn[10,\"std::os::fd::owned\",\"std::os::fd::owned\"]Ae[5,\"noq::congestion\"]Ci[5,\"noq_proto::congestion\",\"noq_proto::congestion::bbr3\"]Af[10,\"core::ops::drop\"]Ad[10,\"core::convert\"]Aa[10,\"core::hash\"]1Aa[5,\"noq::crypto\"]Bk[5,\"noq_proto::crypto\",\"noq_proto::crypto\"]Ba[15,\"noq_proto::ProtectedHeader\"]Ak[5,\"std::path\",\"std::path\"]Ae[5,\"noq\",\"noq::path\"]Bi[6,\"core::task::poll\",\"core::task::poll\"]i[6,\"noq\"]Ak[6,\"noq_proto\",\"noq_proto\"]Ak[2,\"noq_proto::congestion\"]f[1,\"\"]f[3,\"\"]i[2,\"noq\"]1Ag[2,\"noq_proto::config\"]Bd[2,\"noq_proto::connection::streams\"]0Ab[2,\"noq::runtime\"]55Bd[10,\"core::convert\",\"core::convert\"]Ba[5,\"bytes::bytes\",\"bytes::bytes\"]i[5,\"noq\"]Cb[5,\"noq_proto\",\"noq_proto::connection::assembler\"]Ab[10,\"core::clone\"]Bi[6,\"noq_proto::frame\",\"noq_proto::frame\"]Ae[5,\"noq::congestion\"]Cj[5,\"noq_proto::congestion\",\"noq_proto::congestion::cubic\"]A`[10,\"core::fmt\"]Ag[10,\"core::ops::deref\"]Ak[5,\"core::fmt\",\"core::fmt\"]Ab[10,\"core::error\"]Ai[2,\"noq::crypto::rustls\"]Bc[6,\"rustls::error\",\"rustls::error\"]Ao[2,\"noq_proto::crypto::rustls\"]Cm[5,\"noq_proto::transport_error\",\"noq_proto::transport_error\"]Ba[8,\"clap_builder\",\"clap_builder\"]Bi[6,\"arbitrary::error\",\"arbitrary::error\"]Ai[6,\"noq::crypto::rustls\"]Ao[6,\"noq_proto::crypto::rustls\"]Dg[6,\"noq_proto::transport_parameters\",\"noq_proto::transport_parameters\"]Co[6,\"noq_proto::n0_nat_traversal\",\"noq_proto::n0_nat_traversal\"]o[2,\"noq_proto\"]Bg[6,\"noq_proto\",\"noq_proto::connection\"]2Bi[5,\"core::ops::range\",\"core::ops::range\"]Ba[15,\"noq_proto::ProtectedHeader\"]0Bb[10,\"core::marker\",\"core::marker\"]Ba[5,\"bench::stats\",\"bench::stats\"]Ao[5,\"perf::stats\",\"perf::stats\"]Ba[5,\"rustls::quic\",\"rustls::quic\"]Ah[10,\"std::io\",\"std::io\"]f[1,\"\"]f[3,\"\"]Ak[2,\"noq_proto::congestion\"]l[2,\"rustls\"]Bi[5,\"bytes::bytes_mut\",\"bytes::bytes_mut\"]Ai[6,\"noq_perf\",\"noq_perf\"]Bb[5,\"noq_proto\",\"noq_proto::frame\"]Ag[10,\"core::ops::deref\"]Am[5,\"core::time\",\"core::time\"]Bc[5,\"noq::endpoint\",\"noq::endpoint\"]Ai[5,\"noq\",\"noq::endpoint\"]Be[5,\"noq_proto\",\"noq_proto::endpoint\"]Am[15,\"noq_proto::StreamEvent\"]Ai[5,\"noq\",\"noq::incoming\"]2Bc[10,\"core::iter::traits::iterator\"]An[10,\"tonic::service::layered\"]Bc[6,\"noq_proto\",\"noq_proto::packet\"]Ak[5,\"noq\",\"noq::connection\"]Ae[5,\"noq\",\"noq::path\"]Ak[6,\"core::cmp\",\"core::cmp\"]7Ag[5,\"noq_udp\",\"noq_udp\"]i[5,\"noq\"]Ak[5,\"noq_proto\",\"noq_proto\"]Ad[10,\"alloc::string\"]j[10,\"noq\"]Bc[10,\"noq_proto\",\"noq_proto::token\"]4354Bn[5,\"noq_proto\",\"noq_proto::connection::stats\"]Bi[5,\"rustls_pki_types\",\"rustls_pki_types\"]?i[2,\"noq\"]o[2,\"noq_proto\"]1Ak[2,\"noq_proto::congestion\"]f[3,\"\"]Ae[15,\"noq::PathEvent\"]Ak[15,\"noq_proto::PathEvent\"]Ak[5,\"noq\",\"noq::connection\"]Ag[10,\"core::ops::arith\"]A`[10,\"arbitrary\"]Al[10,\"tokio::io::async_read\"]Ai[10,\"futures_io::if_std\"]Am[15,\"noq_proto::StreamEvent\"]Ac[10,\"core::borrow\"]Aa[5,\"perf\",\"perf\"]Ak[5,\"criterion\",\"criterion\"]Cb[5,\"noq_proto\",\"noq_proto::connection::datagrams\"]Bl[10,\"noq_proto::coding\",\"noq_proto::coding\"]<;0Ak[5,\"core::fmt\",\"core::fmt\"]i[5,\"noq\"]Ak[5,\"noq_proto\",\"noq_proto\"]i[6,\"noq\"]Bb[6,\"noq_proto\",\"noq_proto::frame\"]Ab[10,\"noq::crypto\"]Bl[10,\"noq_proto::crypto\",\"noq_proto::crypto\"]Ba[5,\"hdrhistogram\",\"hdrhistogram\"]21A`[10,\"core::cmp\"]5Bg[6,\"noq_proto\",\"noq_proto::connection\"]i[2,\"noq\"]o[2,\"noq_proto\"]8Bn[6,\"noq_proto\",\"noq_proto::connection::paths\"];Bn[5,\"noq_proto\",\"noq_proto::connection::stats\"]Al[6,\"noq\",\"noq::recv_stream\"]Cf[6,\"noq_proto\",\"noq_proto::connection::streams::recv\"]Ak[10,\"futures_core::future\"]Ak[10,\"futures_core::stream\"]Ai[10,\"noq\",\"noq::runtime\"]Bc[5,\"std::net::udp\",\"std::net::udp\"]Ak[10,\"clap_builder::derive\"]Ak[2,\"noq_proto::connection\"]0f[3,\"\"]f[1,\"\"]Ag[2,\"noq_proto::config\"]Em[5,\"clap_builder::parser::matches::arg_matches\",\"clap_builder::parser::matches::arg_matches\"]7Am[10,\"tokio::io::async_write\"]Ai[10,\"futures_io::if_std\"]Ae[5,\"noq::congestion\"]Ci[5,\"noq_proto::congestion\",\"noq_proto::congestion::bbr3\"]i[5,\"noq\"]Bn[5,\"noq_proto\",\"noq_proto::connection::paths\"]Aa[10,\"equivalent\"]Ak[5,\"noq\",\"noq::connection\"]Bg[5,\"noq::connection\",\"noq::connection\"]1Bg[5,\"noq_proto\",\"noq_proto::connection\"]Af[10,\"noq::congestion\"]Cd[10,\"noq_proto::congestion\",\"noq_proto::congestion\"]A`[10,\"hashbrown\"]68Bn[5,\"noq_proto\",\"noq_proto::connection::stats\"]B`[10,\"core::future::into_future\"]:Bj[5,\"noq_proto\",\"noq_proto::cid_generator\"]Ag[5,\"std::io\",\"std::io\"]Bi[6,\"noq_proto::frame\",\"noq_proto::frame\"]Bb[6,\"noq_proto\",\"noq_proto::frame\"]Bm[8,\"core::num::nonzero\",\"core::num::nonzero\"]Cm[5,\"rustls::error::other_error\",\"rustls::error::other_error\"]A`[10,\"core::cmp\"]Am[5,\"noq\",\"noq::event_stream\"]i[6,\"noq\"]Bn[6,\"noq_proto\",\"noq_proto::connection::paths\"]i[5,\"noq\"]Bi[5,\"noq_proto\",\"noq_proto::config::qlog\"]Bi[5,\"noq::recv_stream\",\"noq::recv_stream\"]Al[5,\"noq\",\"noq::recv_stream\"]C`[5,\"noq_proto\",\"noq_proto::connection::streams\"]Al[6,\"noq\",\"noq::recv_stream\"]Ai[5,\"noq\",\"noq::incoming\"]Be[5,\"noq_proto\",\"noq_proto::endpoint\"]Bi[5,\"noq::send_stream\",\"noq::send_stream\"]Al[5,\"noq\",\"noq::send_stream\"]5Dc[6,\"rustls_pki_types::server_name\",\"rustls_pki_types::server_name\"]Ce[6,\"core::net::socket_addr\",\"core::net::socket_addr\"]Bb[5,\"noq_proto\",\"noq_proto::frame\"]Ak[10,\"clap_builder::derive\"]Ak[5,\"std::time\",\"std::time\"]j[10,\"noq\"]Bd[10,\"noq_proto\",\"noq_proto::config\"]Ai[5,\"noq::crypto::rustls\"]Ck[5,\"noq_proto::crypto::rustls\",\"noq_proto::crypto::rustls\"]3Bc[10,\"noq_proto\",\"noq_proto::token\"]Ag[5,\"noq_udp\",\"noq_udp\"]Al[6,\"noq\",\"noq::send_stream\"]Cf[6,\"noq_proto\",\"noq_proto::connection::streams::send\"]i[2,\"noq\"]o[2,\"noq_proto\"]10Be[5,\"noq_proto\",\"noq_proto::endpoint\"]Ac[6,\"bench\",\"bench\"]Aa[5,\"bulk\",\"bulk\"]Da[6,\"noq_proto::connection::state\",\"noq_proto::connection::state\"]i[6,\"noq\"]Bc[6,\"noq_proto\",\"noq_proto::config\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Aa[5,\"noq::crypto\"]Bk[5,\"noq_proto::crypto\",\"noq_proto::crypto\"]Ae[5,\"noq::congestion\"]Cj[5,\"noq_proto::congestion\",\"noq_proto::congestion::cubic\"]Ae[15,\"noq::PathEvent\"]Ak[15,\"noq_proto::PathEvent\"]?i[5,\"noq\"]Bn[5,\"noq_proto\",\"noq_proto::config::transport\"]Ae[10,\"tonic::request\"]j[10,\"noq\"]Bj[10,\"noq_proto\",\"noq_proto::config::qlog\"]Ac[6,\"bench\",\"bench\"]An[5,\"noq\",\"noq::runtime::smol\"]o[2,\"noq_proto\"]C`[6,\"noq_proto\",\"noq_proto::connection::streams\"]Ba[5,\"bench::stats\",\"bench::stats\"]Ao[5,\"perf::stats\",\"perf::stats\"]f[3,\"\"]i[2,\"noq\"]Ak[2,\"noq_proto::connection\"]1Co[5,\"rustls::client::client_conn\",\"rustls::client::client_conn\"]Bk[5,\"noq_proto::config\",\"noq_proto::config\"]?Ab[10,\"noq::crypto\"]Bc[5,\"noq_proto\",\"noq_proto::config\"]Bl[10,\"noq_proto::crypto\",\"noq_proto::crypto\"]i[5,\"noq\"]C`[5,\"noq_proto\",\"noq_proto::connection::streams\"]Bo[6,\"noq_proto::endpoint\",\"noq_proto::endpoint\"]i[6,\"noq\"]Be[6,\"noq_proto\",\"noq_proto::endpoint\"]Bk[5,\"noq_proto::shared\",\"noq_proto::shared\"]5Bc[5,\"noq_proto\",\"noq_proto::shared\"]Bb[5,\"noq_proto\",\"noq_proto::frame\"]4Bc[6,\"noq_proto\",\"noq_proto::shared\"]Ag[6,\"noq_udp\",\"noq_udp\"]Bb[10,\"core::iter::traits::collect\"]Bm[8,\"core::num::nonzero\",\"core::num::nonzero\"];Bb[5,\"noq_proto\",\"noq_proto::token\"]Ae[15,\"noq::PathEvent\"]Ak[15,\"noq_proto::PathEvent\"]Ak[5,\"noq\",\"noq::connection\"]21Da[5,\"noq_proto::connection::paths\",\"noq_proto::connection::paths\"]Bn[5,\"noq_proto\",\"noq_proto::connection::paths\"]2Co[5,\"rustls::server::server_conn\",\"rustls::server::server_conn\"]Bk[5,\"noq_proto::config\",\"noq_proto::config\"]i[5,\"noq\"]Ab[10,\"noq::crypto\"]Bc[5,\"noq_proto\",\"noq_proto::config\"]Bl[10,\"noq_proto::crypto\",\"noq_proto::crypto\"]Ce[5,\"core::net::socket_addr\",\"core::net::socket_addr\"]0Al[6,\"noq\",\"noq::send_stream\"]Ao[5,\"noq\",\"noq::runtime::tokio\"]Cg[5,\"arbitrary::unstructured\",\"arbitrary::unstructured\"]i[2,\"noq\"]j[2,\"perf\"]9Bl[5,\"noq_proto\",\"noq_proto::bloom_token_log\"]Ab[10,\"core::clone\"]Be[6,\"noq_proto\",\"noq_proto::endpoint\"]Bc[5,\"noq_proto\",\"noq_proto::shared\"]Ai[5,\"noq\",\"noq::endpoint\"]Ai[5,\"noq::crypto::rustls\"]Ck[5,\"noq_proto::crypto::rustls\",\"noq_proto::crypto::rustls\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Bo[5,\"noq_proto::endpoint\",\"noq_proto::endpoint\"]Be[5,\"noq_proto\",\"noq_proto::endpoint\"]Ae[5,\"noq::congestion\"]Cm[5,\"noq_proto::congestion\",\"noq_proto::congestion::new_reno\"]Bi[5,\"core::num::error\",\"core::num::error\"]Bc[5,\"noq_proto\",\"noq_proto::packet\"]Ec[5,\"clap_builder::builder::possible_value\",\"clap_builder::builder::possible_value\"]Bi[6,\"rustls_pki_types\",\"rustls_pki_types\"]Cf[6,\"noq_proto\",\"noq_proto::connection::streams::recv\"]Cg[5,\"rustls::webpki::anchors\",\"rustls::webpki::anchors\"]i[5,\"noq\"]Bc[5,\"noq_proto\",\"noq_proto::config\"]Bk[5,\"noq_proto::coding\",\"noq_proto::coding\"]o[2,\"noq_proto\"]0f[3,\"\"]Ai[10,\"noq\",\"noq::runtime\"]Bi[5,\"rustls_pki_types\",\"rustls_pki_types\"]i[6,\"noq\"]Bg[6,\"noq_proto\",\"noq_proto::connection\"]Ak[10,\"clap_builder::derive\"]Ag[15,\"noq_proto::Event\"]:91Ai[5,\"noq\",\"noq::incoming\"]Bb[5,\"noq_proto\",\"noq_proto::frame\"]Bc[6,\"rustls::error\",\"rustls::error\"]=Bb[5,\"noq_proto\",\"noq_proto::token\"]1Al[6,\"noq\",\"noq::recv_stream\"]0C`[5,\"noq_proto\",\"noq_proto::connection::streams\"]Ba[5,\"bench::stats\",\"bench::stats\"]o[5,\"noq_proto\"]Al[5,\"noq_udp\",\"noq_udp::imp\"]Ae[5,\"noq\",\"noq::path\"]i[5,\"noq\"]9>=Bc[5,\"noq_proto\",\"noq_proto::shared\"]1Bn[5,\"noq_proto\",\"noq_proto::connection::stats\"]Ba[5,\"rustls::rand\",\"rustls::rand\"]Ao[5,\"perf::stats\",\"perf::stats\"]o[2,\"noq_proto\"]Bc[6,\"noq_proto\",\"noq_proto::packet\"]6Bi[5,\"noq_proto\",\"noq_proto::config::qlog\"]Bc[15,\"noq_proto::PathAbandonReason\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Ak[5,\"std::time\",\"std::time\"]:Bb[5,\"noq_proto\",\"noq_proto::token\"]Da[5,\"noq_proto::config::transport\",\"noq_proto::config::transport\"]Bi[5,\"bytes::bytes_mut\",\"bytes::bytes_mut\"]Ai[6,\"noq_perf\",\"noq_perf\"]Bb[5,\"noq_proto\",\"noq_proto::frame\"]Ag[10,\"core::ops::deref\"]Am[5,\"core::time\",\"core::time\"]Bc[5,\"noq::endpoint\",\"noq::endpoint\"]Ai[5,\"noq\",\"noq::endpoint\"]Be[5,\"noq_proto\",\"noq_proto::endpoint\"]Am[15,\"noq_proto::StreamEvent\"]Ai[5,\"noq\",\"noq::incoming\"]2Bc[10,\"core::iter::traits::iterator\"]An[10,\"tonic::service::layered\"]Bc[6,\"noq_proto\",\"noq_proto::packet\"]Ak[5,\"noq\",\"noq::connection\"]Ae[5,\"noq\",\"noq::path\"]Ak[6,\"core::cmp\",\"core::cmp\"]7Ag[5,\"noq_udp\",\"noq_udp\"]i[5,\"noq\"]Ak[5,\"noq_proto\",\"noq_proto\"]Ad[10,\"alloc::string\"]j[10,\"noq\"]Bc[10,\"noq_proto\",\"noq_proto::token\"]4354Bn[5,\"noq_proto\",\"noq_proto::connection::stats\"]Bi[5,\"rustls_pki_types\",\"rustls_pki_types\"]?i[2,\"noq\"]o[2,\"noq_proto\"]1Ak[2,\"noq_proto::congestion\"]f[3,\"\"]Ae[15,\"noq::PathEvent\"]Ak[15,\"noq_proto::PathEvent\"]Ak[5,\"noq\",\"noq::connection\"]Ag[10,\"core::ops::arith\"]A`[10,\"arbitrary\"]Al[10,\"tokio::io::async_read\"]Ai[10,\"futures_io::if_std\"]Am[15,\"noq_proto::StreamEvent\"]Ac[10,\"core::borrow\"]Aa[5,\"perf\",\"perf\"]Ak[5,\"criterion\",\"criterion\"]Cb[5,\"noq_proto\",\"noq_proto::connection::datagrams\"]Bl[10,\"noq_proto::coding\",\"noq_proto::coding\"]<;0Ak[5,\"core::fmt\",\"core::fmt\"]i[5,\"noq\"]Ak[5,\"noq_proto\",\"noq_proto\"]i[6,\"noq\"]Bb[6,\"noq_proto\",\"noq_proto::frame\"]Ab[10,\"noq::crypto\"]Bl[10,\"noq_proto::crypto\",\"noq_proto::crypto\"]Ba[5,\"hdrhistogram\",\"hdrhistogram\"]21A`[10,\"core::cmp\"]5Bg[6,\"noq_proto\",\"noq_proto::connection\"]i[2,\"noq\"]o[2,\"noq_proto\"]8Bn[6,\"noq_proto\",\"noq_proto::connection::paths\"];Bn[5,\"noq_proto\",\"noq_proto::connection::stats\"]Al[6,\"noq\",\"noq::recv_stream\"]Cf[6,\"noq_proto\",\"noq_proto::connection::streams::recv\"]Ak[10,\"futures_core::future\"]Ak[10,\"futures_core::stream\"]Ai[10,\"noq\",\"noq::runtime\"]Bc[5,\"std::net::udp\",\"std::net::udp\"]Ak[10,\"clap_builder::derive\"]Ak[2,\"noq_proto::connection\"]0f[3,\"\"]f[1,\"\"]Ag[2,\"noq_proto::config\"]Em[5,\"clap_builder::parser::matches::arg_matches\",\"clap_builder::parser::matches::arg_matches\"]7Am[10,\"tokio::io::async_write\"]Ai[10,\"futures_io::if_std\"]Ae[5,\"noq::congestion\"]Ci[5,\"noq_proto::congestion\",\"noq_proto::congestion::bbr3\"]i[5,\"noq\"]Bn[5,\"noq_proto\",\"noq_proto::connection::paths\"]Aa[10,\"equivalent\"]Ak[5,\"noq\",\"noq::connection\"]Bg[5,\"noq::connection\",\"noq::connection\"]1Bg[5,\"noq_proto\",\"noq_proto::connection\"]Af[10,\"noq::congestion\"]Cd[10,\"noq_proto::congestion\",\"noq_proto::congestion\"]A`[10,\"hashbrown\"]68Bn[5,\"noq_proto\",\"noq_proto::connection::stats\"]B`[10,\"core::future::into_future\"]:Bj[5,\"noq_proto\",\"noq_proto::cid_generator\"]Ag[5,\"std::io\",\"std::io\"]Bi[6,\"noq_proto::frame\",\"noq_proto::frame\"]Bb[6,\"noq_proto\",\"noq_proto::frame\"]Bm[8,\"core::num::nonzero\",\"core::num::nonzero\"]Cm[5,\"rustls::error::other_error\",\"rustls::error::other_error\"]A`[10,\"core::cmp\"]Am[5,\"noq\",\"noq::event_stream\"]i[6,\"noq\"]Bn[6,\"noq_proto\",\"noq_proto::connection::paths\"]i[5,\"noq\"]Bi[5,\"noq_proto\",\"noq_proto::config::qlog\"]Bi[5,\"noq::recv_stream\",\"noq::recv_stream\"]Al[5,\"noq\",\"noq::recv_stream\"]C`[5,\"noq_proto\",\"noq_proto::connection::streams\"]Al[6,\"noq\",\"noq::recv_stream\"]Ai[5,\"noq\",\"noq::incoming\"]Be[5,\"noq_proto\",\"noq_proto::endpoint\"]Bi[5,\"noq::send_stream\",\"noq::send_stream\"]Al[5,\"noq\",\"noq::send_stream\"]5Dc[6,\"rustls_pki_types::server_name\",\"rustls_pki_types::server_name\"]Ce[6,\"core::net::socket_addr\",\"core::net::socket_addr\"]Bb[5,\"noq_proto\",\"noq_proto::frame\"]Ak[10,\"clap_builder::derive\"]Ak[5,\"std::time\",\"std::time\"]j[10,\"noq\"]Bd[10,\"noq_proto\",\"noq_proto::config\"]Ai[5,\"noq::crypto::rustls\"]Ck[5,\"noq_proto::crypto::rustls\",\"noq_proto::crypto::rustls\"]3Bc[10,\"noq_proto\",\"noq_proto::token\"]Ag[5,\"noq_udp\",\"noq_udp\"]Al[6,\"noq\",\"noq::send_stream\"]Cf[6,\"noq_proto\",\"noq_proto::connection::streams::send\"]i[2,\"noq\"]o[2,\"noq_proto\"]10Be[5,\"noq_proto\",\"noq_proto::endpoint\"]Ac[6,\"bench\",\"bench\"]Aa[5,\"bulk\",\"bulk\"]Da[6,\"noq_proto::connection::state\",\"noq_proto::connection::state\"]i[6,\"noq\"]Bc[6,\"noq_proto\",\"noq_proto::config\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Aa[5,\"noq::crypto\"]Bk[5,\"noq_proto::crypto\",\"noq_proto::crypto\"]Ae[5,\"noq::congestion\"]Cj[5,\"noq_proto::congestion\",\"noq_proto::congestion::cubic\"]Ae[15,\"noq::PathEvent\"]Ak[15,\"noq_proto::PathEvent\"]?i[5,\"noq\"]Bn[5,\"noq_proto\",\"noq_proto::config::transport\"]Ae[10,\"tonic::request\"]j[10,\"noq\"]Bj[10,\"noq_proto\",\"noq_proto::config::qlog\"]Ac[6,\"bench\",\"bench\"]An[5,\"noq\",\"noq::runtime::smol\"]o[2,\"noq_proto\"]C`[6,\"noq_proto\",\"noq_proto::connection::streams\"]Ba[5,\"bench::stats\",\"bench::stats\"]Ao[5,\"perf::stats\",\"perf::stats\"]f[3,\"\"]i[2,\"noq\"]Ak[2,\"noq_proto::connection\"]1Bk[5,\"noq_proto::config\",\"noq_proto::config\"]Co[5,\"rustls::client::client_conn\",\"rustls::client::client_conn\"]?Ab[10,\"noq::crypto\"]Bc[5,\"noq_proto\",\"noq_proto::config\"]Bl[10,\"noq_proto::crypto\",\"noq_proto::crypto\"]i[5,\"noq\"]C`[5,\"noq_proto\",\"noq_proto::connection::streams\"]Bo[6,\"noq_proto::endpoint\",\"noq_proto::endpoint\"]i[6,\"noq\"]Be[6,\"noq_proto\",\"noq_proto::endpoint\"]Bk[5,\"noq_proto::shared\",\"noq_proto::shared\"]5Bc[5,\"noq_proto\",\"noq_proto::shared\"]Bb[5,\"noq_proto\",\"noq_proto::frame\"]4Bc[6,\"noq_proto\",\"noq_proto::shared\"]Ag[6,\"noq_udp\",\"noq_udp\"]Bb[10,\"core::iter::traits::collect\"]Bm[8,\"core::num::nonzero\",\"core::num::nonzero\"];Bb[5,\"noq_proto\",\"noq_proto::token\"]Ae[15,\"noq::PathEvent\"]Ak[15,\"noq_proto::PathEvent\"]Ak[5,\"noq\",\"noq::connection\"]21Da[5,\"noq_proto::connection::paths\",\"noq_proto::connection::paths\"]Bn[5,\"noq_proto\",\"noq_proto::connection::paths\"]2Bk[5,\"noq_proto::config\",\"noq_proto::config\"]Co[5,\"rustls::server::server_conn\",\"rustls::server::server_conn\"]i[5,\"noq\"]Ab[10,\"noq::crypto\"]Bc[5,\"noq_proto\",\"noq_proto::config\"]Bl[10,\"noq_proto::crypto\",\"noq_proto::crypto\"]Ce[5,\"core::net::socket_addr\",\"core::net::socket_addr\"]0Al[6,\"noq\",\"noq::send_stream\"]Ao[5,\"noq\",\"noq::runtime::tokio\"]Cg[5,\"arbitrary::unstructured\",\"arbitrary::unstructured\"]i[2,\"noq\"]j[2,\"perf\"]9Bl[5,\"noq_proto\",\"noq_proto::bloom_token_log\"]Ab[10,\"core::clone\"]Be[6,\"noq_proto\",\"noq_proto::endpoint\"]Bc[5,\"noq_proto\",\"noq_proto::shared\"]Ai[5,\"noq\",\"noq::endpoint\"]Ai[5,\"noq::crypto::rustls\"]Ck[5,\"noq_proto::crypto::rustls\",\"noq_proto::crypto::rustls\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Bo[5,\"noq_proto::endpoint\",\"noq_proto::endpoint\"]Be[5,\"noq_proto\",\"noq_proto::endpoint\"]Ae[5,\"noq::congestion\"]Cm[5,\"noq_proto::congestion\",\"noq_proto::congestion::new_reno\"]Bi[5,\"core::num::error\",\"core::num::error\"]Bc[5,\"noq_proto\",\"noq_proto::packet\"]Ec[5,\"clap_builder::builder::possible_value\",\"clap_builder::builder::possible_value\"]Bi[6,\"rustls_pki_types\",\"rustls_pki_types\"]Cf[6,\"noq_proto\",\"noq_proto::connection::streams::recv\"]Cg[5,\"rustls::webpki::anchors\",\"rustls::webpki::anchors\"]i[5,\"noq\"]Bc[5,\"noq_proto\",\"noq_proto::config\"]Bk[5,\"noq_proto::coding\",\"noq_proto::coding\"]o[2,\"noq_proto\"]0f[3,\"\"]Ai[10,\"noq\",\"noq::runtime\"]Bi[5,\"rustls_pki_types\",\"rustls_pki_types\"]i[6,\"noq\"]Bg[6,\"noq_proto\",\"noq_proto::connection\"]Ak[10,\"clap_builder::derive\"]Ag[15,\"noq_proto::Event\"]:91Ai[5,\"noq\",\"noq::incoming\"]Bb[5,\"noq_proto\",\"noq_proto::frame\"]Bc[6,\"rustls::error\",\"rustls::error\"]=Bb[5,\"noq_proto\",\"noq_proto::token\"]1Al[6,\"noq\",\"noq::recv_stream\"]0C`[5,\"noq_proto\",\"noq_proto::connection::streams\"]Ba[5,\"bench::stats\",\"bench::stats\"]o[5,\"noq_proto\"]Al[5,\"noq_udp\",\"noq_udp::imp\"]Ae[5,\"noq\",\"noq::path\"]i[5,\"noq\"]9>=Bc[5,\"noq_proto\",\"noq_proto::shared\"]1Bn[5,\"noq_proto\",\"noq_proto::connection::stats\"]Ba[5,\"rustls::rand\",\"rustls::rand\"]Ao[5,\"perf::stats\",\"perf::stats\"]o[2,\"noq_proto\"]Bc[6,\"noq_proto\",\"noq_proto::packet\"]6Bi[5,\"noq_proto\",\"noq_proto::config::qlog\"]Bc[15,\"noq_proto::PathAbandonReason\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Ak[5,\"std::time\",\"std::time\"]:Bb[5,\"noq_proto\",\"noq_proto::token\"]Da[5,\"noq_proto::config::transport\",\"noq_proto::config::transport\"]Settings

All

Rustdoc settings

Back
\ No newline at end of file +Settings

All

Rustdoc settings

Back
\ No newline at end of file diff --git a/pr/707/docs/src/noq_proto/connection/mod.rs.html b/pr/707/docs/src/noq_proto/connection/mod.rs.html index ac3bd4524..5bdf35b71 100644 --- a/pr/707/docs/src/noq_proto/connection/mod.rs.html +++ b/pr/707/docs/src/noq_proto/connection/mod.rs.html @@ -87,7 +87,7 @@ 87#[cfg(not(fuzzing))] 88use spaces::Retransmits; 89pub(crate) use spaces::SpaceKind; -90use spaces::{PacketSpace, SendableFrames, SentPacket, ThinRetransmits}; +90use spaces::{OpenStatus, PacketSpace, SendableFrames, SentPacket, ThinRetransmits}; 91 92mod stats; 93pub use stats::{ConnectionStats, FrameStats, PathStats, UdpStats}; @@ -334,7473 +334,7500 @@ 334 let connection_side = ConnectionSide::from(side_args); 335 let side = connection_side.side(); 336 let mut rng = StdRng::from_seed(rng_seed); -337 let initial_space = PacketSpace::new(now, SpaceId::Initial, &mut rng); -338 let handshake_space = PacketSpace::new(now, SpaceId::Handshake, &mut rng); +337 let mut initial_space = PacketSpace::new(now, SpaceId::Initial, &mut rng); +338 let mut handshake_space = PacketSpace::new(now, SpaceId::Handshake, &mut rng); 339 #[cfg(test)] -340 let data_space = match config.deterministic_packet_numbers { +340 let mut data_space = match config.deterministic_packet_numbers { 341 true => PacketSpace::new_deterministic(now, SpaceId::Data), 342 false => PacketSpace::new(now, SpaceId::Data, &mut rng), 343 }; 344 #[cfg(not(test))] -345 let data_space = PacketSpace::new(now, SpaceId::Data, &mut rng); -346 let state = State::handshake(state::Handshake { -347 remote_cid_set: side.is_server(), -348 expected_token: Bytes::new(), -349 client_hello: None, -350 allow_server_migration: side.is_client() && config.server_handshake_migration, -351 }); -352 let local_cid_state = FxHashMap::from_iter([( -353 PathId::ZERO, -354 CidState::new( -355 cid_gen.cid_len(), -356 cid_gen.cid_lifetime(), -357 now, -358 if pref_addr_cid.is_some() { 2 } else { 1 }, -359 ), -360 )]); -361 -362 let mut path = PathData::new(network_path, allow_mtud, None, 0, now, &config); -363 path.open_status = paths::OpenStatus::Informed; -364 let mut this = Self { -365 endpoint_config, -366 crypto_state: CryptoState::new(crypto, init_cid, side, &mut rng), -367 handshake_cid: local_cid, -368 remote_handshake_cid: remote_cid, -369 local_cid_state, -370 paths: BTreeMap::from_iter([( -371 PathId::ZERO, -372 PathState { -373 data: path, -374 prev: None, -375 }, -376 )]), -377 path_generation_counter: 0, -378 allow_mtud, -379 state, -380 side: connection_side, -381 peer_params: TransportParameters::default(), -382 original_remote_cid: remote_cid, -383 initial_dst_cid: init_cid, -384 retry_src_cid: None, -385 events: VecDeque::new(), -386 endpoint_events: VecDeque::new(), -387 spin_enabled: config.allow_spin && rng.random_ratio(7, 8), -388 spin: false, -389 spaces: [initial_space, handshake_space, data_space], -390 highest_space: SpaceKind::Initial, -391 idle_timeout: match config.max_idle_timeout { -392 None | Some(VarInt(0)) => None, -393 Some(dur) => Some(Duration::from_millis(dur.0)), -394 }, -395 timers: TimerTable::default(), -396 authentication_failures: 0, -397 connection_close_pending: false, -398 -399 ack_frequency: AckFrequencyState::new(get_max_ack_delay( -400 &TransportParameters::default(), -401 )), +345 let mut data_space = PacketSpace::new(now, SpaceId::Data, &mut rng); +346 +347 // The spaces for PathId::ZERO do not need the PathEvent::Established event. +348 initial_space.for_path(PathId::ZERO).open_status = OpenStatus::Informed; +349 handshake_space.for_path(PathId::ZERO).open_status = OpenStatus::Informed; +350 data_space.for_path(PathId::ZERO).open_status = OpenStatus::Informed; +351 +352 let state = State::handshake(state::Handshake { +353 remote_cid_set: side.is_server(), +354 expected_token: Bytes::new(), +355 client_hello: None, +356 allow_server_migration: side.is_client() && config.server_handshake_migration, +357 }); +358 let local_cid_state = FxHashMap::from_iter([( +359 PathId::ZERO, +360 CidState::new( +361 cid_gen.cid_len(), +362 cid_gen.cid_lifetime(), +363 now, +364 if pref_addr_cid.is_some() { 2 } else { 1 }, +365 ), +366 )]); +367 +368 let mut this = Self { +369 endpoint_config, +370 crypto_state: CryptoState::new(crypto, init_cid, side, &mut rng), +371 handshake_cid: local_cid, +372 remote_handshake_cid: remote_cid, +373 local_cid_state, +374 paths: BTreeMap::from_iter([( +375 PathId::ZERO, +376 PathState { +377 data: PathData::new(network_path, allow_mtud, None, 0, now, &config), +378 prev: None, +379 }, +380 )]), +381 path_generation_counter: 0, +382 allow_mtud, +383 state, +384 side: connection_side, +385 peer_params: TransportParameters::default(), +386 original_remote_cid: remote_cid, +387 initial_dst_cid: init_cid, +388 retry_src_cid: None, +389 events: VecDeque::new(), +390 endpoint_events: VecDeque::new(), +391 spin_enabled: config.allow_spin && rng.random_ratio(7, 8), +392 spin: false, +393 spaces: [initial_space, handshake_space, data_space], +394 highest_space: SpaceKind::Initial, +395 idle_timeout: match config.max_idle_timeout { +396 None | Some(VarInt(0)) => None, +397 Some(dur) => Some(Duration::from_millis(dur.0)), +398 }, +399 timers: TimerTable::default(), +400 authentication_failures: 0, +401 connection_close_pending: false, 402 -403 receiving_ecn: false, -404 total_authed_packets: 0, -405 -406 next_observed_addr_seq_no: 0u32.into(), -407 -408 streams: StreamsState::new( -409 side, -410 config.max_concurrent_uni_streams, -411 config.max_concurrent_bidi_streams, -412 config.send_window, -413 config.receive_window, -414 config.stream_receive_window, -415 ), -416 datagrams: DatagramState::default(), -417 config, -418 remote_cids: FxHashMap::from_iter([(PathId::ZERO, CidQueue::new(remote_cid))]), -419 rng, -420 path_stats: Default::default(), -421 partial_stats: ConnectionStats::default(), -422 version, -423 -424 // peer params are not yet known, so multipath is not enabled -425 max_concurrent_paths: NonZeroU32::MIN, -426 local_max_path_id: PathId::ZERO, -427 remote_max_path_id: PathId::ZERO, -428 max_path_id_with_cids: PathId::ZERO, -429 abandoned_paths: Default::default(), -430 -431 n0_nat_traversal: Default::default(), -432 qlog, -433 }; -434 if path_validated { -435 this.on_path_validated(PathId::ZERO); -436 } -437 if side.is_client() { -438 // Kick off the connection -439 this.write_crypto(); -440 this.init_0rtt(now); -441 } -442 this.qlog -443 .emit_tuple_assigned(PathId::ZERO, network_path, now); -444 this -445 } -446 -447 /// Returns the next time at which `handle_timeout` should be called -448 /// -449 /// The value returned may change after: -450 /// - the application performed some I/O on the connection -451 /// - a call was made to `handle_event` -452 /// - a call to `poll_transmit` returned `Some` -453 /// - a call was made to `handle_timeout` -454 #[must_use] -455 pub fn poll_timeout(&mut self) -> Option<Instant> { -456 self.timers.peek() -457 } -458 -459 /// Returns application-facing events -460 /// -461 /// Connections should be polled for events after: -462 /// - a call was made to `handle_event` -463 /// - a call was made to `handle_timeout` -464 #[must_use] -465 pub fn poll(&mut self) -> Option<Event> { -466 if let Some(x) = self.events.pop_front() { -467 return Some(x); -468 } -469 -470 if let Some(event) = self.streams.poll() { -471 return Some(Event::Stream(event)); +403 ack_frequency: AckFrequencyState::new(get_max_ack_delay( +404 &TransportParameters::default(), +405 )), +406 +407 receiving_ecn: false, +408 total_authed_packets: 0, +409 +410 next_observed_addr_seq_no: 0u32.into(), +411 +412 streams: StreamsState::new( +413 side, +414 config.max_concurrent_uni_streams, +415 config.max_concurrent_bidi_streams, +416 config.send_window, +417 config.receive_window, +418 config.stream_receive_window, +419 ), +420 datagrams: DatagramState::default(), +421 config, +422 remote_cids: FxHashMap::from_iter([(PathId::ZERO, CidQueue::new(remote_cid))]), +423 rng, +424 path_stats: Default::default(), +425 partial_stats: ConnectionStats::default(), +426 version, +427 +428 // peer params are not yet known, so multipath is not enabled +429 max_concurrent_paths: NonZeroU32::MIN, +430 local_max_path_id: PathId::ZERO, +431 remote_max_path_id: PathId::ZERO, +432 max_path_id_with_cids: PathId::ZERO, +433 abandoned_paths: Default::default(), +434 +435 n0_nat_traversal: Default::default(), +436 qlog, +437 }; +438 if path_validated { +439 this.on_path_validated(PathId::ZERO); +440 } +441 if side.is_client() { +442 // Kick off the connection +443 this.write_crypto(); +444 this.init_0rtt(now); +445 } +446 this.qlog +447 .emit_tuple_assigned(PathId::ZERO, network_path, now); +448 this +449 } +450 +451 /// Returns the next time at which `handle_timeout` should be called +452 /// +453 /// The value returned may change after: +454 /// - the application performed some I/O on the connection +455 /// - a call was made to `handle_event` +456 /// - a call to `poll_transmit` returned `Some` +457 /// - a call was made to `handle_timeout` +458 #[must_use] +459 pub fn poll_timeout(&mut self) -> Option<Instant> { +460 self.timers.peek() +461 } +462 +463 /// Returns application-facing events +464 /// +465 /// Connections should be polled for events after: +466 /// - a call was made to `handle_event` +467 /// - a call was made to `handle_timeout` +468 #[must_use] +469 pub fn poll(&mut self) -> Option<Event> { +470 if let Some(x) = self.events.pop_front() { +471 return Some(x); 472 } 473 -474 if let Some(reason) = self.state.take_error() { -475 return Some(Event::ConnectionLost { reason }); +474 if let Some(event) = self.streams.poll() { +475 return Some(Event::Stream(event)); 476 } 477 -478 None -479 } -480 -481 /// Return endpoint-facing events -482 #[must_use] -483 pub fn poll_endpoint_events(&mut self) -> Option<EndpointEvent> { -484 self.endpoint_events.pop_front().map(EndpointEvent) -485 } -486 -487 /// Provide control over streams -488 #[must_use] -489 pub fn streams(&mut self) -> Streams<'_> { -490 Streams { -491 state: &mut self.streams, -492 conn_state: &self.state, -493 } -494 } -495 -496 /// Provide control over streams -497 #[must_use] -498 pub fn recv_stream(&mut self, id: StreamId) -> RecvStream<'_> { -499 assert!(id.dir() == Dir::Bi || id.initiator() != self.side.side()); -500 RecvStream { -501 id, -502 state: &mut self.streams, -503 pending: &mut self.spaces[SpaceId::Data].pending, -504 } -505 } -506 -507 /// Provide control over streams -508 #[must_use] -509 pub fn send_stream(&mut self, id: StreamId) -> SendStream<'_> { -510 assert!(id.dir() == Dir::Bi || id.initiator() == self.side.side()); -511 SendStream { -512 id, -513 state: &mut self.streams, -514 pending: &mut self.spaces[SpaceId::Data].pending, -515 conn_state: &self.state, -516 } -517 } -518 -519 /// Opens a new path only if no path on the same network path currently exists. -520 /// -521 /// Returns `(path_id, true)` if the path already existed, or `(path_id, false)` -522 /// if was opened. -523 /// -524 /// If `network_path` has no local IP set, then this will open a new path -525 /// if no path exists for this remote address, independent of the existing -526 /// path's local IP. If a local IP is set, it will match against the full -527 /// four-tuple of existing paths. Not setting the local IP avoids having to -528 /// guess which local interface will be used to communicate with the remote, -529 /// should it not be known yet. We assume that if we already have a path to -530 /// the remote, the OS is likely to use the same interface to talk to said remote. -531 /// -532 /// See also [`open_path`]. -533 /// -534 /// [`open_path`]: Connection::open_path -535 pub fn open_path_ensure( -536 &mut self, -537 network_path: FourTuple, -538 initial_status: PathStatus, -539 now: Instant, -540 ) -> Result<(PathId, bool), PathError> { -541 let existing_open_path = self.paths.iter().find(|(id, path)| { -542 network_path.is_probably_same_path(&path.data.network_path) -543 && !self.abandoned_paths.contains(id) -544 }); -545 match existing_open_path { -546 Some((path_id, _state)) => Ok((*path_id, true)), -547 None => Ok((self.open_path(network_path, initial_status, now)?, false)), -548 } -549 } -550 -551 /// Opens a new path. -552 /// -553 /// Further errors might occur and they will be emitted in [`PathEvent::Abandoned`] -554 /// events with this path id. Once the path is opened and can carry application data it -555 /// will be reported using a [`PathEvent::Established`] event. -556 pub fn open_path( -557 &mut self, -558 network_path: FourTuple, -559 initial_status: PathStatus, -560 now: Instant, -561 ) -> Result<PathId, PathError> { -562 if !self.is_multipath_negotiated() { -563 return Err(PathError::MultipathNotNegotiated); -564 } -565 if self.side().is_server() { -566 return Err(PathError::ServerSideNotAllowed); -567 } -568 -569 let max_abandoned = self.abandoned_paths.max(); -570 let max_used = self.paths.keys().last().copied(); -571 let path_id = max_abandoned -572 .max(max_used) -573 .unwrap_or(PathId::ZERO) -574 .saturating_add(1u8); -575 -576 if Some(path_id) > self.max_path_id() { -577 return Err(PathError::MaxPathIdReached); -578 } -579 if path_id > self.remote_max_path_id { -580 self.spaces[SpaceId::Data].pending.paths_blocked = true; +478 if let Some(reason) = self.state.take_error() { +479 return Some(Event::ConnectionLost { reason }); +480 } +481 +482 None +483 } +484 +485 /// Return endpoint-facing events +486 #[must_use] +487 pub fn poll_endpoint_events(&mut self) -> Option<EndpointEvent> { +488 self.endpoint_events.pop_front().map(EndpointEvent) +489 } +490 +491 /// Provide control over streams +492 #[must_use] +493 pub fn streams(&mut self) -> Streams<'_> { +494 Streams { +495 state: &mut self.streams, +496 conn_state: &self.state, +497 } +498 } +499 +500 /// Provide control over streams +501 #[must_use] +502 pub fn recv_stream(&mut self, id: StreamId) -> RecvStream<'_> { +503 assert!(id.dir() == Dir::Bi || id.initiator() != self.side.side()); +504 RecvStream { +505 id, +506 state: &mut self.streams, +507 pending: &mut self.spaces[SpaceId::Data].pending, +508 } +509 } +510 +511 /// Provide control over streams +512 #[must_use] +513 pub fn send_stream(&mut self, id: StreamId) -> SendStream<'_> { +514 assert!(id.dir() == Dir::Bi || id.initiator() == self.side.side()); +515 SendStream { +516 id, +517 state: &mut self.streams, +518 pending: &mut self.spaces[SpaceId::Data].pending, +519 conn_state: &self.state, +520 } +521 } +522 +523 /// Opens a new path only if no path on the same network path currently exists. +524 /// +525 /// Returns `(path_id, true)` if the path already existed, or `(path_id, false)` +526 /// if was opened. +527 /// +528 /// If `network_path` has no local IP set, then this will open a new path +529 /// if no path exists for this remote address, independent of the existing +530 /// path's local IP. If a local IP is set, it will match against the full +531 /// four-tuple of existing paths. Not setting the local IP avoids having to +532 /// guess which local interface will be used to communicate with the remote, +533 /// should it not be known yet. We assume that if we already have a path to +534 /// the remote, the OS is likely to use the same interface to talk to said remote. +535 /// +536 /// See also [`open_path`]. +537 /// +538 /// [`open_path`]: Connection::open_path +539 pub fn open_path_ensure( +540 &mut self, +541 network_path: FourTuple, +542 initial_status: PathStatus, +543 now: Instant, +544 ) -> Result<(PathId, bool), PathError> { +545 let existing_open_path = self.paths.iter().find(|(id, path)| { +546 network_path.is_probably_same_path(&path.data.network_path) +547 && !self.abandoned_paths.contains(id) +548 }); +549 match existing_open_path { +550 Some((path_id, _state)) => Ok((*path_id, true)), +551 None => Ok((self.open_path(network_path, initial_status, now)?, false)), +552 } +553 } +554 +555 /// Opens a new path. +556 /// +557 /// Further errors might occur and they will be emitted in [`PathEvent::Abandoned`] +558 /// events with this path id. Once the path is opened and can carry application data it +559 /// will be reported using a [`PathEvent::Established`] event. +560 pub fn open_path( +561 &mut self, +562 network_path: FourTuple, +563 initial_status: PathStatus, +564 now: Instant, +565 ) -> Result<PathId, PathError> { +566 if !self.is_multipath_negotiated() { +567 return Err(PathError::MultipathNotNegotiated); +568 } +569 if self.side().is_server() { +570 return Err(PathError::ServerSideNotAllowed); +571 } +572 +573 let max_abandoned = self.abandoned_paths.max(); +574 let max_used = self.paths.keys().last().copied(); +575 let path_id = max_abandoned +576 .max(max_used) +577 .unwrap_or(PathId::ZERO) +578 .saturating_add(1u8); +579 +580 if Some(path_id) > self.max_path_id() { 581 return Err(PathError::MaxPathIdReached); 582 } -583 if self -584 .remote_cids -585 .get(&path_id) -586 .map(CidQueue::active) -587 .is_none() -588 { -589 self.spaces[SpaceId::Data] -590 .pending -591 .path_cids_blocked -592 .insert(path_id); -593 return Err(PathError::RemoteCidsExhausted); -594 } -595 -596 let path = self.ensure_path(path_id, network_path, now, None); -597 path.status.local_update(initial_status); -598 -599 Ok(path_id) -600 } -601 -602 /// Closes a path and sends a PATH_ABANDON frame with the passed error code. -603 /// -604 /// Returns [`ClosePathError::LastOpenPath`] if this is the last open path. -605 /// It does allow closing paths which have not yet been opened, as e.g. is the case -606 /// when receiving a PATH_ABANDON from the peer for a path that was never opened locally. -607 pub fn close_path( -608 &mut self, -609 now: Instant, -610 path_id: PathId, -611 error_code: VarInt, -612 ) -> Result<(), ClosePathError> { -613 self.close_path_inner( -614 now, -615 path_id, -616 PathAbandonReason::ApplicationClosed { error_code }, -617 ) -618 } -619 -620 /// Closes a path and sends a PATH_ABANDON frame. -621 /// -622 /// Other than [`Self::close_path`] this allows to specify the reason for the path being closed. -623 /// Internally, this should be used over [`Self::close_path`]. -624 pub(crate) fn close_path_inner( -625 &mut self, -626 now: Instant, -627 path_id: PathId, -628 reason: PathAbandonReason, -629 ) -> Result<(), ClosePathError> { -630 if self.state.is_drained() { -631 return Ok(()); -632 } -633 -634 if !self.is_multipath_negotiated() { -635 return Err(ClosePathError::MultipathNotNegotiated); +583 if path_id > self.remote_max_path_id { +584 self.spaces[SpaceId::Data].pending.paths_blocked = true; +585 return Err(PathError::MaxPathIdReached); +586 } +587 if self +588 .remote_cids +589 .get(&path_id) +590 .map(CidQueue::active) +591 .is_none() +592 { +593 self.spaces[SpaceId::Data] +594 .pending +595 .path_cids_blocked +596 .insert(path_id); +597 return Err(PathError::RemoteCidsExhausted); +598 } +599 +600 let path = self.ensure_path(path_id, network_path, now, None); +601 path.status.local_update(initial_status); +602 +603 Ok(path_id) +604 } +605 +606 /// Closes a path and sends a PATH_ABANDON frame with the passed error code. +607 /// +608 /// Returns [`ClosePathError::LastOpenPath`] if this is the last open path. +609 /// It does allow closing paths which have not yet been opened, as e.g. is the case +610 /// when receiving a PATH_ABANDON from the peer for a path that was never opened locally. +611 pub fn close_path( +612 &mut self, +613 now: Instant, +614 path_id: PathId, +615 error_code: VarInt, +616 ) -> Result<(), ClosePathError> { +617 self.close_path_inner( +618 now, +619 path_id, +620 PathAbandonReason::ApplicationClosed { error_code }, +621 ) +622 } +623 +624 /// Closes a path and sends a PATH_ABANDON frame. +625 /// +626 /// Other than [`Self::close_path`] this allows to specify the reason for the path being closed. +627 /// Internally, this should be used over [`Self::close_path`]. +628 pub(crate) fn close_path_inner( +629 &mut self, +630 now: Instant, +631 path_id: PathId, +632 reason: PathAbandonReason, +633 ) -> Result<(), ClosePathError> { +634 if self.state.is_drained() { +635 return Ok(()); 636 } -637 if self.abandoned_paths.contains(&path_id) -638 || Some(path_id) > self.max_path_id() -639 || !self.paths.contains_key(&path_id) -640 { -641 return Err(ClosePathError::ClosedPath); -642 } -643 -644 let is_last_path = !self -645 .paths -646 .keys() -647 .any(|id| *id != path_id && !self.abandoned_paths.contains(id)); -648 -649 if is_last_path && !reason.is_remote() { -650 return Err(ClosePathError::LastOpenPath); -651 } +637 +638 if !self.is_multipath_negotiated() { +639 return Err(ClosePathError::MultipathNotNegotiated); +640 } +641 if self.abandoned_paths.contains(&path_id) +642 || Some(path_id) > self.max_path_id() +643 || !self.paths.contains_key(&path_id) +644 { +645 return Err(ClosePathError::ClosedPath); +646 } +647 +648 let is_last_path = !self +649 .paths +650 .keys() +651 .any(|id| *id != path_id && !self.abandoned_paths.contains(id)); 652 -653 self.abandon_path(now, path_id, reason); -654 -655 // When the remote abandons our last path, start a grace timer to allow -656 // the application to open a replacement path. -657 // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-21.html#section-3.4-8 -658 if is_last_path { -659 // The spec suggests 1 PTO, but we use 3 * PTO to account for -660 // packet loss when opening a replacement path. Uses initial RTT -661 // since the abandoned path's RTT estimate is no longer valid. -662 let rtt = RttEstimator::new(self.config.initial_rtt); -663 let pto = rtt.pto_base() + self.ack_frequency.max_ack_delay_for_pto(); -664 let grace = pto * 3; -665 self.timers.set( -666 Timer::Conn(ConnTimer::NoAvailablePath), -667 now + grace, -668 self.qlog.with_time(now), -669 ); -670 } -671 -672 Ok(()) -673 } -674 -675 /// Unconditionally abandon a path. -676 /// -677 /// Only to be called once sure this path should be abandoned, all checks -678 /// should have happened before calling this. -679 fn abandon_path(&mut self, now: Instant, path_id: PathId, reason: PathAbandonReason) { -680 trace!(%path_id, ?reason, "abandoning path"); -681 -682 let pending_space = &mut self.spaces[SpaceId::Data].pending; -683 // Send PATH_ABANDON -684 pending_space -685 .path_abandon -686 .insert(path_id, reason.error_code()); -687 -688 // Remove pending NEW CIDs for this path -689 pending_space.new_cids.retain(|cid| cid.path_id != path_id); -690 pending_space.path_cids_blocked.retain(|&id| id != path_id); -691 pending_space.path_status.retain(|&id| id != path_id); -692 -693 // Cleanup retransmits across ALL paths (CIDs for path_id may have been transmitted on other paths) -694 for space in self.spaces[SpaceId::Data].iter_paths_mut() { -695 for sent_packet in space.sent_packets.values_mut() { -696 if let Some(retransmits) = sent_packet.retransmits.get_mut() { -697 retransmits.new_cids.retain(|cid| cid.path_id != path_id); -698 retransmits.path_cids_blocked.retain(|&id| id != path_id); -699 retransmits.path_status.retain(|&id| id != path_id); -700 } -701 } -702 } -703 -704 // We can't send anything on abandoned paths, so we set -705 // tail-loss probes to zero. -706 // This likely doesn't do much, as the path won't even be tried for sending -707 // in poll_transmit after the path is abandoned. -708 self.spaces[SpaceId::Data].for_path(path_id).loss_probes = 0; -709 -710 // Note: remote CIDs are NOT removed here. They are removed when the PATH_ABANDON -711 // frame is actually written to a packet (in populate_packet). This allows sending -712 // PATH_ABANDON on the abandoned path itself when no other path exists (#509). -713 debug_assert!(!self.state.is_drained()); // requirement for endpoint_events, checked in `close_path_inner` -714 self.endpoint_events -715 .push_back(EndpointEventInner::RetireResetToken(path_id)); -716 -717 self.abandoned_paths.insert(path_id); -718 -719 for timer in timer::PathTimer::VALUES { -720 // match for completeness -721 let keep_timer = match timer { -722 // These timers deal with sending and receiving PATH_CHALLENGE and -723 // PATH_RESPONSE, but now that the path is abandoned, we no longer care about -724 // these frames or their timing -725 PathTimer::PathValidationFailed -726 | PathTimer::PathChallengeLost -727 | PathTimer::AbandonFromValidation => false, -728 // These timers deal with the lifetime of the path. Now that the path is abandoned, -729 // these are not relevant. -730 PathTimer::PathKeepAlive | PathTimer::PathIdle => false, -731 // The path has already been informed that outstanding acks should be sent -732 // immediately -733 PathTimer::MaxAckDelay => false, -734 // This timer should not be set, for completeness it's not kept as it's set when -735 // the PATH_ABANDON frame is sent. -736 PathTimer::PathDrained => false, -737 // Sent packets still need to be identified as lost to trigger timely -738 // retransmission. -739 PathTimer::LossDetection => true, -740 // This path should not be used for sending after the PATH_ABANDON frame is sent. -741 // However, any outstanding data that should be sent before PATH_ABANDON, should -742 // still respect pacing. -743 PathTimer::Pacing => true, -744 }; -745 -746 if !keep_timer { -747 let qlog = self.qlog.with_time(now); -748 self.timers.stop(Timer::PerPath(path_id, timer), qlog); -749 } -750 } -751 -752 // Set the loss detection timer again, as now it should only be set -753 // for time-based loss detection, not tail-loss probes, but currently it -754 // could still be set to a tail-loss probe. -755 // This will reset it to the next time-based loss time, if applicable. -756 self.set_loss_detection_timer(now, path_id); -757 -758 // Emit event to the application. -759 self.events.push_back(Event::Path(PathEvent::Abandoned { -760 id: path_id, -761 reason, -762 })); -763 } -764 -765 /// Gets the [`PathData`] for a known [`PathId`]. -766 /// -767 /// Will panic if the path_id does not reference any known path. -768 #[track_caller] -769 fn path_data(&self, path_id: PathId) -> &PathData { -770 if let Some(data) = self.paths.get(&path_id) { -771 &data.data -772 } else { -773 panic!( -774 "unknown path: {path_id}, currently known paths: {:?}", -775 self.paths.keys().collect::<Vec<_>>() -776 ); -777 } -778 } -779 -780 /// Gets the [`PathData`] for a known [`PathId`]. -781 /// -782 /// Will panic if the path_id does not reference any known path. -783 #[track_caller] -784 fn path_data_mut(&mut self, path_id: PathId) -> &mut PathData { -785 &mut self.paths.get_mut(&path_id).expect("known path").data -786 } -787 -788 /// Gets a reference to the [`PathData`] for a [`PathId`] -789 fn path(&self, path_id: PathId) -> Option<&PathData> { -790 self.paths.get(&path_id).map(|path_state| &path_state.data) -791 } -792 -793 /// Gets a mutable reference to the [`PathData`] for a [`PathId`] -794 fn path_mut(&mut self, path_id: PathId) -> Option<&mut PathData> { -795 self.paths -796 .get_mut(&path_id) -797 .map(|path_state| &mut path_state.data) -798 } -799 -800 /// Returns all known paths. -801 /// -802 /// There is no guarantee any of these paths are open or usable. -803 pub fn paths(&self) -> Vec<PathId> { -804 self.paths.keys().copied().collect() -805 } -806 -807 /// Gets the local [`PathStatus`] for a known [`PathId`] -808 pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath> { -809 self.path(path_id) -810 .map(PathData::local_status) -811 .ok_or(ClosedPath { _private: () }) -812 } -813 -814 /// Returns the path's network path represented as a 4-tuple. -815 pub fn network_path(&self, path_id: PathId) -> Result<FourTuple, ClosedPath> { -816 self.path(path_id) -817 .map(|path| path.network_path) -818 .ok_or(ClosedPath { _private: () }) -819 } -820 -821 /// Sets the [`PathStatus`] for a known [`PathId`] -822 /// -823 /// Returns the previous path status on success. -824 pub fn set_path_status( -825 &mut self, -826 path_id: PathId, -827 status: PathStatus, -828 ) -> Result<PathStatus, SetPathStatusError> { -829 if !self.is_multipath_negotiated() { -830 return Err(SetPathStatusError::MultipathNotNegotiated); -831 } -832 let path = self -833 .path_mut(path_id) -834 .ok_or(SetPathStatusError::ClosedPath)?; -835 let prev = match path.status.local_update(status) { -836 Some(prev) => { -837 self.spaces[SpaceId::Data] -838 .pending -839 .path_status -840 .insert(path_id); -841 prev -842 } -843 None => path.local_status(), -844 }; -845 Ok(prev) -846 } -847 -848 /// Returns the remote path status -849 // TODO(flub): Probably should also be some kind of path event? Not even sure if I like -850 // this as an API, but for now it allows me to write a test easily. -851 // TODO(flub): Technically this should be a Result<Option<PathSTatus>>? -852 pub fn remote_path_status(&self, path_id: PathId) -> Option<PathStatus> { -853 self.path(path_id).and_then(|path| path.remote_status()) -854 } -855 -856 /// Sets the max_idle_timeout for a specific path. -857 /// -858 /// The PathIdle timer is immediately re-armed accounting for already-elapsed -859 /// idle time. Setting `None` disables the timeout and stops the timer. -860 /// -861 /// See [`TransportConfig::default_path_max_idle_timeout`] for details. -862 /// -863 /// Returns the previous value of the setting. -864 pub fn set_path_max_idle_timeout( -865 &mut self, -866 now: Instant, -867 path_id: PathId, -868 timeout: Option<Duration>, -869 ) -> Result<Option<Duration>, ClosedPath> { -870 let path = self -871 .paths -872 .get_mut(&path_id) -873 .ok_or(ClosedPath { _private: () })?; -874 let prev = std::mem::replace(&mut path.data.idle_timeout, timeout); -875 -876 // Adjust the PathIdle timer, accounting for already-elapsed idle time. -877 if !self.state.is_closed() { -878 if let Some(new_timeout) = timeout { -879 let timer = Timer::PerPath(path_id, PathTimer::PathIdle); -880 let deadline = match (prev, self.timers.get(timer)) { -881 (Some(old_timeout), Some(old_deadline)) => { -882 let last_activity = old_deadline.checked_sub(old_timeout).unwrap_or(now); -883 last_activity + new_timeout -884 } -885 _ => now + new_timeout, -886 }; -887 self.timers.set(timer, deadline, self.qlog.with_time(now)); -888 } else { -889 self.timers.stop( -890 Timer::PerPath(path_id, PathTimer::PathIdle), -891 self.qlog.with_time(now), -892 ); -893 } -894 } -895 -896 Ok(prev) -897 } -898 -899 /// Sets the keep_alive_interval for a specific path -900 /// -901 /// See [`TransportConfig::default_path_keep_alive_interval`] for details. -902 /// -903 /// Returns the previous value of the setting. -904 pub fn set_path_keep_alive_interval( -905 &mut self, -906 path_id: PathId, -907 interval: Option<Duration>, -908 ) -> Result<Option<Duration>, ClosedPath> { -909 let path = self -910 .paths -911 .get_mut(&path_id) -912 .ok_or(ClosedPath { _private: () })?; -913 Ok(std::mem::replace(&mut path.data.keep_alive, interval)) -914 } -915 -916 /// Find an open, validated path that's on the same network path as the given network path. -917 /// -918 /// Returns the first path matching, even if there's multiple. -919 fn find_validated_path_on_network_path( -920 &self, -921 network_path: FourTuple, -922 ) -> Option<(&PathId, &PathState)> { -923 self.paths.iter().find(|(path_id, path_state)| { -924 path_state.data.validated -925 // Would this use the same network path, if network_path were used to send right now? -926 && network_path.is_probably_same_path(&path_state.data.network_path) -927 && !self.abandoned_paths.contains(path_id) -928 }) -929 // TODO(@divma): we might want to ensure the path has been recently active to consider the -930 // address validated -931 // matheus23: Perhaps looking at !self.abandoned_paths.contains(path_id) is enough, given keep-alives? -932 } -933 -934 /// Creates the [`PathData`] for a new [`PathId`]. -935 /// -936 /// Called for incoming packets as well as when opening a new path locally. -937 fn ensure_path( -938 &mut self, -939 path_id: PathId, -940 network_path: FourTuple, -941 now: Instant, -942 pn: Option<u64>, -943 ) -> &mut PathData { -944 let valid_path = self.find_validated_path_on_network_path(network_path); -945 let validated = valid_path.is_some(); -946 let initial_rtt = valid_path.map(|(_, path)| path.data.rtt.conservative()); -947 let vacant_entry = match self.paths.entry(path_id) { -948 btree_map::Entry::Vacant(vacant_entry) => vacant_entry, -949 btree_map::Entry::Occupied(occupied_entry) => { -950 return &mut occupied_entry.into_mut().data; -951 } -952 }; -953 -954 debug!(%validated, %path_id, %network_path, "path added"); -955 -956 // A new path was added. Cancel any pending NoAvailablePath grace timer. -957 self.timers.stop( -958 Timer::Conn(ConnTimer::NoAvailablePath), -959 self.qlog.with_time(now), -960 ); -961 let peer_max_udp_payload_size = -962 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX); -963 self.path_generation_counter = self.path_generation_counter.wrapping_add(1); -964 let mut data = PathData::new( -965 network_path, -966 self.allow_mtud, -967 Some(peer_max_udp_payload_size), -968 self.path_generation_counter, -969 now, -970 &self.config, -971 ); -972 -973 data.validated = validated; -974 if let Some(initial_rtt) = initial_rtt { -975 data.rtt.reset_initial_rtt(initial_rtt); -976 } -977 -978 // To open a path locally we need to send a packet on the path. Sending a challenge -979 // guarantees this. -980 data.pending_on_path_challenge = true; +653 if is_last_path && !reason.is_remote() { +654 return Err(ClosePathError::LastOpenPath); +655 } +656 +657 self.abandon_path(now, path_id, reason); +658 +659 // When the remote abandons our last path, start a grace timer to allow +660 // the application to open a replacement path. +661 // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-21.html#section-3.4-8 +662 if is_last_path { +663 // The spec suggests 1 PTO, but we use 3 * PTO to account for +664 // packet loss when opening a replacement path. Uses initial RTT +665 // since the abandoned path's RTT estimate is no longer valid. +666 let rtt = RttEstimator::new(self.config.initial_rtt); +667 let pto = rtt.pto_base() + self.ack_frequency.max_ack_delay_for_pto(); +668 let grace = pto * 3; +669 self.timers.set( +670 Timer::Conn(ConnTimer::NoAvailablePath), +671 now + grace, +672 self.qlog.with_time(now), +673 ); +674 } +675 +676 Ok(()) +677 } +678 +679 /// Unconditionally abandon a path. +680 /// +681 /// Only to be called once sure this path should be abandoned, all checks +682 /// should have happened before calling this. +683 fn abandon_path(&mut self, now: Instant, path_id: PathId, reason: PathAbandonReason) { +684 trace!(%path_id, ?reason, "abandoning path"); +685 +686 let pending_space = &mut self.spaces[SpaceId::Data].pending; +687 // Send PATH_ABANDON +688 pending_space +689 .path_abandon +690 .insert(path_id, reason.error_code()); +691 +692 // Remove pending NEW CIDs for this path +693 pending_space.new_cids.retain(|cid| cid.path_id != path_id); +694 pending_space.path_cids_blocked.retain(|&id| id != path_id); +695 pending_space.path_status.retain(|&id| id != path_id); +696 +697 // Cleanup retransmits across ALL paths (CIDs for path_id may have been transmitted on other paths) +698 for space in self.spaces[SpaceId::Data].iter_paths_mut() { +699 for sent_packet in space.sent_packets.values_mut() { +700 if let Some(retransmits) = sent_packet.retransmits.get_mut() { +701 retransmits.new_cids.retain(|cid| cid.path_id != path_id); +702 retransmits.path_cids_blocked.retain(|&id| id != path_id); +703 retransmits.path_status.retain(|&id| id != path_id); +704 } +705 } +706 } +707 +708 // We can't send anything on abandoned paths, so we set +709 // tail-loss probes to zero. +710 // This likely doesn't do much, as the path won't even be tried for sending +711 // in poll_transmit after the path is abandoned. +712 self.spaces[SpaceId::Data].for_path(path_id).loss_probes = 0; +713 +714 // Note: remote CIDs are NOT removed here. They are removed when the PATH_ABANDON +715 // frame is actually written to a packet (in populate_packet). This allows sending +716 // PATH_ABANDON on the abandoned path itself when no other path exists (#509). +717 debug_assert!(!self.state.is_drained()); // requirement for endpoint_events, checked in `close_path_inner` +718 self.endpoint_events +719 .push_back(EndpointEventInner::RetireResetToken(path_id)); +720 +721 self.abandoned_paths.insert(path_id); +722 +723 for timer in timer::PathTimer::VALUES { +724 // match for completeness +725 let keep_timer = match timer { +726 // These timers deal with sending and receiving PATH_CHALLENGE and +727 // PATH_RESPONSE, but now that the path is abandoned, we no longer care about +728 // these frames or their timing +729 PathTimer::PathValidationFailed +730 | PathTimer::PathChallengeLost +731 | PathTimer::AbandonFromValidation => false, +732 // These timers deal with the lifetime of the path. Now that the path is abandoned, +733 // these are not relevant. +734 PathTimer::PathKeepAlive | PathTimer::PathIdle => false, +735 // The path has already been informed that outstanding acks should be sent +736 // immediately +737 PathTimer::MaxAckDelay => false, +738 // This timer should not be set, for completeness it's not kept as it's set when +739 // the PATH_ABANDON frame is sent. +740 PathTimer::PathDrained => false, +741 // Sent packets still need to be identified as lost to trigger timely +742 // retransmission. +743 PathTimer::LossDetection => true, +744 // This path should not be used for sending after the PATH_ABANDON frame is sent. +745 // However, any outstanding data that should be sent before PATH_ABANDON, should +746 // still respect pacing. +747 PathTimer::Pacing => true, +748 }; +749 +750 if !keep_timer { +751 let qlog = self.qlog.with_time(now); +752 self.timers.stop(Timer::PerPath(path_id, timer), qlog); +753 } +754 } +755 +756 // Set the loss detection timer again, as now it should only be set +757 // for time-based loss detection, not tail-loss probes, but currently it +758 // could still be set to a tail-loss probe. +759 // This will reset it to the next time-based loss time, if applicable. +760 self.set_loss_detection_timer(now, path_id); +761 +762 // Emit event to the application. +763 self.events.push_back(Event::Path(PathEvent::Abandoned { +764 id: path_id, +765 reason, +766 })); +767 } +768 +769 /// Gets the [`PathData`] for a known [`PathId`]. +770 /// +771 /// Will panic if the path_id does not reference any known path. +772 #[track_caller] +773 fn path_data(&self, path_id: PathId) -> &PathData { +774 if let Some(data) = self.paths.get(&path_id) { +775 &data.data +776 } else { +777 panic!( +778 "unknown path: {path_id}, currently known paths: {:?}", +779 self.paths.keys().collect::<Vec<_>>() +780 ); +781 } +782 } +783 +784 /// Gets the [`PathData`] for a known [`PathId`]. +785 /// +786 /// Will panic if the path_id does not reference any known path. +787 #[track_caller] +788 fn path_data_mut(&mut self, path_id: PathId) -> &mut PathData { +789 &mut self.paths.get_mut(&path_id).expect("known path").data +790 } +791 +792 /// Gets a reference to the [`PathData`] for a [`PathId`] +793 fn path(&self, path_id: PathId) -> Option<&PathData> { +794 self.paths.get(&path_id).map(|path_state| &path_state.data) +795 } +796 +797 /// Gets a mutable reference to the [`PathData`] for a [`PathId`] +798 fn path_mut(&mut self, path_id: PathId) -> Option<&mut PathData> { +799 self.paths +800 .get_mut(&path_id) +801 .map(|path_state| &mut path_state.data) +802 } +803 +804 /// Returns all known paths. +805 /// +806 /// There is no guarantee any of these paths are open or usable. +807 pub fn paths(&self) -> Vec<PathId> { +808 self.paths.keys().copied().collect() +809 } +810 +811 /// Gets the local [`PathStatus`] for a known [`PathId`] +812 pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath> { +813 self.path(path_id) +814 .map(PathData::local_status) +815 .ok_or(ClosedPath { _private: () }) +816 } +817 +818 /// Returns the path's network path represented as a 4-tuple. +819 pub fn network_path(&self, path_id: PathId) -> Result<FourTuple, ClosedPath> { +820 self.path(path_id) +821 .map(|path| path.network_path) +822 .ok_or(ClosedPath { _private: () }) +823 } +824 +825 /// Sets the [`PathStatus`] for a known [`PathId`] +826 /// +827 /// Returns the previous path status on success. +828 pub fn set_path_status( +829 &mut self, +830 path_id: PathId, +831 status: PathStatus, +832 ) -> Result<PathStatus, SetPathStatusError> { +833 if !self.is_multipath_negotiated() { +834 return Err(SetPathStatusError::MultipathNotNegotiated); +835 } +836 let path = self +837 .path_mut(path_id) +838 .ok_or(SetPathStatusError::ClosedPath)?; +839 let prev = match path.status.local_update(status) { +840 Some(prev) => { +841 self.spaces[SpaceId::Data] +842 .pending +843 .path_status +844 .insert(path_id); +845 prev +846 } +847 None => path.local_status(), +848 }; +849 Ok(prev) +850 } +851 +852 /// Returns the remote path status +853 // TODO(flub): Probably should also be some kind of path event? Not even sure if I like +854 // this as an API, but for now it allows me to write a test easily. +855 // TODO(flub): Technically this should be a Result<Option<PathSTatus>>? +856 pub fn remote_path_status(&self, path_id: PathId) -> Option<PathStatus> { +857 self.path(path_id).and_then(|path| path.remote_status()) +858 } +859 +860 /// Sets the max_idle_timeout for a specific path. +861 /// +862 /// The PathIdle timer is immediately re-armed accounting for already-elapsed +863 /// idle time. Setting `None` disables the timeout and stops the timer. +864 /// +865 /// See [`TransportConfig::default_path_max_idle_timeout`] for details. +866 /// +867 /// Returns the previous value of the setting. +868 pub fn set_path_max_idle_timeout( +869 &mut self, +870 now: Instant, +871 path_id: PathId, +872 timeout: Option<Duration>, +873 ) -> Result<Option<Duration>, ClosedPath> { +874 let path = self +875 .paths +876 .get_mut(&path_id) +877 .ok_or(ClosedPath { _private: () })?; +878 let prev = std::mem::replace(&mut path.data.idle_timeout, timeout); +879 +880 // Adjust the PathIdle timer, accounting for already-elapsed idle time. +881 if !self.state.is_closed() { +882 if let Some(new_timeout) = timeout { +883 let timer = Timer::PerPath(path_id, PathTimer::PathIdle); +884 let deadline = match (prev, self.timers.get(timer)) { +885 (Some(old_timeout), Some(old_deadline)) => { +886 let last_activity = old_deadline.checked_sub(old_timeout).unwrap_or(now); +887 last_activity + new_timeout +888 } +889 _ => now + new_timeout, +890 }; +891 self.timers.set(timer, deadline, self.qlog.with_time(now)); +892 } else { +893 self.timers.stop( +894 Timer::PerPath(path_id, PathTimer::PathIdle), +895 self.qlog.with_time(now), +896 ); +897 } +898 } +899 +900 Ok(prev) +901 } +902 +903 /// Sets the keep_alive_interval for a specific path +904 /// +905 /// See [`TransportConfig::default_path_keep_alive_interval`] for details. +906 /// +907 /// Returns the previous value of the setting. +908 pub fn set_path_keep_alive_interval( +909 &mut self, +910 path_id: PathId, +911 interval: Option<Duration>, +912 ) -> Result<Option<Duration>, ClosedPath> { +913 let path = self +914 .paths +915 .get_mut(&path_id) +916 .ok_or(ClosedPath { _private: () })?; +917 Ok(std::mem::replace(&mut path.data.keep_alive, interval)) +918 } +919 +920 /// Find an open, validated path that's on the same network path as the given network path. +921 /// +922 /// Returns the first path matching, even if there's multiple. +923 fn find_validated_path_on_network_path( +924 &self, +925 network_path: FourTuple, +926 ) -> Option<(&PathId, &PathState)> { +927 self.paths.iter().find(|(path_id, path_state)| { +928 path_state.data.validated +929 // Would this use the same network path, if network_path were used to send right now? +930 && network_path.is_probably_same_path(&path_state.data.network_path) +931 && !self.abandoned_paths.contains(path_id) +932 }) +933 // TODO(@divma): we might want to ensure the path has been recently active to consider the +934 // address validated +935 // matheus23: Perhaps looking at !self.abandoned_paths.contains(path_id) is enough, given keep-alives? +936 } +937 +938 /// Creates the [`PathData`] for a new [`PathId`]. +939 /// +940 /// Called for incoming packets as well as when opening a new path locally. +941 fn ensure_path( +942 &mut self, +943 path_id: PathId, +944 network_path: FourTuple, +945 now: Instant, +946 pn: Option<u64>, +947 ) -> &mut PathData { +948 let valid_path = self.find_validated_path_on_network_path(network_path); +949 let validated = valid_path.is_some(); +950 let initial_rtt = valid_path.map(|(_, path)| path.data.rtt.conservative()); +951 let vacant_entry = match self.paths.entry(path_id) { +952 btree_map::Entry::Vacant(vacant_entry) => vacant_entry, +953 btree_map::Entry::Occupied(occupied_entry) => { +954 return &mut occupied_entry.into_mut().data; +955 } +956 }; +957 +958 debug!(%validated, %path_id, %network_path, "path added"); +959 +960 // A new path was added. Cancel any pending NoAvailablePath grace timer. +961 self.timers.stop( +962 Timer::Conn(ConnTimer::NoAvailablePath), +963 self.qlog.with_time(now), +964 ); +965 let peer_max_udp_payload_size = +966 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX); +967 self.path_generation_counter = self.path_generation_counter.wrapping_add(1); +968 let mut data = PathData::new( +969 network_path, +970 self.allow_mtud, +971 Some(peer_max_udp_payload_size), +972 self.path_generation_counter, +973 now, +974 &self.config, +975 ); +976 +977 data.validated = validated; +978 if let Some(initial_rtt) = initial_rtt { +979 data.rtt.reset_initial_rtt(initial_rtt); +980 } 981 -982 let path = vacant_entry.insert(PathState { data, prev: None }); -983 -984 let mut pn_space = spaces::PacketNumberSpace::new(now, SpaceId::Data, &mut self.rng); -985 if let Some(pn) = pn { -986 pn_space.dedup.insert(pn); -987 } -988 self.spaces[SpaceId::Data] -989 .number_spaces -990 .insert(path_id, pn_space); -991 self.qlog.emit_tuple_assigned(path_id, network_path, now); -992 -993 // If the remote opened this path we may not have CIDs for it. For locally opened -994 // paths the caller should have already made sure we have CIDs and refused to open -995 // it if there were none. -996 if !self.remote_cids.contains_key(&path_id) { -997 debug!(%path_id, "Remote opened path without issuing CIDs"); -998 self.spaces[SpaceId::Data] -999 .pending -1000 .path_cids_blocked -1001 .insert(path_id); -1002 // Do not abandon this path right away. CIDs might be in-flight still and arrive -1003 // soon. It is up to the remote to handle this situation. -1004 } -1005 -1006 &mut path.data -1007 } -1008 -1009 /// Returns packets to transmit -1010 /// -1011 /// Connections should be polled for transmit after: -1012 /// - the application performed some I/O on the connection -1013 /// - a call was made to `handle_event` -1014 /// - a call was made to `handle_timeout` -1015 /// -1016 /// `max_datagrams` specifies how many datagrams can be returned inside a -1017 /// single Transmit using GSO. This must be at least 1. -1018 #[must_use] -1019 pub fn poll_transmit( -1020 &mut self, -1021 now: Instant, -1022 max_datagrams: NonZeroUsize, -1023 buf: &mut Vec<u8>, -1024 ) -> Option<Transmit> { -1025 let max_datagrams = match self.config.enable_segmentation_offload { -1026 false => NonZeroUsize::MIN, -1027 true => max_datagrams, -1028 }; -1029 -1030 // Each call to poll_transmit can only send datagrams to one destination, because -1031 // all datagrams in a GSO batch are for the same destination. Therefore only -1032 // datagrams for one destination address are produced for each poll_transmit call. +982 // To open a path locally we need to send a packet on the path. Sending a challenge +983 // guarantees this. +984 data.pending_challenge = true; +985 +986 let path = vacant_entry.insert(PathState { data, prev: None }); +987 +988 let mut pn_space = spaces::PacketNumberSpace::new(now, SpaceId::Data, &mut self.rng); +989 if let Some(pn) = pn { +990 pn_space.dedup.insert(pn); +991 } +992 self.spaces[SpaceId::Data] +993 .number_spaces +994 .insert(path_id, pn_space); +995 self.qlog.emit_tuple_assigned(path_id, network_path, now); +996 +997 // If the remote opened this path we may not have CIDs for it. For locally opened +998 // paths the caller should have already made sure we have CIDs and refused to open +999 // it if there were none. +1000 if !self.remote_cids.contains_key(&path_id) { +1001 debug!(%path_id, "Remote opened path without issuing CIDs"); +1002 self.spaces[SpaceId::Data] +1003 .pending +1004 .path_cids_blocked +1005 .insert(path_id); +1006 // Do not abandon this path right away. CIDs might be in-flight still and arrive +1007 // soon. It is up to the remote to handle this situation. +1008 } +1009 +1010 &mut path.data +1011 } +1012 +1013 /// Returns packets to transmit +1014 /// +1015 /// Connections should be polled for transmit after: +1016 /// - the application performed some I/O on the connection +1017 /// - a call was made to `handle_event` +1018 /// - a call was made to `handle_timeout` +1019 /// +1020 /// `max_datagrams` specifies how many datagrams can be returned inside a +1021 /// single Transmit using GSO. This must be at least 1. +1022 #[must_use] +1023 pub fn poll_transmit( +1024 &mut self, +1025 now: Instant, +1026 max_datagrams: NonZeroUsize, +1027 buf: &mut Vec<u8>, +1028 ) -> Option<Transmit> { +1029 let max_datagrams = match self.config.enable_segmentation_offload { +1030 false => NonZeroUsize::MIN, +1031 true => max_datagrams, +1032 }; 1033 -1034 // Check whether we need to send a close message -1035 let connection_close_pending = match self.state.as_type() { -1036 StateType::Drained => { -1037 for path in self.paths.values_mut() { -1038 path.data.app_limited = true; -1039 } -1040 return None; -1041 } -1042 StateType::Draining | StateType::Closed => { -1043 // self.connection_close_pending is only reset once the associated packet -1044 // had been encoded successfully -1045 if !self.connection_close_pending { -1046 for path in self.paths.values_mut() { -1047 path.data.app_limited = true; -1048 } -1049 return None; -1050 } -1051 true -1052 } -1053 _ => false, -1054 }; -1055 -1056 // Schedule an ACK_FREQUENCY frame if a new one needs to be sent. -1057 if let Some(config) = &self.config.ack_frequency_config { -1058 let rtt = self -1059 .paths -1060 .values() -1061 .map(|p| p.data.rtt.get()) -1062 .min() -1063 .expect("one path exists"); -1064 self.spaces[SpaceId::Data].pending.ack_frequency = self -1065 .ack_frequency -1066 .should_send_ack_frequency(rtt, config, &self.peer_params) -1067 && self.highest_space == SpaceKind::Data -1068 && self.peer_supports_ack_frequency(); -1069 } -1070 -1071 let mut next_path_id = self.paths.first_entry().map(|e| *e.key()); -1072 while let Some(path_id) = next_path_id { -1073 if !connection_close_pending -1074 && let Some(transmit) = self.poll_transmit_off_path(now, buf, path_id) -1075 { -1076 return Some(transmit); -1077 } -1078 -1079 let info = self.scheduling_info(path_id); -1080 if let Some(transmit) = self.poll_transmit_on_path( -1081 now, -1082 buf, -1083 path_id, -1084 max_datagrams, -1085 &info, -1086 connection_close_pending, -1087 ) { -1088 return Some(transmit); -1089 } -1090 -1091 // Continue checking other paths, tail-loss probes may need to be sent -1092 // in all spaces. -1093 debug_assert!( -1094 buf.is_empty(), -1095 "nothing to send on path but buffer not empty" -1096 ); -1097 -1098 next_path_id = self.paths.keys().find(|i| **i > path_id).copied(); -1099 } -1100 -1101 // We didn't produce any application data packet -1102 debug_assert!( -1103 buf.is_empty(), -1104 "there was data in the buffer, but it was not sent" -1105 ); -1106 -1107 if self.state.is_established() { -1108 // Try MTU probing now -1109 let mut next_path_id = self.paths.first_entry().map(|e| *e.key()); -1110 while let Some(path_id) = next_path_id { -1111 if let Some(transmit) = self.poll_transmit_mtu_probe(now, buf, path_id) { -1112 return Some(transmit); -1113 } -1114 next_path_id = self.paths.keys().find(|i| **i > path_id).copied(); -1115 } -1116 } -1117 -1118 None -1119 } -1120 -1121 /// Computes the packet scheduling information for this path. -1122 /// -1123 /// While this information is only returned for a single path, it is important to know -1124 /// that this information remains static for the entire span of a single -1125 /// [`Connection::poll_transmit`] call. In other words, the return value is purely -1126 /// functional and only depends on the [`PathId`] **during a single** `poll_transmit` -1127 /// call. It can be computed up-front for all paths but we don't do that because it -1128 /// involves an allocation. -1129 /// -1130 /// See the inline comments for how the packet scheduling works. -1131 /// -1132 /// # Panics +1034 // Each call to poll_transmit can only send datagrams to one destination, because +1035 // all datagrams in a GSO batch are for the same destination. Therefore only +1036 // datagrams for one destination address are produced for each poll_transmit call. +1037 +1038 // Check whether we need to send a close message +1039 let connection_close_pending = match self.state.as_type() { +1040 StateType::Drained => { +1041 for path in self.paths.values_mut() { +1042 path.data.app_limited = true; +1043 } +1044 return None; +1045 } +1046 StateType::Draining | StateType::Closed => { +1047 // self.connection_close_pending is only reset once the associated packet +1048 // had been encoded successfully +1049 if !self.connection_close_pending { +1050 for path in self.paths.values_mut() { +1051 path.data.app_limited = true; +1052 } +1053 return None; +1054 } +1055 true +1056 } +1057 _ => false, +1058 }; +1059 +1060 // Schedule an ACK_FREQUENCY frame if a new one needs to be sent. +1061 if let Some(config) = &self.config.ack_frequency_config { +1062 let rtt = self +1063 .paths +1064 .values() +1065 .map(|p| p.data.rtt.get()) +1066 .min() +1067 .expect("one path exists"); +1068 self.spaces[SpaceId::Data].pending.ack_frequency = self +1069 .ack_frequency +1070 .should_send_ack_frequency(rtt, config, &self.peer_params) +1071 && self.highest_space == SpaceKind::Data +1072 && self.peer_supports_ack_frequency(); +1073 } +1074 +1075 let mut next_path_id = self.paths.first_entry().map(|e| *e.key()); +1076 while let Some(path_id) = next_path_id { +1077 if !connection_close_pending +1078 && let Some(transmit) = self.poll_transmit_off_path(now, buf, path_id) +1079 { +1080 return Some(transmit); +1081 } +1082 +1083 let info = self.scheduling_info(path_id); +1084 if let Some(transmit) = self.poll_transmit_on_path( +1085 now, +1086 buf, +1087 path_id, +1088 max_datagrams, +1089 &info, +1090 connection_close_pending, +1091 ) { +1092 return Some(transmit); +1093 } +1094 +1095 // Continue checking other paths, tail-loss probes may need to be sent +1096 // in all spaces. +1097 debug_assert!( +1098 buf.is_empty(), +1099 "nothing to send on path but buffer not empty" +1100 ); +1101 +1102 next_path_id = self.paths.keys().find(|i| **i > path_id).copied(); +1103 } +1104 +1105 // We didn't produce any application data packet +1106 debug_assert!( +1107 buf.is_empty(), +1108 "there was data in the buffer, but it was not sent" +1109 ); +1110 +1111 if self.state.is_established() { +1112 // Try MTU probing now +1113 let mut next_path_id = self.paths.first_entry().map(|e| *e.key()); +1114 while let Some(path_id) = next_path_id { +1115 if let Some(transmit) = self.poll_transmit_mtu_probe(now, buf, path_id) { +1116 return Some(transmit); +1117 } +1118 next_path_id = self.paths.keys().find(|i| **i > path_id).copied(); +1119 } +1120 } +1121 +1122 None +1123 } +1124 +1125 /// Computes the packet scheduling information for this path. +1126 /// +1127 /// While this information is only returned for a single path, it is important to know +1128 /// that this information remains static for the entire span of a single +1129 /// [`Connection::poll_transmit`] call. In other words, the return value is purely +1130 /// functional and only depends on the [`PathId`] **during a single** `poll_transmit` +1131 /// call. It can be computed up-front for all paths but we don't do that because it +1132 /// involves an allocation. 1133 /// -1134 /// This will panic if called for a path for which we do not have any [`PathData`], like -1135 /// so many other functions we have. But this is the only one to document this in its -1136 /// doc comment. Maybe that should change. Eventually we'll refactor things for this -1137 /// panic to go away. -1138 fn scheduling_info(&self, path_id: PathId) -> PathSchedulingInfo { -1139 // Such a space is preferred for SpaceKind::Data frames. -1140 let have_validated_status_available_space = self.paths.iter().any(|(path_id, path)| { -1141 self.remote_cids.contains_key(path_id) -1142 && !self.abandoned_paths.contains(path_id) -1143 && path.data.validated -1144 && path.data.local_status() == PathStatus::Available -1145 }); -1146 -1147 // Such a space is able to send SpaceKind::Data frames. -1148 let have_validated_space = self.paths.iter().any(|(path_id, path)| { -1149 self.remote_cids.contains_key(path_id) -1150 && !self.abandoned_paths.contains(path_id) -1151 && path.data.validated -1152 }); -1153 -1154 let is_handshaking = self.is_handshaking(); -1155 let has_cids = self.remote_cids.contains_key(&path_id); -1156 let is_abandoned = self.abandoned_paths.contains(&path_id); -1157 let path_data = self.path_data(path_id); -1158 let validated = path_data.validated; -1159 let status = path_data.local_status(); -1160 -1161 // This is the core packet scheduling, whether this space ID may send -1162 // SpaceKind::Data frames. -1163 let may_send_data = has_cids -1164 && !is_abandoned -1165 && if is_handshaking { -1166 // There is only one path during the handshake. We want to -1167 // already send 0-RTT and 0.5-RTT (permitting anti-amplification -1168 // limit) data. -1169 true -1170 } else if !validated { -1171 // TODO(flub): When we have a network change we might end up -1172 // having to abandon all paths and re-open new ones to the -1173 // same remotes. This leaves us without any validated -1174 // path. Perhaps we should have a way to figure out if the -1175 // path is to a previously-validated remote address and allow -1176 // sending data to such remotes immediately. -1177 false -1178 } else { -1179 match status { -1180 PathStatus::Available => { -1181 // Best possible space to send data on. -1182 true -1183 } -1184 PathStatus::Backup => { -1185 // If there is a status-available path we prefer that. -1186 !have_validated_status_available_space -1187 } -1188 } -1189 }; -1190 -1191 // CONNECTION_CLOSE is allowed to be sent on a non-validated -1192 // path. Particularly during the handshake we want to send it before the -1193 // path is validated. Later if there is no validated path available we -1194 // will also accept sending it on an un-validated path. -1195 let may_send_close = has_cids -1196 && !is_abandoned -1197 && if !validated && have_validated_status_available_space { -1198 // We have a better space to send on. -1199 false -1200 } else { -1201 // No other validated space, this is as good as it gets. -1202 true -1203 }; -1204 -1205 // PATH_ABANDON is normally sent together with other SpaceKind::Data frames. But if -1206 // there is no other validated space to send it on, it can be sent on the path to be -1207 // abandoned itself if that was validated. -1208 let may_self_abandon = has_cids && validated && !have_validated_space; -1209 -1210 PathSchedulingInfo { -1211 is_abandoned, -1212 may_send_data, -1213 may_send_close, -1214 may_self_abandon, -1215 } -1216 } -1217 -1218 fn build_transmit(&mut self, path_id: PathId, transmit: TransmitBuf<'_>) -> Transmit { -1219 debug_assert!( -1220 !transmit.is_empty(), -1221 "must not be called with an empty transmit buffer" -1222 ); -1223 -1224 let network_path = self.path_data(path_id).network_path; -1225 trace!( -1226 segment_size = transmit.segment_size(), -1227 last_datagram_len = transmit.len() % transmit.segment_size(), -1228 %network_path, -1229 "sending {} bytes in {} datagrams", -1230 transmit.len(), -1231 transmit.num_datagrams() -1232 ); -1233 self.path_data_mut(path_id) -1234 .inc_total_sent(transmit.len() as u64); -1235 -1236 self.path_stats -1237 .for_path(path_id) -1238 .udp_tx -1239 .on_sent(transmit.num_datagrams() as u64, transmit.len()); -1240 -1241 Transmit { -1242 destination: network_path.remote, -1243 size: transmit.len(), -1244 ecn: if self.path_data(path_id).sending_ecn { -1245 Some(EcnCodepoint::Ect0) -1246 } else { -1247 None -1248 }, -1249 segment_size: match transmit.num_datagrams() { -1250 1 => None, -1251 _ => Some(transmit.segment_size()), -1252 }, -1253 src_ip: network_path.local_ip, -1254 } -1255 } -1256 -1257 /// poll_transmit logic for off-path data. -1258 fn poll_transmit_off_path( -1259 &mut self, -1260 now: Instant, -1261 buf: &mut Vec<u8>, -1262 path_id: PathId, -1263 ) -> Option<Transmit> { -1264 if let Some(challenge) = self.send_prev_path_challenge(now, buf, path_id) { -1265 return Some(challenge); -1266 } -1267 if let Some(response) = self.send_off_path_path_response(now, buf, path_id) { -1268 return Some(response); -1269 } -1270 if let Some(challenge) = self.send_nat_traversal_path_challenge(now, buf, path_id) { -1271 return Some(challenge); -1272 } -1273 None -1274 } -1275 -1276 /// poll_transmit logic for on-path data. -1277 /// -1278 /// This is not quite the same as for a multipath packet space, since [`PathId::ZERO`] -1279 /// has 3 packet spaces, which this handles. -1280 /// -1281 /// See [`Self::poll_transmit_off_path`] for off-path data. -1282 #[must_use] -1283 fn poll_transmit_on_path( -1284 &mut self, -1285 now: Instant, -1286 buf: &mut Vec<u8>, -1287 path_id: PathId, -1288 max_datagrams: NonZeroUsize, -1289 scheduling_info: &PathSchedulingInfo, -1290 connection_close_pending: bool, -1291 ) -> Option<Transmit> { -1292 // Check if there is at least one active CID to use for sending -1293 let Some(remote_cid) = self.remote_cids.get(&path_id).map(CidQueue::active) else { -1294 if !self.abandoned_paths.contains(&path_id) { -1295 debug!(%path_id, "no remote CIDs for path"); -1296 } -1297 return None; -1298 }; -1299 -1300 // Whether the last packet in the datagram must be padded so the datagram takes up -1301 // an exact size. An earlier space can decide to not fill an entire datagram and -1302 // require the next space to fill it further. But may need a specific size of the -1303 // datagram containing the packet. The final packet built in the datagram must pad -1304 // to this size. -1305 let mut pad_datagram = PadDatagram::No; -1306 -1307 // The packet number of the last built packet. This is kept kept across spaces. -1308 // QUIC is supposed to have a single congestion controller for the Initial, -1309 // Handshake and Data(PathId::ZERO) spaces. -1310 let mut last_packet_number = None; -1311 -1312 // If we end up not sending anything, we need to know if that was because there was -1313 // nothing to send or because we were congestion blocked. -1314 let mut congestion_blocked = false; +1134 /// See the inline comments for how the packet scheduling works. +1135 /// +1136 /// # Panics +1137 /// +1138 /// This will panic if called for a path for which we do not have any [`PathData`], like +1139 /// so many other functions we have. But this is the only one to document this in its +1140 /// doc comment. Maybe that should change. Eventually we'll refactor things for this +1141 /// panic to go away. +1142 fn scheduling_info(&self, path_id: PathId) -> PathSchedulingInfo { +1143 // Such a space is preferred for SpaceKind::Data frames. +1144 let have_validated_status_available_space = self.paths.iter().any(|(path_id, path)| { +1145 self.remote_cids.contains_key(path_id) +1146 && !self.abandoned_paths.contains(path_id) +1147 && path.data.validated +1148 && path.data.local_status() == PathStatus::Available +1149 }); +1150 +1151 // Such a space is able to send SpaceKind::Data frames. +1152 let have_validated_space = self.paths.iter().any(|(path_id, path)| { +1153 self.remote_cids.contains_key(path_id) +1154 && !self.abandoned_paths.contains(path_id) +1155 && path.data.validated +1156 }); +1157 +1158 let is_handshaking = self.is_handshaking(); +1159 let has_cids = self.remote_cids.contains_key(&path_id); +1160 let is_abandoned = self.abandoned_paths.contains(&path_id); +1161 let path_data = self.path_data(path_id); +1162 let validated = path_data.validated; +1163 let status = path_data.local_status(); +1164 +1165 // This is the core packet scheduling, whether this space ID may send +1166 // SpaceKind::Data frames. +1167 let may_send_data = has_cids +1168 && !is_abandoned +1169 && if is_handshaking { +1170 // There is only one path during the handshake. We want to +1171 // already send 0-RTT and 0.5-RTT (permitting anti-amplification +1172 // limit) data. +1173 true +1174 } else if !validated { +1175 // TODO(flub): When we have a network change we might end up +1176 // having to abandon all paths and re-open new ones to the +1177 // same remotes. This leaves us without any validated +1178 // path. Perhaps we should have a way to figure out if the +1179 // path is to a previously-validated remote address and allow +1180 // sending data to such remotes immediately. +1181 false +1182 } else { +1183 match status { +1184 PathStatus::Available => { +1185 // Best possible space to send data on. +1186 true +1187 } +1188 PathStatus::Backup => { +1189 // If there is a status-available path we prefer that. +1190 !have_validated_status_available_space +1191 } +1192 } +1193 }; +1194 +1195 // CONNECTION_CLOSE is allowed to be sent on a non-validated +1196 // path. Particularly during the handshake we want to send it before the +1197 // path is validated. Later if there is no validated path available we +1198 // will also accept sending it on an un-validated path. +1199 let may_send_close = has_cids +1200 && !is_abandoned +1201 && if !validated && have_validated_status_available_space { +1202 // We have a better space to send on. +1203 false +1204 } else { +1205 // No other validated space, this is as good as it gets. +1206 true +1207 }; +1208 +1209 // PATH_ABANDON is normally sent together with other SpaceKind::Data frames. But if +1210 // there is no other validated space to send it on, it can be sent on the path to be +1211 // abandoned itself if that was validated. +1212 let may_self_abandon = has_cids && validated && !have_validated_space; +1213 +1214 PathSchedulingInfo { +1215 is_abandoned, +1216 may_send_data, +1217 may_send_close, +1218 may_self_abandon, +1219 } +1220 } +1221 +1222 fn build_transmit(&mut self, path_id: PathId, transmit: TransmitBuf<'_>) -> Transmit { +1223 debug_assert!( +1224 !transmit.is_empty(), +1225 "must not be called with an empty transmit buffer" +1226 ); +1227 +1228 let network_path = self.path_data(path_id).network_path; +1229 trace!( +1230 segment_size = transmit.segment_size(), +1231 last_datagram_len = transmit.len() % transmit.segment_size(), +1232 %network_path, +1233 "sending {} bytes in {} datagrams", +1234 transmit.len(), +1235 transmit.num_datagrams() +1236 ); +1237 self.path_data_mut(path_id) +1238 .inc_total_sent(transmit.len() as u64); +1239 +1240 self.path_stats +1241 .for_path(path_id) +1242 .udp_tx +1243 .on_sent(transmit.num_datagrams() as u64, transmit.len()); +1244 +1245 Transmit { +1246 destination: network_path.remote, +1247 size: transmit.len(), +1248 ecn: if self.path_data(path_id).sending_ecn { +1249 Some(EcnCodepoint::Ect0) +1250 } else { +1251 None +1252 }, +1253 segment_size: match transmit.num_datagrams() { +1254 1 => None, +1255 _ => Some(transmit.segment_size()), +1256 }, +1257 src_ip: network_path.local_ip, +1258 } +1259 } +1260 +1261 /// poll_transmit logic for off-path data. +1262 fn poll_transmit_off_path( +1263 &mut self, +1264 now: Instant, +1265 buf: &mut Vec<u8>, +1266 path_id: PathId, +1267 ) -> Option<Transmit> { +1268 if let Some(challenge) = self.send_prev_path_challenge(now, buf, path_id) { +1269 return Some(challenge); +1270 } +1271 if let Some(response) = self.send_off_path_path_response(now, buf, path_id) { +1272 return Some(response); +1273 } +1274 if let Some(challenge) = self.send_nat_traversal_path_challenge(now, buf, path_id) { +1275 return Some(challenge); +1276 } +1277 None +1278 } +1279 +1280 /// poll_transmit logic for on-path data. +1281 /// +1282 /// This is not quite the same as for a multipath packet space, since [`PathId::ZERO`] +1283 /// has 3 packet spaces, which this handles. +1284 /// +1285 /// See [`Self::poll_transmit_off_path`] for off-path data. +1286 #[must_use] +1287 fn poll_transmit_on_path( +1288 &mut self, +1289 now: Instant, +1290 buf: &mut Vec<u8>, +1291 path_id: PathId, +1292 max_datagrams: NonZeroUsize, +1293 scheduling_info: &PathSchedulingInfo, +1294 connection_close_pending: bool, +1295 ) -> Option<Transmit> { +1296 // Check if there is at least one active CID to use for sending +1297 let Some(remote_cid) = self.remote_cids.get(&path_id).map(CidQueue::active) else { +1298 if !self.abandoned_paths.contains(&path_id) { +1299 debug!(%path_id, "no remote CIDs for path"); +1300 } +1301 return None; +1302 }; +1303 +1304 // Whether the last packet in the datagram must be padded so the datagram takes up +1305 // an exact size. An earlier space can decide to not fill an entire datagram and +1306 // require the next space to fill it further. But may need a specific size of the +1307 // datagram containing the packet. The final packet built in the datagram must pad +1308 // to this size. +1309 let mut pad_datagram = PadDatagram::No; +1310 +1311 // The packet number of the last built packet. This is kept kept across spaces. +1312 // QUIC is supposed to have a single congestion controller for the Initial, +1313 // Handshake and Data(PathId::ZERO) spaces. +1314 let mut last_packet_number = None; 1315 -1316 // Set the segment size to this path's MTU for on-path data. -1317 let pmtu = self.path_data(path_id).current_mtu().into(); -1318 let mut transmit = TransmitBuf::new(buf, max_datagrams, pmtu); +1316 // If we end up not sending anything, we need to know if that was because there was +1317 // nothing to send or because we were congestion blocked. +1318 let mut congestion_blocked = false; 1319 -1320 // Iterate over the available spaces. -1321 for space_id in SpaceId::iter() { -1322 // Only PathId::ZERO uses non Data space ids. -1323 if path_id != PathId::ZERO && space_id != SpaceId::Data { -1324 continue; -1325 } -1326 match self.poll_transmit_path_space( -1327 now, -1328 &mut transmit, -1329 path_id, -1330 space_id, -1331 remote_cid, -1332 scheduling_info, -1333 connection_close_pending, -1334 pad_datagram, -1335 ) { -1336 PollPathSpaceStatus::NothingToSend { -1337 congestion_blocked: cb, -1338 } => { -1339 congestion_blocked |= cb; -1340 // Continue checking other spaces, tail-loss probes may need to be sent -1341 // in all spaces. -1342 } -1343 PollPathSpaceStatus::WrotePacket { -1344 last_packet_number: pn, -1345 pad_datagram: pad, -1346 } => { -1347 debug_assert!(!transmit.is_empty(), "transmit must contain packets"); -1348 last_packet_number = Some(pn); -1349 pad_datagram = pad; -1350 // Always check higher spaces. If the transmit is full or they have -1351 // nothing to send they will not write packets. But if they can, they -1352 // must always be allowed to add to this transmit because coalescing may -1353 // be required. -1354 continue; -1355 } -1356 PollPathSpaceStatus::Send { -1357 last_packet_number: pn, -1358 } => { -1359 debug_assert!(!transmit.is_empty(), "transmit must contain packets"); -1360 last_packet_number = Some(pn); -1361 break; -1362 } -1363 } -1364 } -1365 -1366 if last_packet_number.is_some() || congestion_blocked { -1367 self.qlog.emit_recovery_metrics( -1368 path_id, -1369 &mut self -1370 .paths -1371 .get_mut(&path_id) -1372 .expect("path_id was iterated from self.paths above") -1373 .data, -1374 now, -1375 ); -1376 } -1377 -1378 self.path_data_mut(path_id).app_limited = -1379 last_packet_number.is_none() && !congestion_blocked; -1380 -1381 match last_packet_number { -1382 Some(last_packet_number) => { -1383 // Note that when sending in multiple spaces the last packet number will be -1384 // the one from the highest space. -1385 self.path_data_mut(path_id).congestion.on_sent( -1386 now, -1387 transmit.len() as u64, -1388 last_packet_number, -1389 ); -1390 Some(self.build_transmit(path_id, transmit)) -1391 } -1392 None => None, -1393 } -1394 } -1395 -1396 /// poll_transmit logic for a QUIC-MULTIPATH packet number space (PathID + SpaceId). -1397 #[must_use] -1398 fn poll_transmit_path_space( -1399 &mut self, -1400 now: Instant, -1401 transmit: &mut TransmitBuf<'_>, -1402 path_id: PathId, -1403 space_id: SpaceId, -1404 remote_cid: ConnectionId, -1405 scheduling_info: &PathSchedulingInfo, -1406 // If we need to send a CONNECTION_CLOSE frame. -1407 connection_close_pending: bool, -1408 // Whether the current datagram needs to be padded to a certain size. -1409 mut pad_datagram: PadDatagram, -1410 ) -> PollPathSpaceStatus { -1411 // Keep track of the last packet number we wrote. If None we did not write any -1412 // packets. -1413 let mut last_packet_number = None; -1414 -1415 // Each loop of this may build one packet. It works logically as follows: -1416 // -1417 // - Check if something *needs* to be sent in this space and *can* be sent. -1418 // - If not, return to the caller who will call us again for the next space. -1419 // - Start a new datagram. -1420 // - Unless coalescing the packet into an existing datagram. -1421 // - Write the packet header and payload. -1422 // - Check if coalescing a next packet into the datagram is possible. -1423 // - If coalescing, finish packet without padding to leave space in the datagram. -1424 // - If not coalescing, complete the datagram: -1425 // - Finish packet with padding. -1426 // - Set the transmit segment size if this is the first datagram. -1427 // - Loop: next iteration will exit the loop if nothing more to send in this -1428 // space. The TransmitBuf will contain a started datagram with space if -1429 // coalescing, or completely filled datagram if not coalescing. -1430 loop { -1431 // Determine if anything can be sent in this packet number space. -1432 let max_packet_size = if transmit.datagram_remaining_mut() > 0 { -1433 // A datagram is started already, we are coalescing another packet into it. -1434 transmit.datagram_remaining_mut() -1435 } else { -1436 // A new datagram needs to be started. -1437 transmit.segment_size() -1438 }; -1439 let can_send = -1440 self.space_can_send(space_id, path_id, max_packet_size, connection_close_pending); -1441 let needs_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0; -1442 let space_will_send = { -1443 if scheduling_info.is_abandoned { -1444 // If this path is abandoned then we might still have to send -1445 // PATH_ABANDON itself on it if there was no better space -1446 // available. Otherwise we want to send the PATH_ABANDON as permitted by -1447 // may_send_data however. -1448 scheduling_info.may_self_abandon -1449 && self.spaces[space_id] -1450 .pending -1451 .path_abandon -1452 .contains_key(&path_id) -1453 } else if can_send.close && scheduling_info.may_send_close { -1454 // This is the best path to send a CONNECTION_CLOSE on. -1455 true -1456 } else if needs_loss_probe || can_send.space_specific { -1457 // We always send a loss probe or space-specific frames if the path is -1458 // not abandoned. +1320 // Set the segment size to this path's MTU for on-path data. +1321 let pmtu = self.path_data(path_id).current_mtu().into(); +1322 let mut transmit = TransmitBuf::new(buf, max_datagrams, pmtu); +1323 +1324 // Iterate over the available spaces. +1325 for space_id in SpaceId::iter() { +1326 // Only PathId::ZERO uses non Data space ids. +1327 if path_id != PathId::ZERO && space_id != SpaceId::Data { +1328 continue; +1329 } +1330 match self.poll_transmit_path_space( +1331 now, +1332 &mut transmit, +1333 path_id, +1334 space_id, +1335 remote_cid, +1336 scheduling_info, +1337 connection_close_pending, +1338 pad_datagram, +1339 ) { +1340 PollPathSpaceStatus::NothingToSend { +1341 congestion_blocked: cb, +1342 } => { +1343 congestion_blocked |= cb; +1344 // Continue checking other spaces, tail-loss probes may need to be sent +1345 // in all spaces. +1346 } +1347 PollPathSpaceStatus::WrotePacket { +1348 last_packet_number: pn, +1349 pad_datagram: pad, +1350 } => { +1351 debug_assert!(!transmit.is_empty(), "transmit must contain packets"); +1352 last_packet_number = Some(pn); +1353 pad_datagram = pad; +1354 // Always check higher spaces. If the transmit is full or they have +1355 // nothing to send they will not write packets. But if they can, they +1356 // must always be allowed to add to this transmit because coalescing may +1357 // be required. +1358 continue; +1359 } +1360 PollPathSpaceStatus::Send { +1361 last_packet_number: pn, +1362 } => { +1363 debug_assert!(!transmit.is_empty(), "transmit must contain packets"); +1364 last_packet_number = Some(pn); +1365 break; +1366 } +1367 } +1368 } +1369 +1370 if last_packet_number.is_some() || congestion_blocked { +1371 self.qlog.emit_recovery_metrics( +1372 path_id, +1373 &mut self +1374 .paths +1375 .get_mut(&path_id) +1376 .expect("path_id was iterated from self.paths above") +1377 .data, +1378 now, +1379 ); +1380 } +1381 +1382 self.path_data_mut(path_id).app_limited = +1383 last_packet_number.is_none() && !congestion_blocked; +1384 +1385 match last_packet_number { +1386 Some(last_packet_number) => { +1387 // Note that when sending in multiple spaces the last packet number will be +1388 // the one from the highest space. +1389 self.path_data_mut(path_id).congestion.on_sent( +1390 now, +1391 transmit.len() as u64, +1392 last_packet_number, +1393 ); +1394 Some(self.build_transmit(path_id, transmit)) +1395 } +1396 None => None, +1397 } +1398 } +1399 +1400 /// poll_transmit logic for a QUIC-MULTIPATH packet number space (PathID + SpaceId). +1401 #[must_use] +1402 fn poll_transmit_path_space( +1403 &mut self, +1404 now: Instant, +1405 transmit: &mut TransmitBuf<'_>, +1406 path_id: PathId, +1407 space_id: SpaceId, +1408 remote_cid: ConnectionId, +1409 scheduling_info: &PathSchedulingInfo, +1410 // If we need to send a CONNECTION_CLOSE frame. +1411 connection_close_pending: bool, +1412 // Whether the current datagram needs to be padded to a certain size. +1413 mut pad_datagram: PadDatagram, +1414 ) -> PollPathSpaceStatus { +1415 // Keep track of the last packet number we wrote. If None we did not write any +1416 // packets. +1417 let mut last_packet_number = None; +1418 +1419 // Each loop of this may build one packet. It works logically as follows: +1420 // +1421 // - Check if something *needs* to be sent in this space and *can* be sent. +1422 // - If not, return to the caller who will call us again for the next space. +1423 // - Start a new datagram. +1424 // - Unless coalescing the packet into an existing datagram. +1425 // - Write the packet header and payload. +1426 // - Check if coalescing a next packet into the datagram is possible. +1427 // - If coalescing, finish packet without padding to leave space in the datagram. +1428 // - If not coalescing, complete the datagram: +1429 // - Finish packet with padding. +1430 // - Set the transmit segment size if this is the first datagram. +1431 // - Loop: next iteration will exit the loop if nothing more to send in this +1432 // space. The TransmitBuf will contain a started datagram with space if +1433 // coalescing, or completely filled datagram if not coalescing. +1434 loop { +1435 // Determine if anything can be sent in this packet number space. +1436 let max_packet_size = if transmit.datagram_remaining_mut() > 0 { +1437 // A datagram is started already, we are coalescing another packet into it. +1438 transmit.datagram_remaining_mut() +1439 } else { +1440 // A new datagram needs to be started. +1441 transmit.segment_size() +1442 }; +1443 let can_send = +1444 self.space_can_send(space_id, path_id, max_packet_size, connection_close_pending); +1445 let needs_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0; +1446 let space_will_send = { +1447 if scheduling_info.is_abandoned { +1448 // If this path is abandoned then we might still have to send +1449 // PATH_ABANDON itself on it if there was no better space +1450 // available. Otherwise we want to send the PATH_ABANDON as permitted by +1451 // may_send_data however. +1452 scheduling_info.may_self_abandon +1453 && self.spaces[space_id] +1454 .pending +1455 .path_abandon +1456 .contains_key(&path_id) +1457 } else if can_send.close && scheduling_info.may_send_close { +1458 // This is the best path to send a CONNECTION_CLOSE on. 1459 true -1460 } else { -1461 // Anything else we only send if we're the best path for SpaceKind::Data -1462 // frames. -1463 !can_send.is_empty() && scheduling_info.may_send_data -1464 } -1465 }; -1466 -1467 if !space_will_send { -1468 // Nothing more to send. Previous iterations of this loop may have built -1469 // packets already. -1470 return match last_packet_number { -1471 Some(pn) => PollPathSpaceStatus::WrotePacket { -1472 last_packet_number: pn, -1473 pad_datagram, -1474 }, -1475 None => { -1476 // Only log for spaces which have crypto. -1477 if self.crypto_state.has_keys(space_id.encryption_level()) -1478 || (space_id == SpaceId::Data -1479 && self.crypto_state.has_keys(EncryptionLevel::ZeroRtt)) -1480 { -1481 trace!(?space_id, %path_id, "nothing to send in space"); -1482 } -1483 PollPathSpaceStatus::NothingToSend { -1484 congestion_blocked: false, -1485 } -1486 } -1487 }; -1488 } -1489 -1490 // We want to send on this space, check congestion control if we can. But only -1491 // if we will need to start a new datagram. If we are coalescing into an already -1492 // started datagram we do not need to check congestion control again. -1493 if transmit.datagram_remaining_mut() == 0 { -1494 let congestion_blocked = -1495 self.path_congestion_check(space_id, path_id, transmit, &can_send, now); -1496 if congestion_blocked != PathBlocked::No { -1497 // Previous iterations of this loop may have built packets already. -1498 return match last_packet_number { -1499 Some(pn) => PollPathSpaceStatus::WrotePacket { -1500 last_packet_number: pn, -1501 pad_datagram, -1502 }, -1503 None => { -1504 return PollPathSpaceStatus::NothingToSend { -1505 congestion_blocked: true, -1506 }; -1507 } -1508 }; -1509 } -1510 -1511 // If the datagram is full (or there never was one started), we need to start a -1512 // new one. -1513 if transmit.num_datagrams() >= transmit.max_datagrams().get() { -1514 // No more datagrams allowed. -1515 // Previous iterations of this loop may have built packets already. -1516 return match last_packet_number { -1517 Some(pn) => PollPathSpaceStatus::WrotePacket { -1518 last_packet_number: pn, -1519 pad_datagram, -1520 }, -1521 None => { -1522 return PollPathSpaceStatus::NothingToSend { -1523 congestion_blocked: false, -1524 }; -1525 } -1526 }; -1527 } -1528 -1529 if needs_loss_probe { -1530 // Ensure we have something to send for a tail-loss probe. -1531 let request_immediate_ack = -1532 space_id == SpaceId::Data && self.peer_supports_ack_frequency(); -1533 self.spaces[space_id].queue_tail_loss_probe( -1534 path_id, -1535 request_immediate_ack, -1536 &self.streams, -1537 ); -1538 -1539 self.spaces[space_id].for_path(path_id).loss_probes -= 1; // needs_loss_probe ensures loss_probes > 0 -1540 -1541 // Clamp the datagram to at most the minimum MTU to ensure that loss -1542 // probes can get through and enable recovery even if the path MTU -1543 // has shrank unexpectedly. -1544 transmit.start_new_datagram_with_size(std::cmp::min( -1545 usize::from(INITIAL_MTU), -1546 transmit.segment_size(), -1547 )); -1548 } else { -1549 transmit.start_new_datagram(); -1550 } -1551 trace!(count = transmit.num_datagrams(), "new datagram started"); -1552 -1553 // We started a new datagram, we decide later if it needs padding. -1554 pad_datagram = PadDatagram::No; -1555 } +1460 } else if needs_loss_probe || can_send.space_specific { +1461 // We always send a loss probe or space-specific frames if the path is +1462 // not abandoned. +1463 true +1464 } else { +1465 // Anything else we only send if we're the best path for SpaceKind::Data +1466 // frames. +1467 !can_send.is_empty() && scheduling_info.may_send_data +1468 } +1469 }; +1470 +1471 if !space_will_send { +1472 // Nothing more to send. Previous iterations of this loop may have built +1473 // packets already. +1474 return match last_packet_number { +1475 Some(pn) => PollPathSpaceStatus::WrotePacket { +1476 last_packet_number: pn, +1477 pad_datagram, +1478 }, +1479 None => { +1480 // Only log for spaces which have crypto. +1481 if self.crypto_state.has_keys(space_id.encryption_level()) +1482 || (space_id == SpaceId::Data +1483 && self.crypto_state.has_keys(EncryptionLevel::ZeroRtt)) +1484 { +1485 trace!(?space_id, %path_id, "nothing to send in space"); +1486 } +1487 PollPathSpaceStatus::NothingToSend { +1488 congestion_blocked: false, +1489 } +1490 } +1491 }; +1492 } +1493 +1494 // We want to send on this space, check congestion control if we can. But only +1495 // if we will need to start a new datagram. If we are coalescing into an already +1496 // started datagram we do not need to check congestion control again. +1497 if transmit.datagram_remaining_mut() == 0 { +1498 let congestion_blocked = +1499 self.path_congestion_check(space_id, path_id, transmit, &can_send, now); +1500 if congestion_blocked != PathBlocked::No { +1501 // Previous iterations of this loop may have built packets already. +1502 return match last_packet_number { +1503 Some(pn) => PollPathSpaceStatus::WrotePacket { +1504 last_packet_number: pn, +1505 pad_datagram, +1506 }, +1507 None => { +1508 return PollPathSpaceStatus::NothingToSend { +1509 congestion_blocked: true, +1510 }; +1511 } +1512 }; +1513 } +1514 +1515 // If the datagram is full (or there never was one started), we need to start a +1516 // new one. +1517 if transmit.num_datagrams() >= transmit.max_datagrams().get() { +1518 // No more datagrams allowed. +1519 // Previous iterations of this loop may have built packets already. +1520 return match last_packet_number { +1521 Some(pn) => PollPathSpaceStatus::WrotePacket { +1522 last_packet_number: pn, +1523 pad_datagram, +1524 }, +1525 None => { +1526 return PollPathSpaceStatus::NothingToSend { +1527 congestion_blocked: false, +1528 }; +1529 } +1530 }; +1531 } +1532 +1533 if needs_loss_probe { +1534 // Ensure we have something to send for a tail-loss probe. +1535 let request_immediate_ack = +1536 space_id == SpaceId::Data && self.peer_supports_ack_frequency(); +1537 self.spaces[space_id].queue_tail_loss_probe( +1538 path_id, +1539 request_immediate_ack, +1540 &self.streams, +1541 ); +1542 +1543 self.spaces[space_id].for_path(path_id).loss_probes -= 1; // needs_loss_probe ensures loss_probes > 0 +1544 +1545 // Clamp the datagram to at most the minimum MTU to ensure that loss +1546 // probes can get through and enable recovery even if the path MTU +1547 // has shrank unexpectedly. +1548 transmit.start_new_datagram_with_size(std::cmp::min( +1549 usize::from(INITIAL_MTU), +1550 transmit.segment_size(), +1551 )); +1552 } else { +1553 transmit.start_new_datagram(); +1554 } +1555 trace!(count = transmit.num_datagrams(), "new datagram started"); 1556 -1557 // If coalescing another packet into the existing datagram, there should -1558 // still be enough space for a whole packet. -1559 if transmit.datagram_start_offset() < transmit.len() { -1560 debug_assert!(transmit.datagram_remaining_mut() >= MIN_PACKET_SPACE); -1561 } -1562 -1563 // -1564 // From here on, we've determined that a packet will definitely be sent. -1565 // +1557 // We started a new datagram, we decide later if it needs padding. +1558 pad_datagram = PadDatagram::No; +1559 } +1560 +1561 // If coalescing another packet into the existing datagram, there should +1562 // still be enough space for a whole packet. +1563 if transmit.datagram_start_offset() < transmit.len() { +1564 debug_assert!(transmit.datagram_remaining_mut() >= MIN_PACKET_SPACE); +1565 } 1566 -1567 if self.crypto_state.has_keys(EncryptionLevel::Initial) -1568 && space_id == SpaceId::Handshake -1569 && self.side.is_client() -1570 { -1571 // A client stops both sending and processing Initial packets when it -1572 // sends its first Handshake packet. -1573 self.discard_space(now, SpaceKind::Initial); -1574 } -1575 if let Some(ref mut prev) = self.crypto_state.prev_crypto { -1576 prev.update_unacked = false; -1577 } -1578 -1579 let Some(mut builder) = -1580 PacketBuilder::new(now, space_id, path_id, remote_cid, transmit, self) -1581 else { -1582 // Confidentiality limit is exceeded and the connection has been killed. We -1583 // should not send any other packets. This works in a roundabout way: We -1584 // have started a datagram but not written anything into it. So even if we -1585 // get called again for another space we will see an already started -1586 // datagram and try and start another packet here. Then be stopped by the -1587 // same confidentiality limit. -1588 return PollPathSpaceStatus::NothingToSend { -1589 congestion_blocked: false, -1590 }; -1591 }; -1592 last_packet_number = Some(builder.packet_number); -1593 -1594 if space_id == SpaceId::Initial -1595 && (self.side.is_client() || can_send.is_ack_eliciting() || needs_loss_probe) -1596 { -1597 // https://www.rfc-editor.org/rfc/rfc9000.html#section-14.1 -1598 pad_datagram |= PadDatagram::ToMinMtu; -1599 } -1600 if space_id == SpaceId::Data && self.config.pad_to_mtu { -1601 pad_datagram |= PadDatagram::ToSegmentSize; -1602 } -1603 -1604 if scheduling_info.may_send_close && can_send.close { -1605 trace!("sending CONNECTION_CLOSE"); -1606 // Encode ACKs before the ConnectionClose message, to give the receiver -1607 // a better approximate on what data has been processed. This is -1608 // especially important with ack delay, since the peer might not -1609 // have gotten any other ACK for the data earlier on. -1610 let is_multipath_negotiated = self.is_multipath_negotiated(); -1611 for path_id in self.spaces[space_id] -1612 .number_spaces -1613 .iter() -1614 .filter(|(_, pns)| !pns.pending_acks.ranges().is_empty()) -1615 .map(|(&path_id, _)| path_id) -1616 .collect::<Vec<_>>() -1617 { -1618 Self::populate_acks( -1619 now, -1620 self.receiving_ecn, -1621 path_id, -1622 space_id, -1623 &mut self.spaces[space_id], -1624 is_multipath_negotiated, -1625 &mut builder, -1626 &mut self.path_stats.for_path(path_id).frame_tx, -1627 self.crypto_state.has_keys(space_id.encryption_level()), -1628 ); -1629 } -1630 -1631 // Since there only 64 ACK frames there will always be enough space -1632 // to encode the ConnectionClose frame too. However we still have the -1633 // check here to prevent crashes if something changes. +1567 // +1568 // From here on, we've determined that a packet will definitely be sent. +1569 // +1570 +1571 if self.crypto_state.has_keys(EncryptionLevel::Initial) +1572 && space_id == SpaceId::Handshake +1573 && self.side.is_client() +1574 { +1575 // A client stops both sending and processing Initial packets when it +1576 // sends its first Handshake packet. +1577 self.discard_space(now, SpaceKind::Initial); +1578 } +1579 if let Some(ref mut prev) = self.crypto_state.prev_crypto { +1580 prev.update_unacked = false; +1581 } +1582 +1583 let Some(mut builder) = +1584 PacketBuilder::new(now, space_id, path_id, remote_cid, transmit, self) +1585 else { +1586 // Confidentiality limit is exceeded and the connection has been killed. We +1587 // should not send any other packets. This works in a roundabout way: We +1588 // have started a datagram but not written anything into it. So even if we +1589 // get called again for another space we will see an already started +1590 // datagram and try and start another packet here. Then be stopped by the +1591 // same confidentiality limit. +1592 return PollPathSpaceStatus::NothingToSend { +1593 congestion_blocked: false, +1594 }; +1595 }; +1596 last_packet_number = Some(builder.packet_number); +1597 +1598 if space_id == SpaceId::Initial +1599 && (self.side.is_client() || can_send.is_ack_eliciting() || needs_loss_probe) +1600 { +1601 // https://www.rfc-editor.org/rfc/rfc9000.html#section-14.1 +1602 pad_datagram |= PadDatagram::ToMinMtu; +1603 } +1604 if space_id == SpaceId::Data && self.config.pad_to_mtu { +1605 pad_datagram |= PadDatagram::ToSegmentSize; +1606 } +1607 +1608 if scheduling_info.may_send_close && can_send.close { +1609 trace!("sending CONNECTION_CLOSE"); +1610 // Encode ACKs before the ConnectionClose message, to give the receiver +1611 // a better approximate on what data has been processed. This is +1612 // especially important with ack delay, since the peer might not +1613 // have gotten any other ACK for the data earlier on. +1614 let is_multipath_negotiated = self.is_multipath_negotiated(); +1615 for path_id in self.spaces[space_id] +1616 .number_spaces +1617 .iter() +1618 .filter(|(_, pns)| !pns.pending_acks.ranges().is_empty()) +1619 .map(|(&path_id, _)| path_id) +1620 .collect::<Vec<_>>() +1621 { +1622 Self::populate_acks( +1623 now, +1624 self.receiving_ecn, +1625 path_id, +1626 space_id, +1627 &mut self.spaces[space_id], +1628 is_multipath_negotiated, +1629 &mut builder, +1630 &mut self.path_stats.for_path(path_id).frame_tx, +1631 self.crypto_state.has_keys(space_id.encryption_level()), +1632 ); +1633 } 1634 -1635 // TODO(flub): This needs fixing for multipath, to ensure we can always -1636 // write the CONNECTION_CLOSE even if we have many PATH_ACKs to send: -1637 // https://github.com/n0-computer/noq/issues/367. -1638 debug_assert!( -1639 builder.frame_space_remaining() > frame::ConnectionClose::SIZE_BOUND, -1640 "ACKs should leave space for ConnectionClose" -1641 ); -1642 let stats = &mut self.path_stats.for_path(path_id).frame_tx; -1643 if frame::ConnectionClose::SIZE_BOUND < builder.frame_space_remaining() { -1644 let max_frame_size = builder.frame_space_remaining(); -1645 let close: Close = match self.state.as_type() { -1646 StateType::Closed => { -1647 let reason: Close = -1648 self.state.as_closed().expect("checked").clone().into(); -1649 if space_id == SpaceId::Data || reason.is_transport_layer() { -1650 reason -1651 } else { -1652 TransportError::APPLICATION_ERROR("").into() -1653 } -1654 } -1655 StateType::Draining => TransportError::NO_ERROR("").into(), -1656 _ => unreachable!( -1657 "tried to make a close packet when the connection wasn't closed" -1658 ), -1659 }; -1660 builder.write_frame(close.encoder(max_frame_size), stats); -1661 } -1662 let last_pn = builder.packet_number; -1663 builder.finish_and_track(now, self, path_id, pad_datagram); -1664 if space_id.kind() == self.highest_space { -1665 // Don't send another close packet. Even with multipath we only send -1666 // CONNECTION_CLOSE on a single path since we expect our paths to work. -1667 self.connection_close_pending = false; -1668 } -1669 // Send a close frame in every possible space for robustness, per -1670 // RFC9000 "Immediate Close during the Handshake". Don't bother trying -1671 // to send anything else. -1672 // TODO(flub): This breaks during the handshake if we can not coalesce -1673 // packets due to space reasons: the next space would either fail a -1674 // debug_assert checking for enough packet space or produce an invalid -1675 // packet. We need to keep track of per-space pending CONNECTION_CLOSE to -1676 // be able to send these across multiple calls to poll_transmit. Then -1677 // check for coalescing space here because initial packets need to be in -1678 // padded datagrams. And also add space checks for CONNECTION_CLOSE in -1679 // space_can_send so it would stop a GSO batch if the datagram is too -1680 // small for another CONNECTION_CLOSE packet. -1681 return PollPathSpaceStatus::WrotePacket { -1682 last_packet_number: last_pn, -1683 pad_datagram, -1684 }; -1685 } -1686 -1687 self.populate_packet(now, space_id, path_id, scheduling_info, &mut builder); -1688 -1689 // ACK-only packets should only be sent when explicitly allowed. If we write them due to -1690 // any other reason, there is a bug which leads to one component announcing write -1691 // readiness while not writing any data. This degrades performance. The condition is -1692 // only checked if the full MTU is available and when potentially large fixed-size -1693 // frames aren't queued, so that lack of space in the datagram isn't the reason for just -1694 // writing ACKs. -1695 debug_assert!( -1696 !(builder.sent_frames().is_ack_only(&self.streams) -1697 && !can_send.acks -1698 && (can_send.other || can_send.space_specific) -1699 && builder.buf.segment_size() -1700 == self.path_data(path_id).current_mtu() as usize -1701 && self.datagrams.outgoing.is_empty()), -1702 "SendableFrames was {can_send:?}, but only ACKs have been written" -1703 ); -1704 if builder.sent_frames().requires_padding { -1705 pad_datagram |= PadDatagram::ToMinMtu; -1706 } -1707 -1708 for (path_id, _pn) in builder.sent_frames().largest_acked.iter() { -1709 self.spaces[space_id] -1710 .for_path(*path_id) -1711 .pending_acks -1712 .acks_sent(); -1713 self.timers.stop( -1714 Timer::PerPath(*path_id, PathTimer::MaxAckDelay), -1715 self.qlog.with_time(now), -1716 ); -1717 } -1718 -1719 // Now we need to finish the packet. Before we do so we need to know if we will -1720 // be coalescing the next packet into this one, or will be ending the datagram -1721 // as well. Because if this is the last packet in the datagram more padding -1722 // might be needed because of the packet type, or to fill the GSO segment size. -1723 -1724 // Are we allowed to coalesce AND is there enough space for another *packet* in -1725 // this datagram AND will we definitely send another packet? -1726 if builder.can_coalesce && path_id == PathId::ZERO && { -1727 let max_packet_size = builder -1728 .buf -1729 .datagram_remaining_mut() -1730 .saturating_sub(builder.predict_packet_end()); -1731 max_packet_size > MIN_PACKET_SPACE -1732 && self.has_pending_packet(space_id, max_packet_size, connection_close_pending) -1733 } { -1734 // We can append/coalesce the next packet into the current -1735 // datagram. Finish the current packet without adding extra padding. -1736 trace!("will coalesce with next packet"); -1737 builder.finish_and_track(now, self, path_id, PadDatagram::No); -1738 } else { -1739 // We need a new datagram for the next packet. Finish the current -1740 // packet with padding. -1741 // TODO(flub): if there isn't any more data to be sent, this will still pad -1742 // to the segment size and only discover there is nothing to send before -1743 // starting the next packet. That is wasting up to 32 bytes. -1744 if builder.buf.num_datagrams() > 1 && matches!(pad_datagram, PadDatagram::No) { -1745 // If too many padding bytes would be required to continue the -1746 // GSO batch after this packet, end the GSO batch here. Ensures -1747 // that fixed-size frames with heterogeneous sizes -1748 // (e.g. application datagrams) won't inadvertently waste large -1749 // amounts of bandwidth. The exact threshold is a bit arbitrary -1750 // and might benefit from further tuning, though there's no -1751 // universally optimal value. -1752 const MAX_PADDING: usize = 32; -1753 if builder.buf.datagram_remaining_mut() -1754 > builder.predict_packet_end() + MAX_PADDING -1755 { -1756 trace!( -1757 "GSO truncated by demand for {} padding bytes", -1758 builder.buf.datagram_remaining_mut() - builder.predict_packet_end() -1759 ); -1760 let last_pn = builder.packet_number; -1761 builder.finish_and_track(now, self, path_id, PadDatagram::No); -1762 return PollPathSpaceStatus::Send { -1763 last_packet_number: last_pn, -1764 }; -1765 } -1766 -1767 // Pad the current datagram to GSO segment size so it can be -1768 // included in the GSO batch. -1769 builder.finish_and_track(now, self, path_id, PadDatagram::ToSegmentSize); -1770 } else { -1771 builder.finish_and_track(now, self, path_id, pad_datagram); -1772 } -1773 -1774 // If this is the first datagram we set the segment size to the size of the -1775 // first datagram. -1776 if transmit.num_datagrams() == 1 { -1777 transmit.clip_segment_size(); -1778 } -1779 } -1780 } -1781 } -1782 -1783 fn poll_transmit_mtu_probe( -1784 &mut self, -1785 now: Instant, -1786 buf: &mut Vec<u8>, -1787 path_id: PathId, -1788 ) -> Option<Transmit> { -1789 let (active_cid, probe_size) = self.get_mtu_probe_data(now, path_id)?; -1790 -1791 // We are definitely sending a DPLPMTUD probe. -1792 let mut transmit = TransmitBuf::new(buf, NonZeroUsize::MIN, probe_size as usize); -1793 transmit.start_new_datagram_with_size(probe_size as usize); +1635 // Since there only 64 ACK frames there will always be enough space +1636 // to encode the ConnectionClose frame too. However we still have the +1637 // check here to prevent crashes if something changes. +1638 +1639 // TODO(flub): This needs fixing for multipath, to ensure we can always +1640 // write the CONNECTION_CLOSE even if we have many PATH_ACKs to send: +1641 // https://github.com/n0-computer/noq/issues/367. +1642 debug_assert!( +1643 builder.frame_space_remaining() > frame::ConnectionClose::SIZE_BOUND, +1644 "ACKs should leave space for ConnectionClose" +1645 ); +1646 let stats = &mut self.path_stats.for_path(path_id).frame_tx; +1647 if frame::ConnectionClose::SIZE_BOUND < builder.frame_space_remaining() { +1648 let max_frame_size = builder.frame_space_remaining(); +1649 let close: Close = match self.state.as_type() { +1650 StateType::Closed => { +1651 let reason: Close = +1652 self.state.as_closed().expect("checked").clone().into(); +1653 if space_id == SpaceId::Data || reason.is_transport_layer() { +1654 reason +1655 } else { +1656 TransportError::APPLICATION_ERROR("").into() +1657 } +1658 } +1659 StateType::Draining => TransportError::NO_ERROR("").into(), +1660 _ => unreachable!( +1661 "tried to make a close packet when the connection wasn't closed" +1662 ), +1663 }; +1664 builder.write_frame(close.encoder(max_frame_size), stats); +1665 } +1666 let last_pn = builder.packet_number; +1667 builder.finish_and_track(now, self, path_id, pad_datagram); +1668 if space_id.kind() == self.highest_space { +1669 // Don't send another close packet. Even with multipath we only send +1670 // CONNECTION_CLOSE on a single path since we expect our paths to work. +1671 self.connection_close_pending = false; +1672 } +1673 // Send a close frame in every possible space for robustness, per +1674 // RFC9000 "Immediate Close during the Handshake". Don't bother trying +1675 // to send anything else. +1676 // TODO(flub): This breaks during the handshake if we can not coalesce +1677 // packets due to space reasons: the next space would either fail a +1678 // debug_assert checking for enough packet space or produce an invalid +1679 // packet. We need to keep track of per-space pending CONNECTION_CLOSE to +1680 // be able to send these across multiple calls to poll_transmit. Then +1681 // check for coalescing space here because initial packets need to be in +1682 // padded datagrams. And also add space checks for CONNECTION_CLOSE in +1683 // space_can_send so it would stop a GSO batch if the datagram is too +1684 // small for another CONNECTION_CLOSE packet. +1685 return PollPathSpaceStatus::WrotePacket { +1686 last_packet_number: last_pn, +1687 pad_datagram, +1688 }; +1689 } +1690 +1691 self.populate_packet(now, space_id, path_id, scheduling_info, &mut builder); +1692 +1693 // ACK-only packets should only be sent when explicitly allowed. If we write them due to +1694 // any other reason, there is a bug which leads to one component announcing write +1695 // readiness while not writing any data. This degrades performance. The condition is +1696 // only checked if the full MTU is available and when potentially large fixed-size +1697 // frames aren't queued, so that lack of space in the datagram isn't the reason for just +1698 // writing ACKs. +1699 debug_assert!( +1700 !(builder.sent_frames().is_ack_only(&self.streams) +1701 && !can_send.acks +1702 && (can_send.other || can_send.space_specific) +1703 && builder.buf.segment_size() +1704 == self.path_data(path_id).current_mtu() as usize +1705 && self.datagrams.outgoing.is_empty()), +1706 "SendableFrames was {can_send:?}, but only ACKs have been written" +1707 ); +1708 if builder.sent_frames().requires_padding { +1709 pad_datagram |= PadDatagram::ToMinMtu; +1710 } +1711 +1712 for (path_id, _pn) in builder.sent_frames().largest_acked.iter() { +1713 self.spaces[space_id] +1714 .for_path(*path_id) +1715 .pending_acks +1716 .acks_sent(); +1717 self.timers.stop( +1718 Timer::PerPath(*path_id, PathTimer::MaxAckDelay), +1719 self.qlog.with_time(now), +1720 ); +1721 } +1722 +1723 // Now we need to finish the packet. Before we do so we need to know if we will +1724 // be coalescing the next packet into this one, or will be ending the datagram +1725 // as well. Because if this is the last packet in the datagram more padding +1726 // might be needed because of the packet type, or to fill the GSO segment size. +1727 +1728 // Are we allowed to coalesce AND is there enough space for another *packet* in +1729 // this datagram AND will we definitely send another packet? +1730 if builder.can_coalesce && path_id == PathId::ZERO && { +1731 let max_packet_size = builder +1732 .buf +1733 .datagram_remaining_mut() +1734 .saturating_sub(builder.predict_packet_end()); +1735 max_packet_size > MIN_PACKET_SPACE +1736 && self.has_pending_packet(space_id, max_packet_size, connection_close_pending) +1737 } { +1738 // We can append/coalesce the next packet into the current +1739 // datagram. Finish the current packet without adding extra padding. +1740 trace!("will coalesce with next packet"); +1741 builder.finish_and_track(now, self, path_id, PadDatagram::No); +1742 } else { +1743 // We need a new datagram for the next packet. Finish the current +1744 // packet with padding. +1745 // TODO(flub): if there isn't any more data to be sent, this will still pad +1746 // to the segment size and only discover there is nothing to send before +1747 // starting the next packet. That is wasting up to 32 bytes. +1748 if builder.buf.num_datagrams() > 1 && matches!(pad_datagram, PadDatagram::No) { +1749 // If too many padding bytes would be required to continue the +1750 // GSO batch after this packet, end the GSO batch here. Ensures +1751 // that fixed-size frames with heterogeneous sizes +1752 // (e.g. application datagrams) won't inadvertently waste large +1753 // amounts of bandwidth. The exact threshold is a bit arbitrary +1754 // and might benefit from further tuning, though there's no +1755 // universally optimal value. +1756 const MAX_PADDING: usize = 32; +1757 if builder.buf.datagram_remaining_mut() +1758 > builder.predict_packet_end() + MAX_PADDING +1759 { +1760 trace!( +1761 "GSO truncated by demand for {} padding bytes", +1762 builder.buf.datagram_remaining_mut() - builder.predict_packet_end() +1763 ); +1764 let last_pn = builder.packet_number; +1765 builder.finish_and_track(now, self, path_id, PadDatagram::No); +1766 return PollPathSpaceStatus::Send { +1767 last_packet_number: last_pn, +1768 }; +1769 } +1770 +1771 // Pad the current datagram to GSO segment size so it can be +1772 // included in the GSO batch. +1773 builder.finish_and_track(now, self, path_id, PadDatagram::ToSegmentSize); +1774 } else { +1775 builder.finish_and_track(now, self, path_id, pad_datagram); +1776 } +1777 +1778 // If this is the first datagram we set the segment size to the size of the +1779 // first datagram. +1780 if transmit.num_datagrams() == 1 { +1781 transmit.clip_segment_size(); +1782 } +1783 } +1784 } +1785 } +1786 +1787 fn poll_transmit_mtu_probe( +1788 &mut self, +1789 now: Instant, +1790 buf: &mut Vec<u8>, +1791 path_id: PathId, +1792 ) -> Option<Transmit> { +1793 let (active_cid, probe_size) = self.get_mtu_probe_data(now, path_id)?; 1794 -1795 let mut builder = -1796 PacketBuilder::new(now, SpaceId::Data, path_id, active_cid, &mut transmit, self)?; -1797 -1798 // We implement MTU probes as ping packets padded up to the probe size -1799 trace!(?probe_size, "writing MTUD probe"); -1800 builder.write_frame(frame::Ping, &mut self.path_stats.for_path(path_id).frame_tx); +1795 // We are definitely sending a DPLPMTUD probe. +1796 let mut transmit = TransmitBuf::new(buf, NonZeroUsize::MIN, probe_size as usize); +1797 transmit.start_new_datagram_with_size(probe_size as usize); +1798 +1799 let mut builder = +1800 PacketBuilder::new(now, SpaceId::Data, path_id, active_cid, &mut transmit, self)?; 1801 -1802 // If supported by the peer, we want no delays to the probe's ACK -1803 if self.peer_supports_ack_frequency() { -1804 builder.write_frame( -1805 frame::ImmediateAck, -1806 &mut self.path_stats.for_path(path_id).frame_tx, -1807 ); -1808 } -1809 -1810 builder.finish_and_track(now, self, path_id, PadDatagram::ToSize(probe_size)); -1811 -1812 self.path_stats.for_path(path_id).sent_plpmtud_probes += 1; +1802 // We implement MTU probes as ping packets padded up to the probe size +1803 trace!(?probe_size, "writing MTUD probe"); +1804 builder.write_frame(frame::Ping, &mut self.path_stats.for_path(path_id).frame_tx); +1805 +1806 // If supported by the peer, we want no delays to the probe's ACK +1807 if self.peer_supports_ack_frequency() { +1808 builder.write_frame( +1809 frame::ImmediateAck, +1810 &mut self.path_stats.for_path(path_id).frame_tx, +1811 ); +1812 } 1813 -1814 Some(self.build_transmit(path_id, transmit)) -1815 } -1816 -1817 /// Returns the CID and probe size if a DPLPMTUD probe is needed. -1818 /// -1819 /// We MTU probe all paths for which all of the following is true: -1820 /// - We have an active destination CID for the path. -1821 /// - The remote address *and* path are validated. -1822 /// - The path is not abandoned. -1823 /// - The MTU Discovery subsystem wants to probe the path. -1824 fn get_mtu_probe_data(&mut self, now: Instant, path_id: PathId) -> Option<(ConnectionId, u16)> { -1825 let active_cid = self.remote_cids.get(&path_id).map(CidQueue::active)?; -1826 let is_eligible = self.path_data(path_id).validated -1827 && !self.path_data(path_id).is_validating_path() -1828 && !self.abandoned_paths.contains(&path_id); -1829 -1830 if !is_eligible { -1831 return None; -1832 } -1833 let next_pn = self.spaces[SpaceId::Data] -1834 .for_path(path_id) -1835 .peek_tx_number(); -1836 let probe_size = self -1837 .path_data_mut(path_id) -1838 .mtud -1839 .poll_transmit(now, next_pn)?; -1840 -1841 Some((active_cid, probe_size)) -1842 } -1843 -1844 /// Returns true if there is a further packet to send on [`PathId::ZERO`]. -1845 /// -1846 /// In other words this is predicting whether the next call to -1847 /// [`Connection::space_can_send`] issued will return some frames to be sent. Including -1848 /// having to predict which packet number space it will be invoked with. This depends on -1849 /// how both [`Connection::poll_transmit_on_path`] and -1850 /// [`Connection::poll_transmit_path_space`] behave. -1851 /// -1852 /// This is needed to determine if packet coalescing can happen. Because the last packet -1853 /// in a datagram may need to be padded and thus we must know if another packet will -1854 /// follow or not. +1814 builder.finish_and_track(now, self, path_id, PadDatagram::ToSize(probe_size)); +1815 +1816 self.path_stats.for_path(path_id).sent_plpmtud_probes += 1; +1817 +1818 Some(self.build_transmit(path_id, transmit)) +1819 } +1820 +1821 /// Returns the CID and probe size if a DPLPMTUD probe is needed. +1822 /// +1823 /// We MTU probe all paths for which all of the following is true: +1824 /// - We have an active destination CID for the path. +1825 /// - The remote address *and* path are validated. +1826 /// - The path is not abandoned. +1827 /// - The MTU Discovery subsystem wants to probe the path. +1828 fn get_mtu_probe_data(&mut self, now: Instant, path_id: PathId) -> Option<(ConnectionId, u16)> { +1829 let active_cid = self.remote_cids.get(&path_id).map(CidQueue::active)?; +1830 let is_eligible = self.path_data(path_id).validated +1831 && !self.path_data(path_id).is_validating_path() +1832 && !self.abandoned_paths.contains(&path_id); +1833 +1834 if !is_eligible { +1835 return None; +1836 } +1837 let next_pn = self.spaces[SpaceId::Data] +1838 .for_path(path_id) +1839 .peek_tx_number(); +1840 let probe_size = self +1841 .path_data_mut(path_id) +1842 .mtud +1843 .poll_transmit(now, next_pn)?; +1844 +1845 Some((active_cid, probe_size)) +1846 } +1847 +1848 /// Returns true if there is a further packet to send on [`PathId::ZERO`]. +1849 /// +1850 /// In other words this is predicting whether the next call to +1851 /// [`Connection::space_can_send`] issued will return some frames to be sent. Including +1852 /// having to predict which packet number space it will be invoked with. This depends on +1853 /// how both [`Connection::poll_transmit_on_path`] and +1854 /// [`Connection::poll_transmit_path_space`] behave. 1855 /// -1856 /// The next packet can be either in the same space, or in one of the following spaces -1857 /// on the same path. Because a 0-RTT packet can be coalesced with a 1-RTT packet and -1858 /// both are in the Data(PathId::ZERO) space. Previous spaces are not checked, because -1859 /// packets are built from Initial to Handshake to Data spaces. -1860 fn has_pending_packet( -1861 &mut self, -1862 current_space_id: SpaceId, -1863 max_packet_size: usize, -1864 connection_close_pending: bool, -1865 ) -> bool { -1866 let mut space_id = current_space_id; -1867 loop { -1868 let can_send = self.space_can_send( -1869 space_id, -1870 PathId::ZERO, -1871 max_packet_size, -1872 connection_close_pending, -1873 ); -1874 if !can_send.is_empty() { -1875 return true; -1876 } -1877 match space_id.next() { -1878 Some(next_space_id) => space_id = next_space_id, -1879 None => break, +1856 /// This is needed to determine if packet coalescing can happen. Because the last packet +1857 /// in a datagram may need to be padded and thus we must know if another packet will +1858 /// follow or not. +1859 /// +1860 /// The next packet can be either in the same space, or in one of the following spaces +1861 /// on the same path. Because a 0-RTT packet can be coalesced with a 1-RTT packet and +1862 /// both are in the Data(PathId::ZERO) space. Previous spaces are not checked, because +1863 /// packets are built from Initial to Handshake to Data spaces. +1864 fn has_pending_packet( +1865 &mut self, +1866 current_space_id: SpaceId, +1867 max_packet_size: usize, +1868 connection_close_pending: bool, +1869 ) -> bool { +1870 let mut space_id = current_space_id; +1871 loop { +1872 let can_send = self.space_can_send( +1873 space_id, +1874 PathId::ZERO, +1875 max_packet_size, +1876 connection_close_pending, +1877 ); +1878 if !can_send.is_empty() { +1879 return true; 1880 } -1881 } -1882 false -1883 } -1884 -1885 /// Checks if creating a new datagram would be blocked by congestion control -1886 fn path_congestion_check( -1887 &mut self, -1888 space_id: SpaceId, -1889 path_id: PathId, -1890 transmit: &TransmitBuf<'_>, -1891 can_send: &SendableFrames, -1892 now: Instant, -1893 ) -> PathBlocked { -1894 // Anti-amplification is only based on `total_sent`, which gets updated after -1895 // the transmit is sent. Therefore we pass the amount of bytes for datagrams -1896 // that are already created, as well as 1 byte for starting another datagram. If -1897 // there is any anti-amplification budget left, we always allow a full MTU to be -1898 // sent (see https://github.com/quinn-rs/quinn/issues/1082). -1899 if self.side().is_server() -1900 && self -1901 .path_data(path_id) -1902 .anti_amplification_blocked(transmit.len() as u64 + 1) -1903 { -1904 trace!(?space_id, %path_id, "blocked by anti-amplification"); -1905 return PathBlocked::AntiAmplification; -1906 } -1907 -1908 // Congestion control check. -1909 // Tail loss probes must not be blocked by congestion, or a deadlock could arise. -1910 let bytes_to_send = transmit.segment_size() as u64; -1911 let need_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0; -1912 -1913 if can_send.other && !need_loss_probe && !can_send.close { -1914 let path = self.path_data(path_id); -1915 if path.in_flight.bytes + bytes_to_send >= path.congestion.window() { -1916 trace!( -1917 ?space_id, -1918 %path_id, -1919 in_flight=%path.in_flight.bytes, -1920 congestion_window=%path.congestion.window(), -1921 "blocked by congestion control", -1922 ); -1923 return PathBlocked::Congestion; -1924 } -1925 } -1926 -1927 // Pacing check. -1928 if let Some(delay) = self.path_data_mut(path_id).pacing_delay(bytes_to_send, now) { -1929 let resume_time = now + delay; -1930 self.timers.set( -1931 Timer::PerPath(path_id, PathTimer::Pacing), -1932 resume_time, -1933 self.qlog.with_time(now), -1934 ); -1935 // Loss probes and CONNECTION_CLOSE should be subject to pacing, even though -1936 // they are not congestion controlled. -1937 trace!(?space_id, %path_id, ?delay, "blocked by pacing"); -1938 return PathBlocked::Pacing; -1939 } -1940 -1941 PathBlocked::No -1942 } -1943 -1944 /// Send PATH_CHALLENGE for a previous path if necessary -1945 /// -1946 /// QUIC-TRANSPORT section 9.3.3 -1947 /// <https://www.rfc-editor.org/rfc/rfc9000.html#name-off-path-packet-forwarding> -1948 fn send_prev_path_challenge( -1949 &mut self, -1950 now: Instant, -1951 buf: &mut Vec<u8>, -1952 path_id: PathId, -1953 ) -> Option<Transmit> { -1954 let (prev_cid, prev_path) = self.paths.get_mut(&path_id)?.prev.as_mut()?; -1955 if !prev_path.pending_on_path_challenge { -1956 return None; -1957 }; -1958 prev_path.pending_on_path_challenge = false; -1959 let token = self.rng.random(); -1960 let network_path = prev_path.network_path; -1961 prev_path.record_path_challenge_sent(now, token, network_path); -1962 -1963 debug_assert_eq!( -1964 self.highest_space, -1965 SpaceKind::Data, -1966 "PATH_CHALLENGE queued without 1-RTT keys" -1967 ); -1968 let buf = &mut TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into()); -1969 buf.start_new_datagram(); -1970 -1971 // Use the previous CID to avoid linking the new path with the previous path. We -1972 // don't bother accounting for possible retirement of that prev_cid because this is -1973 // sent once, immediately after migration, when the CID is known to be valid. Even -1974 // if a post-migration packet caused the CID to be retired, it's fair to pretend -1975 // this is sent first. -1976 let mut builder = PacketBuilder::new(now, SpaceId::Data, path_id, *prev_cid, buf, self)?; -1977 let challenge = frame::PathChallenge(token); -1978 let stats = &mut self.path_stats.for_path(path_id).frame_tx; -1979 builder.write_frame_with_log_msg(challenge, stats, Some("validating previous path")); -1980 -1981 // An endpoint MUST expand datagrams that contain a PATH_CHALLENGE frame -1982 // to at least the smallest allowed maximum datagram size of 1200 bytes, -1983 // unless the anti-amplification limit for the path does not permit -1984 // sending a datagram of this size -1985 builder.pad_to(MIN_INITIAL_SIZE); -1986 -1987 builder.finish(self, now); -1988 self.path_stats -1989 .for_path(path_id) -1990 .udp_tx -1991 .on_sent(1, buf.len()); -1992 -1993 trace!( -1994 dst = ?network_path.remote, -1995 src = ?network_path.local_ip, -1996 len = buf.len(), -1997 "sending prev_path off-path challenge", -1998 ); -1999 Some(Transmit { -2000 destination: network_path.remote, -2001 size: buf.len(), -2002 ecn: None, -2003 segment_size: None, -2004 src_ip: network_path.local_ip, -2005 }) -2006 } -2007 -2008 fn send_off_path_path_response( -2009 &mut self, -2010 now: Instant, -2011 buf: &mut Vec<u8>, -2012 path_id: PathId, -2013 ) -> Option<Transmit> { -2014 let path = self.paths.get_mut(&path_id).map(|state| &mut state.data)?; -2015 let cid_queue = self.remote_cids.get_mut(&path_id)?; -2016 let (token, network_path) = path.path_responses.pop_off_path(path.network_path)?; -2017 -2018 // TODO: make off-path probes unlinkable. -2019 let cid = cid_queue.active(); -2020 -2021 let frame = frame::PathResponse(token); -2022 -2023 let buf = &mut TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into()); -2024 buf.start_new_datagram(); +1881 match space_id.next() { +1882 Some(next_space_id) => space_id = next_space_id, +1883 None => break, +1884 } +1885 } +1886 false +1887 } +1888 +1889 /// Checks if creating a new datagram would be blocked by congestion control +1890 fn path_congestion_check( +1891 &mut self, +1892 space_id: SpaceId, +1893 path_id: PathId, +1894 transmit: &TransmitBuf<'_>, +1895 can_send: &SendableFrames, +1896 now: Instant, +1897 ) -> PathBlocked { +1898 // Anti-amplification is only based on `total_sent`, which gets updated after +1899 // the transmit is sent. Therefore we pass the amount of bytes for datagrams +1900 // that are already created, as well as 1 byte for starting another datagram. If +1901 // there is any anti-amplification budget left, we always allow a full MTU to be +1902 // sent (see https://github.com/quinn-rs/quinn/issues/1082). +1903 if self.side().is_server() +1904 && self +1905 .path_data(path_id) +1906 .anti_amplification_blocked(transmit.len() as u64 + 1) +1907 { +1908 trace!(?space_id, %path_id, "blocked by anti-amplification"); +1909 return PathBlocked::AntiAmplification; +1910 } +1911 +1912 // Congestion control check. +1913 // Tail loss probes must not be blocked by congestion, or a deadlock could arise. +1914 let bytes_to_send = transmit.segment_size() as u64; +1915 let need_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0; +1916 +1917 if can_send.other && !need_loss_probe && !can_send.close { +1918 let path = self.path_data(path_id); +1919 if path.in_flight.bytes + bytes_to_send >= path.congestion.window() { +1920 trace!( +1921 ?space_id, +1922 %path_id, +1923 in_flight=%path.in_flight.bytes, +1924 congestion_window=%path.congestion.window(), +1925 "blocked by congestion control", +1926 ); +1927 return PathBlocked::Congestion; +1928 } +1929 } +1930 +1931 // Pacing check. +1932 if let Some(delay) = self.path_data_mut(path_id).pacing_delay(bytes_to_send, now) { +1933 let resume_time = now + delay; +1934 self.timers.set( +1935 Timer::PerPath(path_id, PathTimer::Pacing), +1936 resume_time, +1937 self.qlog.with_time(now), +1938 ); +1939 // Loss probes and CONNECTION_CLOSE should be subject to pacing, even though +1940 // they are not congestion controlled. +1941 trace!(?space_id, %path_id, ?delay, "blocked by pacing"); +1942 return PathBlocked::Pacing; +1943 } +1944 +1945 PathBlocked::No +1946 } +1947 +1948 /// Send PATH_CHALLENGE for a previous path if necessary +1949 /// +1950 /// QUIC-TRANSPORT section 9.3.3 +1951 /// <https://www.rfc-editor.org/rfc/rfc9000.html#name-off-path-packet-forwarding> +1952 fn send_prev_path_challenge( +1953 &mut self, +1954 now: Instant, +1955 buf: &mut Vec<u8>, +1956 path_id: PathId, +1957 ) -> Option<Transmit> { +1958 let (prev_cid, prev_path) = self.paths.get_mut(&path_id)?.prev.as_mut()?; +1959 if !prev_path.pending_challenge { +1960 return None; +1961 }; +1962 prev_path.pending_challenge = false; +1963 let token = self.rng.random(); +1964 let network_path = prev_path.network_path; +1965 prev_path.record_path_challenge_sent(now, token, network_path); +1966 +1967 debug_assert_eq!( +1968 self.highest_space, +1969 SpaceKind::Data, +1970 "PATH_CHALLENGE queued without 1-RTT keys" +1971 ); +1972 let buf = &mut TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into()); +1973 buf.start_new_datagram(); +1974 +1975 // Use the previous CID to avoid linking the new path with the previous path. We +1976 // don't bother accounting for possible retirement of that prev_cid because this is +1977 // sent once, immediately after migration, when the CID is known to be valid. Even +1978 // if a post-migration packet caused the CID to be retired, it's fair to pretend +1979 // this is sent first. +1980 let mut builder = PacketBuilder::new(now, SpaceId::Data, path_id, *prev_cid, buf, self)?; +1981 let challenge = frame::PathChallenge(token); +1982 let stats = &mut self.path_stats.for_path(path_id).frame_tx; +1983 builder.write_frame_with_log_msg(challenge, stats, Some("validating previous path")); +1984 +1985 // An endpoint MUST expand datagrams that contain a PATH_CHALLENGE frame +1986 // to at least the smallest allowed maximum datagram size of 1200 bytes, +1987 // unless the anti-amplification limit for the path does not permit +1988 // sending a datagram of this size +1989 builder.pad_to(MIN_INITIAL_SIZE); +1990 +1991 builder.finish(self, now); +1992 self.path_stats +1993 .for_path(path_id) +1994 .udp_tx +1995 .on_sent(1, buf.len()); +1996 +1997 trace!( +1998 dst = ?network_path.remote, +1999 src = ?network_path.local_ip, +2000 len = buf.len(), +2001 "sending prev_path off-path challenge", +2002 ); +2003 Some(Transmit { +2004 destination: network_path.remote, +2005 size: buf.len(), +2006 ecn: None, +2007 segment_size: None, +2008 src_ip: network_path.local_ip, +2009 }) +2010 } +2011 +2012 fn send_off_path_path_response( +2013 &mut self, +2014 now: Instant, +2015 buf: &mut Vec<u8>, +2016 path_id: PathId, +2017 ) -> Option<Transmit> { +2018 let network_path = self +2019 .paths +2020 .get_mut(&path_id) +2021 .map(|state| state.data.network_path)?; +2022 let cid_queue = self.remote_cids.get_mut(&path_id)?; +2023 let pns = self.spaces[SpaceKind::Data].for_path(path_id); +2024 let (token, network_path) = pns.pending_path_responses.pop_off_path(network_path)?; 2025 -2026 let mut builder = PacketBuilder::new(now, SpaceId::Data, path_id, cid, buf, self)?; -2027 let stats = &mut self.path_stats.for_path(path_id).frame_tx; -2028 builder.write_frame_with_log_msg(frame, stats, Some("(off-path)")); -2029 -2030 // If we are a client doing NAT traversal, always include a PATH_CHALLENGE with any -2031 // off-path PATH_RESPONSE. No need to schedule any retries for this, if NAT -2032 // traversal is taking place then this remote already is being probed with -2033 // retries, this only speeds up a successful traversal. -2034 if self -2035 .find_validated_path_on_network_path(network_path) -2036 .is_none() -2037 && self.n0_nat_traversal.client_side().is_ok() -2038 { -2039 let token = self.rng.random(); -2040 let stats = &mut self.path_stats.for_path(path_id).frame_tx; -2041 builder.write_frame(frame::PathChallenge(token), stats); -2042 let ip_port = (network_path.remote.ip(), network_path.remote.port()); -2043 self.n0_nat_traversal.mark_probe_sent(ip_port, token); -2044 } -2045 -2046 // Off-path: not tracked in congestion control. The packet is sent to a -2047 // different destination than path_id's network path. -2048 builder.pad_to(MIN_INITIAL_SIZE); -2049 builder.finish(self, now); -2050 -2051 let size = buf.len(); -2052 self.path_stats.for_path(path_id).udp_tx.on_sent(1, size); -2053 -2054 trace!( -2055 dst = ?network_path.remote, -2056 src = ?network_path.local_ip, -2057 len = buf.len(), -2058 "sending off-path PATH_RESPONSE", -2059 ); -2060 Some(Transmit { -2061 destination: network_path.remote, -2062 size, -2063 ecn: None, -2064 segment_size: None, -2065 src_ip: network_path.local_ip, -2066 }) -2067 } -2068 -2069 /// Send a nat traversal challenge (off-path) on this path if possible. -2070 fn send_nat_traversal_path_challenge( -2071 &mut self, -2072 now: Instant, -2073 buf: &mut Vec<u8>, -2074 path_id: PathId, -2075 ) -> Option<Transmit> { -2076 let remote = self.n0_nat_traversal.next_probe_addr()?; -2077 -2078 if !self.paths.get(&path_id)?.data.validated { -2079 // Path is not usable for probing -2080 return None; -2081 } -2082 -2083 // TODO: Using the active CID here makes the paths linkable. This is a violation of -2084 // RFC9000 but something we want to accept in the short term. Eventually we aim -2085 // to fix up the supply of CIDs sufficiently so that we can keep paths unlinkable -2086 // again. -2087 let Some(cid) = self -2088 .remote_cids -2089 .get(&path_id) -2090 .map(|cid_queue| cid_queue.active()) -2091 else { -2092 trace!(%path_id, "Not sending NAT traversal probe for path with no CIDs"); -2093 return None; -2094 }; -2095 let token = self.rng.random(); -2096 -2097 let frame = frame::PathChallenge(token); -2098 -2099 let mut buf = TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into()); -2100 buf.start_new_datagram(); -2101 -2102 let mut builder = PacketBuilder::new(now, SpaceId::Data, path_id, cid, &mut buf, self)?; -2103 let stats = &mut self.path_stats.for_path(path_id).frame_tx; -2104 builder.write_frame_with_log_msg(frame, stats, Some("(nat-traversal)")); -2105 // Off-path: not tracked in congestion control. The packet is sent to a -2106 // different destination than path_id's network path. -2107 builder.finish(self, now); -2108 -2109 // Mark as sent after packet build succeeds. -2110 self.n0_nat_traversal.mark_probe_sent(remote, token); -2111 -2112 let size = buf.len(); -2113 self.path_stats.for_path(path_id).udp_tx.on_sent(1, size); -2114 -2115 trace!(dst = ?remote, len = buf.len(), "sending off-path NAT probe"); -2116 Some(Transmit { -2117 destination: remote.into(), -2118 size, -2119 ecn: None, -2120 segment_size: None, -2121 src_ip: None, -2122 }) -2123 } -2124 -2125 /// Indicate what types of frames are ready to send for the given space. -2126 /// -2127 /// Only for on-path data. -2128 /// -2129 /// *packet_size* is the number of bytes available to build the next packet. -2130 /// *connection_close_pending* indicates whether a CONNECTION_CLOSE frame needs to be -2131 /// sent. -2132 fn space_can_send( -2133 &mut self, -2134 space_id: SpaceId, -2135 path_id: PathId, -2136 packet_size: usize, -2137 connection_close_pending: bool, -2138 ) -> SendableFrames { -2139 let space = &mut self.spaces[space_id]; -2140 let space_has_crypto = self.crypto_state.has_keys(space_id.encryption_level()); -2141 -2142 if !space_has_crypto -2143 && (space_id != SpaceId::Data -2144 || !self.crypto_state.has_keys(EncryptionLevel::ZeroRtt) -2145 || self.side.is_server()) -2146 { -2147 // Nothing to send in this space -2148 return SendableFrames::empty(); -2149 } -2150 -2151 let mut can_send = space.can_send(path_id, &self.streams); -2152 -2153 // Check for 1RTT space. -2154 if space_id == SpaceId::Data { -2155 let pn = space.for_path(path_id).peek_tx_number(); -2156 // Number of bytes available for frames if this is a 1-RTT packet. We're -2157 // guaranteed to be able to send an individual frame at least this large in the -2158 // next 1-RTT packet. This could be generalized to support every space, but it's -2159 // only needed to handle large fixed-size frames, which only exist in 1-RTT -2160 // (application datagrams). -2161 let frame_space_1rtt = -2162 packet_size.saturating_sub(self.predict_1rtt_overhead(pn, path_id)); -2163 can_send |= self.can_send_1rtt(path_id, frame_space_1rtt); -2164 } -2165 -2166 can_send.close = connection_close_pending && space_has_crypto; -2167 -2168 can_send -2169 } -2170 -2171 /// Process `ConnectionEvent`s generated by the associated `Endpoint` -2172 /// -2173 /// Will execute protocol logic upon receipt of a connection event, in turn preparing signals -2174 /// (including application `Event`s, `EndpointEvent`s and outgoing datagrams) that should be -2175 /// extracted through the relevant methods. -2176 pub fn handle_event(&mut self, event: ConnectionEvent) { -2177 use ConnectionEventInner::*; -2178 match event.0 { -2179 Datagram(DatagramConnectionEvent { -2180 now, -2181 network_path, -2182 path_id, -2183 ecn, -2184 first_decode, -2185 remaining, -2186 }) => { -2187 let span = trace_span!("pkt", %path_id); -2188 let _guard = span.enter(); -2189 -2190 if self.early_discard_packet(network_path, path_id) { -2191 // A return value of true indicates we should discard this packet. -2192 return; -2193 } -2194 -2195 let was_anti_amplification_blocked = self -2196 .path(path_id) -2197 .map(|path| path.anti_amplification_blocked(1)) -2198 // We never tried to send on an non-existing (new) path so have not been -2199 // anti-amplification blocked for it previously. -2200 .unwrap_or(false); +2026 // TODO: make off-path probes unlinkable. +2027 let cid = cid_queue.active(); +2028 +2029 // PATH_RESPONSE (off-path) +2030 let frame = frame::PathResponse(token); +2031 +2032 let buf = &mut TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into()); +2033 buf.start_new_datagram(); +2034 +2035 let mut builder = PacketBuilder::new(now, SpaceId::Data, path_id, cid, buf, self)?; +2036 let stats = &mut self.path_stats.for_path(path_id).frame_tx; +2037 builder.write_frame_with_log_msg(frame, stats, Some("(off-path)")); +2038 +2039 // PATH_CHALLENGE (off-path) +2040 // +2041 // If we are a client doing NAT traversal, always include a PATH_CHALLENGE with any +2042 // off-path PATH_RESPONSE. No need to schedule any retries for this, if NAT +2043 // traversal is taking place then this remote already is being probed with +2044 // retries, this only speeds up a successful traversal. +2045 if self +2046 .find_validated_path_on_network_path(network_path) +2047 .is_none() +2048 && self.n0_nat_traversal.client_side().is_ok() +2049 { +2050 let token = self.rng.random(); +2051 let stats = &mut self.path_stats.for_path(path_id).frame_tx; +2052 builder.write_frame(frame::PathChallenge(token), stats); +2053 let ip_port = (network_path.remote.ip(), network_path.remote.port()); +2054 self.n0_nat_traversal.mark_probe_sent(ip_port, token); +2055 } +2056 +2057 // Off-path: not tracked in congestion control. The packet is sent to a +2058 // different destination than path_id's network path. +2059 builder.pad_to(MIN_INITIAL_SIZE); +2060 builder.finish(self, now); +2061 +2062 let size = buf.len(); +2063 self.path_stats.for_path(path_id).udp_tx.on_sent(1, size); +2064 +2065 trace!( +2066 dst = ?network_path.remote, +2067 src = ?network_path.local_ip, +2068 len = buf.len(), +2069 "sending off-path PATH_RESPONSE", +2070 ); +2071 Some(Transmit { +2072 destination: network_path.remote, +2073 size, +2074 ecn: None, +2075 segment_size: None, +2076 src_ip: network_path.local_ip, +2077 }) +2078 } +2079 +2080 /// Send a nat traversal challenge (off-path) on this path if possible. +2081 fn send_nat_traversal_path_challenge( +2082 &mut self, +2083 now: Instant, +2084 buf: &mut Vec<u8>, +2085 path_id: PathId, +2086 ) -> Option<Transmit> { +2087 let remote = self.n0_nat_traversal.next_probe_addr()?; +2088 +2089 if !self.paths.get(&path_id)?.data.validated { +2090 // Path is not usable for probing +2091 return None; +2092 } +2093 +2094 // TODO: Using the active CID here makes the paths linkable. This is a violation of +2095 // RFC9000 but something we want to accept in the short term. Eventually we aim +2096 // to fix up the supply of CIDs sufficiently so that we can keep paths unlinkable +2097 // again. +2098 let Some(cid) = self +2099 .remote_cids +2100 .get(&path_id) +2101 .map(|cid_queue| cid_queue.active()) +2102 else { +2103 trace!(%path_id, "Not sending NAT traversal probe for path with no CIDs"); +2104 return None; +2105 }; +2106 let token = self.rng.random(); +2107 +2108 // PATH_CHALLENGE (NAT probe) +2109 let frame = frame::PathChallenge(token); +2110 +2111 let mut buf = TransmitBuf::new(buf, NonZeroUsize::MIN, MIN_INITIAL_SIZE.into()); +2112 buf.start_new_datagram(); +2113 +2114 let mut builder = PacketBuilder::new(now, SpaceId::Data, path_id, cid, &mut buf, self)?; +2115 let stats = &mut self.path_stats.for_path(path_id).frame_tx; +2116 builder.write_frame_with_log_msg(frame, stats, Some("(nat-traversal)")); +2117 // Off-path: not tracked in congestion control. The packet is sent to a +2118 // different destination than path_id's network path. +2119 builder.finish(self, now); +2120 +2121 // Mark as sent after packet build succeeds. +2122 self.n0_nat_traversal.mark_probe_sent(remote, token); +2123 +2124 let size = buf.len(); +2125 self.path_stats.for_path(path_id).udp_tx.on_sent(1, size); +2126 +2127 trace!(dst = ?remote, len = buf.len(), "sending off-path NAT probe"); +2128 Some(Transmit { +2129 destination: remote.into(), +2130 size, +2131 ecn: None, +2132 segment_size: None, +2133 src_ip: None, +2134 }) +2135 } +2136 +2137 /// Indicate what types of frames are ready to send for the given space. +2138 /// +2139 /// Only for on-path data. +2140 /// +2141 /// *packet_size* is the number of bytes available to build the next packet. +2142 /// *connection_close_pending* indicates whether a CONNECTION_CLOSE frame needs to be +2143 /// sent. +2144 fn space_can_send( +2145 &mut self, +2146 space_id: SpaceId, +2147 path_id: PathId, +2148 packet_size: usize, +2149 connection_close_pending: bool, +2150 ) -> SendableFrames { +2151 let space = &mut self.spaces[space_id]; +2152 let space_has_crypto = self.crypto_state.has_keys(space_id.encryption_level()); +2153 +2154 if !space_has_crypto +2155 && (space_id != SpaceId::Data +2156 || !self.crypto_state.has_keys(EncryptionLevel::ZeroRtt) +2157 || self.side.is_server()) +2158 { +2159 // Nothing to send in this space +2160 return SendableFrames::empty(); +2161 } +2162 +2163 let mut can_send = space.can_send(path_id, &self.streams); +2164 +2165 // Check for 1RTT space. +2166 if space_id == SpaceId::Data { +2167 let pn = space.for_path(path_id).peek_tx_number(); +2168 // Number of bytes available for frames if this is a 1-RTT packet. We're +2169 // guaranteed to be able to send an individual frame at least this large in the +2170 // next 1-RTT packet. This could be generalized to support every space, but it's +2171 // only needed to handle large fixed-size frames, which only exist in 1-RTT +2172 // (application datagrams). +2173 let frame_space_1rtt = +2174 packet_size.saturating_sub(self.predict_1rtt_overhead(pn, path_id)); +2175 can_send |= self.can_send_1rtt(path_id, frame_space_1rtt); +2176 } +2177 +2178 can_send.close = connection_close_pending && space_has_crypto; +2179 +2180 can_send +2181 } +2182 +2183 /// Process `ConnectionEvent`s generated by the associated `Endpoint` +2184 /// +2185 /// Will execute protocol logic upon receipt of a connection event, in turn preparing signals +2186 /// (including application `Event`s, `EndpointEvent`s and outgoing datagrams) that should be +2187 /// extracted through the relevant methods. +2188 pub fn handle_event(&mut self, event: ConnectionEvent) { +2189 use ConnectionEventInner::*; +2190 match event.0 { +2191 Datagram(DatagramConnectionEvent { +2192 now, +2193 network_path, +2194 path_id, +2195 ecn, +2196 first_decode, +2197 remaining, +2198 }) => { +2199 let span = trace_span!("pkt", %path_id); +2200 let _guard = span.enter(); 2201 -2202 let rx = &mut self.path_stats.for_path(path_id).udp_rx; -2203 rx.datagrams += 1; -2204 rx.bytes += first_decode.len() as u64; -2205 let data_len = first_decode.len(); +2202 if self.early_discard_packet(network_path, path_id) { +2203 // A return value of true indicates we should discard this packet. +2204 return; +2205 } 2206 -2207 self.handle_decode(now, network_path, path_id, ecn, first_decode); -2208 // The current `path` might have changed inside `handle_decode` since the packet -2209 // could have triggered a migration. The packet might also belong to an unknown -2210 // path and have been rejected. Make sure the data received is accounted for the -2211 // most recent path by accessing `path` after `handle_decode`. -2212 if let Some(path) = self.path_mut(path_id) { -2213 path.inc_total_recvd(data_len as u64); -2214 } -2215 -2216 if let Some(data) = remaining { -2217 self.path_stats.for_path(path_id).udp_rx.bytes += data.len() as u64; -2218 self.handle_coalesced(now, network_path, path_id, ecn, data); -2219 } -2220 -2221 if let Some(path) = self.paths.get_mut(&path_id) { -2222 self.qlog -2223 .emit_recovery_metrics(path_id, &mut path.data, now); -2224 } -2225 -2226 if was_anti_amplification_blocked { -2227 // A prior attempt to set the loss detection timer may have failed due to -2228 // anti-amplification, so ensure it's set now. Prevents a handshake deadlock if -2229 // the server's first flight is lost. -2230 self.set_loss_detection_timer(now, path_id); +2207 let was_anti_amplification_blocked = self +2208 .path(path_id) +2209 .map(|path| path.anti_amplification_blocked(1)) +2210 // We never tried to send on an non-existing (new) path so have not been +2211 // anti-amplification blocked for it previously. +2212 .unwrap_or(false); +2213 +2214 let rx = &mut self.path_stats.for_path(path_id).udp_rx; +2215 rx.datagrams += 1; +2216 rx.bytes += first_decode.len() as u64; +2217 let data_len = first_decode.len(); +2218 +2219 self.handle_decode(now, network_path, path_id, ecn, first_decode); +2220 // The current `path` might have changed inside `handle_decode` since the packet +2221 // could have triggered a migration. The packet might also belong to an unknown +2222 // path and have been rejected. Make sure the data received is accounted for the +2223 // most recent path by accessing `path` after `handle_decode`. +2224 if let Some(path) = self.path_mut(path_id) { +2225 path.inc_total_recvd(data_len as u64); +2226 } +2227 +2228 if let Some(data) = remaining { +2229 self.path_stats.for_path(path_id).udp_rx.bytes += data.len() as u64; +2230 self.handle_coalesced(now, network_path, path_id, ecn, data); 2231 } -2232 } -2233 NewIdentifiers(ids, now, cid_len, cid_lifetime) => { -2234 let path_id = ids.first().map(|issued| issued.path_id).unwrap_or_default(); -2235 debug_assert!(ids.iter().all(|issued| issued.path_id == path_id)); -2236 -2237 // Path may have been abandoned while this reply was in flight, -2238 // retire the CIDs instead of queuing them. -2239 if self.abandoned_paths.contains(&path_id) { -2240 if !self.state.is_drained() { -2241 for issued in &ids { -2242 self.endpoint_events -2243 .push_back(EndpointEventInner::RetireConnectionId( -2244 now, -2245 path_id, -2246 issued.sequence, -2247 false, -2248 )); -2249 } -2250 } -2251 return; -2252 } -2253 -2254 let cid_state = self -2255 .local_cid_state -2256 .entry(path_id) -2257 .or_insert_with(|| CidState::new(cid_len, cid_lifetime, now, 0)); -2258 cid_state.new_cids(&ids, now); -2259 -2260 ids.into_iter().rev().for_each(|frame| { -2261 self.spaces[SpaceId::Data].pending.new_cids.push(frame); -2262 }); -2263 // Always update Timer::PushNewCid -2264 self.reset_cid_retirement(now); -2265 } -2266 } -2267 } -2268 -2269 /// Returns whether a packet can be discarded early. -2270 /// -2271 /// Packets sent on the wrong network path can be entirely ignored, saving further -2272 /// processing. -2273 /// -2274 /// Returns true if a packet coming in for this `path_id` over given `network_path` -2275 /// should be discarded. -2276 fn early_discard_packet(&mut self, network_path: FourTuple, path_id: PathId) -> bool { -2277 if self.is_handshaking() && path_id != PathId::ZERO { -2278 debug!(%network_path, %path_id, "discarding multipath packet during handshake"); -2279 return true; -2280 } -2281 -2282 let peer_may_probe = self.peer_may_probe(); -2283 let local_ip_may_migrate = self.local_ip_may_migrate(); -2284 -2285 // If this packet could initiate a migration and we're a client or a server that -2286 // forbids migration, drop the datagram. This could be relaxed to heuristically -2287 // permit NAT-rebinding-like migration. -2288 if let Some(known_path) = self.path_mut(path_id) { -2289 if network_path.remote != known_path.network_path.remote && !peer_may_probe { -2290 trace!( -2291 %path_id, -2292 %network_path, -2293 %known_path.network_path, -2294 "discarding packet from unrecognized peer" -2295 ); -2296 return true; -2297 } -2298 -2299 if known_path.network_path.local_ip.is_some() -2300 && network_path.local_ip.is_some() -2301 && known_path.network_path.local_ip != network_path.local_ip -2302 && !local_ip_may_migrate -2303 { -2304 trace!( -2305 %path_id, -2306 %network_path, -2307 %known_path.network_path, -2308 "discarding packet sent to incorrect interface" -2309 ); -2310 return true; -2311 } -2312 } -2313 false -2314 } -2315 -2316 /// Whether the peer may probe new paths. -2317 /// -2318 /// RFC9000 §9 and QNT both have probing packets which may arrive from new paths. This -2319 /// indicates whether these are allowed or not. This is a strict superset from -2320 /// [`Self::peer_may_migrate`]: every network path that may be migrated to, may also -2321 /// be probed. But e.g. servers may not migrate, but can be allowed to probe. -2322 // TODO(flub): In RFC9000 the server is allowed to send off-path probing packets -2323 // once the client has been probing such a 4-tuple. These probes are currently -2324 // not yet recognised and will end up being discarded because of this. -2325 // See https://github.com/n0-computer/noq/issues/607. -2326 fn peer_may_probe(&self) -> bool { -2327 match &self.side { -2328 ConnectionSide::Client { .. } => { -2329 if let Some(hs) = self.state.as_handshake() { -2330 hs.allow_server_migration -2331 } else { -2332 self.n0_nat_traversal.is_negotiated() && self.is_handshake_confirmed() -2333 } -2334 } -2335 ConnectionSide::Server { server_config } => { -2336 self.is_handshake_confirmed() -2337 && (server_config.migration || self.n0_nat_traversal.is_negotiated()) -2338 } -2339 } -2340 } -2341 -2342 /// Whether the peer's remote address may migrate. -2343 /// -2344 /// In RFC9000 only the client may migrate. -2345 /// -2346 /// QUIC relies on stable endpoints during the handshake. So other than the server's -2347 /// preferred_address transport parameter no side may migrate before the handshake is -2348 /// completed. -2349 /// -2350 /// It is noteworthy that for iroh we allow server migrations during the handshake when -2351 /// [`state::Handshake::allow_server_migration`] is enabled, but that is handled earlier -2352 /// in [`Self::handle_packet`] and without probing the current and previous paths. -2353 fn peer_may_migrate(&self) -> bool { -2354 match &self.side { -2355 ConnectionSide::Server { server_config } => { -2356 server_config.migration && self.is_handshake_confirmed() -2357 } -2358 ConnectionSide::Client { .. } => false, -2359 } -2360 } -2361 -2362 /// Whether our local IP address is allowed to change with new incoming packets. -2363 /// -2364 /// Incoming packets show us the local IP address we received a packet on, which could -2365 /// be different from what we thought due to e.g. NAT rebinding or moving from mobile -2366 /// data to WiFi without being notified of the network change. -2367 /// -2368 /// This is only allowed to happen after the handshake is confirmed and when we are the -2369 /// client. Unless QNT is negotiated in which case the server is also allowed to -2370 /// migrate. -2371 /// -2372 /// Be aware that probing packets, which do not exist in Multipath without QNT, are -2373 /// exempt from this. -2374 fn local_ip_may_migrate(&self) -> bool { -2375 (self.side.is_client() || self.n0_nat_traversal.is_negotiated()) -2376 && self.is_handshake_confirmed() -2377 } -2378 /// Process timer expirations +2232 +2233 if let Some(path) = self.paths.get_mut(&path_id) { +2234 self.qlog +2235 .emit_recovery_metrics(path_id, &mut path.data, now); +2236 } +2237 +2238 if was_anti_amplification_blocked { +2239 // A prior attempt to set the loss detection timer may have failed due to +2240 // anti-amplification, so ensure it's set now. Prevents a handshake deadlock if +2241 // the server's first flight is lost. +2242 self.set_loss_detection_timer(now, path_id); +2243 } +2244 } +2245 NewIdentifiers(ids, now, cid_len, cid_lifetime) => { +2246 let path_id = ids.first().map(|issued| issued.path_id).unwrap_or_default(); +2247 debug_assert!(ids.iter().all(|issued| issued.path_id == path_id)); +2248 +2249 // Path may have been abandoned while this reply was in flight, +2250 // retire the CIDs instead of queuing them. +2251 if self.abandoned_paths.contains(&path_id) { +2252 if !self.state.is_drained() { +2253 for issued in &ids { +2254 self.endpoint_events +2255 .push_back(EndpointEventInner::RetireConnectionId( +2256 now, +2257 path_id, +2258 issued.sequence, +2259 false, +2260 )); +2261 } +2262 } +2263 return; +2264 } +2265 +2266 let cid_state = self +2267 .local_cid_state +2268 .entry(path_id) +2269 .or_insert_with(|| CidState::new(cid_len, cid_lifetime, now, 0)); +2270 cid_state.new_cids(&ids, now); +2271 +2272 ids.into_iter().rev().for_each(|frame| { +2273 self.spaces[SpaceId::Data].pending.new_cids.push(frame); +2274 }); +2275 // Always update Timer::PushNewCid +2276 self.reset_cid_retirement(now); +2277 } +2278 } +2279 } +2280 +2281 /// Returns whether a packet can be discarded early. +2282 /// +2283 /// Packets sent on the wrong network path can be entirely ignored, saving further +2284 /// processing. +2285 /// +2286 /// Returns true if a packet coming in for this `path_id` over given `network_path` +2287 /// should be discarded. +2288 fn early_discard_packet(&mut self, network_path: FourTuple, path_id: PathId) -> bool { +2289 if self.is_handshaking() && path_id != PathId::ZERO { +2290 debug!(%network_path, %path_id, "discarding multipath packet during handshake"); +2291 return true; +2292 } +2293 +2294 let peer_may_probe = self.peer_may_probe(); +2295 let local_ip_may_migrate = self.local_ip_may_migrate(); +2296 +2297 // If this packet could initiate a migration and we're a client or a server that +2298 // forbids migration, drop the datagram. This could be relaxed to heuristically +2299 // permit NAT-rebinding-like migration. +2300 if let Some(known_path) = self.path_mut(path_id) { +2301 if network_path.remote != known_path.network_path.remote && !peer_may_probe { +2302 trace!( +2303 %path_id, +2304 %network_path, +2305 %known_path.network_path, +2306 "discarding packet from unrecognized peer" +2307 ); +2308 return true; +2309 } +2310 +2311 if known_path.network_path.local_ip.is_some() +2312 && network_path.local_ip.is_some() +2313 && known_path.network_path.local_ip != network_path.local_ip +2314 && !local_ip_may_migrate +2315 { +2316 trace!( +2317 %path_id, +2318 %network_path, +2319 %known_path.network_path, +2320 "discarding packet sent to incorrect interface" +2321 ); +2322 return true; +2323 } +2324 } +2325 false +2326 } +2327 +2328 /// Whether the peer may probe new paths. +2329 /// +2330 /// RFC9000 §9 and QNT both have probing packets which may arrive from new paths. This +2331 /// indicates whether these are allowed or not. This is a strict superset from +2332 /// [`Self::peer_may_migrate`]: every network path that may be migrated to, may also +2333 /// be probed. But e.g. servers may not migrate, but can be allowed to probe. +2334 // TODO(flub): In RFC9000 the server is allowed to send off-path probing packets +2335 // once the client has been probing such a 4-tuple. These probes are currently +2336 // not yet recognised and will end up being discarded because of this. +2337 // See https://github.com/n0-computer/noq/issues/607. +2338 fn peer_may_probe(&self) -> bool { +2339 match &self.side { +2340 ConnectionSide::Client { .. } => { +2341 if let Some(hs) = self.state.as_handshake() { +2342 hs.allow_server_migration +2343 } else { +2344 self.n0_nat_traversal.is_negotiated() && self.is_handshake_confirmed() +2345 } +2346 } +2347 ConnectionSide::Server { server_config } => { +2348 self.is_handshake_confirmed() +2349 && (server_config.migration || self.n0_nat_traversal.is_negotiated()) +2350 } +2351 } +2352 } +2353 +2354 /// Whether the peer's remote address may migrate. +2355 /// +2356 /// In RFC9000 only the client may migrate. +2357 /// +2358 /// QUIC relies on stable endpoints during the handshake. So other than the server's +2359 /// preferred_address transport parameter no side may migrate before the handshake is +2360 /// completed. +2361 /// +2362 /// It is noteworthy that for iroh we allow server migrations during the handshake when +2363 /// [`state::Handshake::allow_server_migration`] is enabled, but that is handled earlier +2364 /// in [`Self::handle_packet`] and without probing the current and previous paths. +2365 fn peer_may_migrate(&self) -> bool { +2366 match &self.side { +2367 ConnectionSide::Server { server_config } => { +2368 server_config.migration && self.is_handshake_confirmed() +2369 } +2370 ConnectionSide::Client { .. } => false, +2371 } +2372 } +2373 +2374 /// Whether our local IP address is allowed to change with new incoming packets. +2375 /// +2376 /// Incoming packets show us the local IP address we received a packet on, which could +2377 /// be different from what we thought due to e.g. NAT rebinding or moving from mobile +2378 /// data to WiFi without being notified of the network change. 2379 /// -2380 /// Executes protocol logic, potentially preparing signals (including application `Event`s, -2381 /// `EndpointEvent`s and outgoing datagrams) that should be extracted through the relevant -2382 /// methods. +2380 /// This is only allowed to happen after the handshake is confirmed and when we are the +2381 /// client. Unless QNT is negotiated in which case the server is also allowed to +2382 /// migrate. 2383 /// -2384 /// It is most efficient to call this immediately after the system clock reaches the latest -2385 /// `Instant` that was output by `poll_timeout`; however spurious extra calls will simply -2386 /// no-op and therefore are safe. -2387 pub fn handle_timeout(&mut self, now: Instant) { -2388 while let Some((timer, _time)) = self.timers.expire_before(now, &self.qlog) { -2389 let span = match timer { -2390 Timer::Conn(timer) => trace_span!("timeout", scope = "conn", ?timer), -2391 Timer::PerPath(path_id, timer) => { -2392 trace_span!("timer_fired", scope="path", %path_id, ?timer) -2393 } -2394 }; -2395 let _guard = span.enter(); -2396 trace!("timeout"); -2397 match timer { -2398 Timer::Conn(timer) => match timer { -2399 ConnTimer::Close => { -2400 let was_draining = self.state.move_to_drained(None); -2401 if !was_draining { -2402 self.endpoint_events.push_back(EndpointEventInner::Draining); -2403 } -2404 // move_to_drained checks that we weren't in drained before. -2405 // Adding events to endpoint_events is only legal if `Drained` was never queued before. -2406 self.endpoint_events.push_back(EndpointEventInner::Drained); -2407 } -2408 ConnTimer::Idle => { -2409 self.kill(ConnectionError::TimedOut); -2410 } -2411 ConnTimer::KeepAlive => { -2412 self.ping(); -2413 } -2414 ConnTimer::KeyDiscard => { -2415 self.crypto_state.discard_temporary_keys(); -2416 } -2417 ConnTimer::PushNewCid => { -2418 while let Some((path_id, when)) = self.next_cid_retirement() { -2419 if when > now { -2420 break; -2421 } -2422 match self.local_cid_state.get_mut(&path_id) { -2423 None => error!(%path_id, "No local CID state for path"), -2424 Some(cid_state) => { -2425 // Update `retire_prior_to` field in NEW_CONNECTION_ID frame -2426 let num_new_cid = cid_state.on_cid_timeout().into(); -2427 if !self.state.is_closed() { -2428 trace!( -2429 "push a new CID to peer RETIRE_PRIOR_TO field {}", -2430 cid_state.retire_prior_to() -2431 ); -2432 self.endpoint_events.push_back( -2433 EndpointEventInner::NeedIdentifiers( -2434 path_id, -2435 now, -2436 num_new_cid, -2437 ), -2438 ); -2439 } -2440 } -2441 } -2442 } -2443 } -2444 ConnTimer::NoAvailablePath => { -2445 // Grace period expired: all paths were abandoned and no new path -2446 // was opened. Close the connection. There are no paths left to -2447 // send CONNECTION_CLOSE on, so this is a silent close. -2448 // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-21.html#section-3.4-8 -2449 if self.state.is_closed() || self.state.is_drained() { -2450 // Connection already closing/drained (e.g. application called -2451 // close() before the grace timer fired). Nothing to do. -2452 error!("no viable path timer fired, but connection already closing"); -2453 } else { -2454 trace!("no viable path grace period expired, closing connection"); -2455 let err = TransportError::NO_VIABLE_PATH( -2456 "last path abandoned, no new path opened", -2457 ); -2458 self.close_common(); -2459 self.set_close_timer(now); -2460 self.connection_close_pending = true; -2461 self.state.move_to_closed(err); -2462 } -2463 } -2464 ConnTimer::NatTraversalProbeRetry => { -2465 self.n0_nat_traversal.queue_retries(self.is_ipv6()); -2466 if let Some(delay) = -2467 self.n0_nat_traversal.retry_delay(self.config.initial_rtt) -2468 { -2469 self.timers.set( -2470 Timer::Conn(ConnTimer::NatTraversalProbeRetry), -2471 now + delay, -2472 self.qlog.with_time(now), -2473 ); -2474 trace!("re-queued NAT probes"); -2475 } else { -2476 trace!("no more NAT probes remaining"); -2477 } -2478 } -2479 }, -2480 Timer::PerPath(path_id, timer) => { -2481 match timer { -2482 PathTimer::PathIdle => { -2483 if let Err(err) = -2484 self.close_path_inner(now, path_id, PathAbandonReason::TimedOut) -2485 { -2486 warn!(?err, "failed closing path"); -2487 } -2488 } -2489 -2490 PathTimer::PathKeepAlive => { -2491 self.ping_path(path_id).ok(); -2492 } -2493 PathTimer::LossDetection => { -2494 self.on_loss_detection_timeout(now, path_id); -2495 if let Some(path) = self.paths.get_mut(&path_id) { -2496 self.qlog -2497 .emit_recovery_metrics(path_id, &mut path.data, now); -2498 } else { -2499 error!("LossDetection fired for unknown path"); -2500 } -2501 } -2502 PathTimer::PathValidationFailed => { -2503 let Some(path) = self.paths.get_mut(&path_id) else { -2504 continue; -2505 }; -2506 self.timers.stop( -2507 Timer::PerPath(path_id, PathTimer::PathChallengeLost), -2508 self.qlog.with_time(now), -2509 ); -2510 debug!("path migration validation failed"); -2511 if let Some((_, prev)) = path.prev.take() { -2512 path.data = prev; -2513 } -2514 path.data.reset_on_path_challenges(); -2515 } -2516 PathTimer::PathChallengeLost => { -2517 let Some(path) = self.paths.get_mut(&path_id) else { -2518 continue; -2519 }; -2520 trace!(?path.data.on_path_challenges_lost, "path challenge deemed lost"); -2521 path.data.pending_on_path_challenge = true; -2522 path.data.on_path_challenges_lost += 1; -2523 } -2524 PathTimer::AbandonFromValidation => { -2525 let Some(path) = self.paths.get_mut(&path_id) else { -2526 continue; -2527 }; -2528 path.data.reset_on_path_challenges(); -2529 self.timers.stop( -2530 Timer::PerPath(path_id, PathTimer::PathChallengeLost), -2531 self.qlog.with_time(now), -2532 ); -2533 debug!("new path validation failed"); -2534 if let Err(err) = self.close_path_inner( -2535 now, -2536 path_id, -2537 PathAbandonReason::ValidationFailed, -2538 ) { -2539 warn!(?err, "failed closing path"); -2540 } -2541 } -2542 PathTimer::Pacing => {} -2543 PathTimer::MaxAckDelay => { -2544 // This timer is only armed in the Data space -2545 self.spaces[SpaceId::Data] -2546 .for_path(path_id) -2547 .pending_acks -2548 .on_max_ack_delay_timeout() -2549 } -2550 PathTimer::PathDrained => { -2551 // The path was abandoned and 3*PTO has expired since. Clean up all -2552 // remaining state and install stateless reset token. -2553 self.timers.stop_per_path(path_id, self.qlog.with_time(now)); -2554 if let Some(local_cid_state) = self.local_cid_state.remove(&path_id) { -2555 debug_assert!(!self.state.is_drained()); // requirement for endpoint_events. All timers should be cleared in drained connections. -2556 let (min_seq, max_seq) = local_cid_state.active_seq(); -2557 for seq in min_seq..=max_seq { -2558 self.endpoint_events.push_back( -2559 EndpointEventInner::RetireConnectionId( -2560 now, path_id, seq, false, -2561 ), -2562 ); -2563 } -2564 } -2565 self.discard_path(path_id, now); -2566 } -2567 } -2568 } -2569 } -2570 } -2571 } -2572 -2573 /// Close a connection immediately -2574 /// -2575 /// This does not ensure delivery of outstanding data. It is the application's responsibility to -2576 /// call this only when all important communications have been completed, e.g. by calling -2577 /// [`SendStream::finish`] on outstanding streams and waiting for the corresponding -2578 /// [`StreamEvent::Finished`] event. -2579 /// -2580 /// If [`Streams::send_streams`] returns 0, all outstanding stream data has been -2581 /// delivered. There may still be data from the peer that has not been received. -2582 /// -2583 /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished -2584 pub fn close(&mut self, now: Instant, error_code: VarInt, reason: Bytes) { -2585 self.close_inner( -2586 now, -2587 Close::Application(frame::ApplicationClose { error_code, reason }), -2588 ) -2589 } -2590 -2591 /// Close the connection immediately, initiated by an API call. -2592 /// -2593 /// This will not produce a [`ConnectionLost`] event propagated by the -2594 /// [`Connection::poll`] call, because the API call already propagated the error to the -2595 /// user. -2596 /// -2597 /// Not to be used when entering immediate close due to an internal state change based -2598 /// on an event. See [`State::move_to_closed_local`] for details. -2599 /// -2600 /// This initiates immediate close from -2601 /// <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2>, moving to the closed -2602 /// state. -2603 /// -2604 /// [`ConnectionLost`]: crate::Event::ConnectionLost -2605 /// [`Connection::poll`]: super::Connection::poll -2606 fn close_inner(&mut self, now: Instant, reason: Close) { -2607 let was_closed = self.state.is_closed(); -2608 if !was_closed { -2609 self.close_common(); -2610 self.set_close_timer(now); -2611 self.connection_close_pending = true; -2612 self.state.move_to_closed_local(reason); -2613 } -2614 } -2615 -2616 /// Control datagrams -2617 pub fn datagrams(&mut self) -> Datagrams<'_> { -2618 Datagrams { conn: self } -2619 } -2620 -2621 /// Returns connection statistics -2622 pub fn stats(&mut self) -> ConnectionStats { -2623 let mut stats = self.partial_stats.clone(); -2624 -2625 for path_stats in self.path_stats.iter_stats() { -2626 // Self::path_stats() computes the path rtt, cwnd and current_mtu on access -2627 // because they are not simple counters. When computing the connection stats we -2628 // can skip that effort since those fields are not used in the `impl -2629 // Add<PathStats> for ConnectionStats`. -2630 stats += *path_stats; -2631 } +2384 /// Be aware that probing packets, which do not exist in Multipath without QNT, are +2385 /// exempt from this. +2386 fn local_ip_may_migrate(&self) -> bool { +2387 (self.side.is_client() || self.n0_nat_traversal.is_negotiated()) +2388 && self.is_handshake_confirmed() +2389 } +2390 /// Process timer expirations +2391 /// +2392 /// Executes protocol logic, potentially preparing signals (including application `Event`s, +2393 /// `EndpointEvent`s and outgoing datagrams) that should be extracted through the relevant +2394 /// methods. +2395 /// +2396 /// It is most efficient to call this immediately after the system clock reaches the latest +2397 /// `Instant` that was output by `poll_timeout`; however spurious extra calls will simply +2398 /// no-op and therefore are safe. +2399 pub fn handle_timeout(&mut self, now: Instant) { +2400 while let Some((timer, _time)) = self.timers.expire_before(now, &self.qlog) { +2401 let span = match timer { +2402 Timer::Conn(timer) => trace_span!("timeout", scope = "conn", ?timer), +2403 Timer::PerPath(path_id, timer) => { +2404 trace_span!("timer_fired", scope="path", %path_id, ?timer) +2405 } +2406 }; +2407 let _guard = span.enter(); +2408 trace!("timeout"); +2409 match timer { +2410 Timer::Conn(timer) => match timer { +2411 ConnTimer::Close => { +2412 let was_draining = self.state.move_to_drained(None); +2413 if !was_draining { +2414 self.endpoint_events.push_back(EndpointEventInner::Draining); +2415 } +2416 // move_to_drained checks that we weren't in drained before. +2417 // Adding events to endpoint_events is only legal if `Drained` was never queued before. +2418 self.endpoint_events.push_back(EndpointEventInner::Drained); +2419 } +2420 ConnTimer::Idle => { +2421 self.kill(ConnectionError::TimedOut); +2422 } +2423 ConnTimer::KeepAlive => { +2424 self.ping(); +2425 } +2426 ConnTimer::KeyDiscard => { +2427 self.crypto_state.discard_temporary_keys(); +2428 } +2429 ConnTimer::PushNewCid => { +2430 while let Some((path_id, when)) = self.next_cid_retirement() { +2431 if when > now { +2432 break; +2433 } +2434 match self.local_cid_state.get_mut(&path_id) { +2435 None => error!(%path_id, "No local CID state for path"), +2436 Some(cid_state) => { +2437 // Update `retire_prior_to` field in NEW_CONNECTION_ID frame +2438 let num_new_cid = cid_state.on_cid_timeout().into(); +2439 if !self.state.is_closed() { +2440 trace!( +2441 "push a new CID to peer RETIRE_PRIOR_TO field {}", +2442 cid_state.retire_prior_to() +2443 ); +2444 self.endpoint_events.push_back( +2445 EndpointEventInner::NeedIdentifiers( +2446 path_id, +2447 now, +2448 num_new_cid, +2449 ), +2450 ); +2451 } +2452 } +2453 } +2454 } +2455 } +2456 ConnTimer::NoAvailablePath => { +2457 // Grace period expired: all paths were abandoned and no new path +2458 // was opened. Close the connection. There are no paths left to +2459 // send CONNECTION_CLOSE on, so this is a silent close. +2460 // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-21.html#section-3.4-8 +2461 if self.state.is_closed() || self.state.is_drained() { +2462 // Connection already closing/drained (e.g. application called +2463 // close() before the grace timer fired). Nothing to do. +2464 error!("no viable path timer fired, but connection already closing"); +2465 } else { +2466 trace!("no viable path grace period expired, closing connection"); +2467 let err = TransportError::NO_VIABLE_PATH( +2468 "last path abandoned, no new path opened", +2469 ); +2470 self.close_common(); +2471 self.set_close_timer(now); +2472 self.connection_close_pending = true; +2473 self.state.move_to_closed(err); +2474 } +2475 } +2476 ConnTimer::NatTraversalProbeRetry => { +2477 self.n0_nat_traversal.queue_retries(self.is_ipv6()); +2478 if let Some(delay) = +2479 self.n0_nat_traversal.retry_delay(self.config.initial_rtt) +2480 { +2481 self.timers.set( +2482 Timer::Conn(ConnTimer::NatTraversalProbeRetry), +2483 now + delay, +2484 self.qlog.with_time(now), +2485 ); +2486 trace!("re-queued NAT probes"); +2487 } else { +2488 trace!("no more NAT probes remaining"); +2489 } +2490 } +2491 }, +2492 Timer::PerPath(path_id, timer) => { +2493 match timer { +2494 PathTimer::PathIdle => { +2495 if let Err(err) = +2496 self.close_path_inner(now, path_id, PathAbandonReason::TimedOut) +2497 { +2498 warn!(?err, "failed closing path"); +2499 } +2500 } +2501 +2502 PathTimer::PathKeepAlive => { +2503 self.ping_path(path_id).ok(); +2504 } +2505 PathTimer::LossDetection => { +2506 self.on_loss_detection_timeout(now, path_id); +2507 if let Some(path) = self.paths.get_mut(&path_id) { +2508 self.qlog +2509 .emit_recovery_metrics(path_id, &mut path.data, now); +2510 } else { +2511 error!("LossDetection fired for unknown path"); +2512 } +2513 } +2514 PathTimer::PathValidationFailed => { +2515 let Some(path) = self.paths.get_mut(&path_id) else { +2516 continue; +2517 }; +2518 self.timers.stop( +2519 Timer::PerPath(path_id, PathTimer::PathChallengeLost), +2520 self.qlog.with_time(now), +2521 ); +2522 debug!("path migration validation failed"); +2523 if let Some((_, prev)) = path.prev.take() { +2524 path.data = prev; +2525 } +2526 path.data.reset_on_path_challenges(); +2527 } +2528 PathTimer::PathChallengeLost => { +2529 let Some(path) = self.paths.get_mut(&path_id) else { +2530 continue; +2531 }; +2532 trace!(?path.data.lost_challenge_count, "path challenge deemed lost"); +2533 path.data.pending_challenge = true; +2534 path.data.lost_challenge_count += 1; +2535 } +2536 PathTimer::AbandonFromValidation => { +2537 let Some(path) = self.paths.get_mut(&path_id) else { +2538 continue; +2539 }; +2540 path.data.reset_on_path_challenges(); +2541 self.timers.stop( +2542 Timer::PerPath(path_id, PathTimer::PathChallengeLost), +2543 self.qlog.with_time(now), +2544 ); +2545 debug!("new path validation failed"); +2546 if let Err(err) = self.close_path_inner( +2547 now, +2548 path_id, +2549 PathAbandonReason::ValidationFailed, +2550 ) { +2551 warn!(?err, "failed closing path"); +2552 } +2553 } +2554 PathTimer::Pacing => {} +2555 PathTimer::MaxAckDelay => { +2556 // This timer is only armed in the Data space +2557 self.spaces[SpaceId::Data] +2558 .for_path(path_id) +2559 .pending_acks +2560 .on_max_ack_delay_timeout() +2561 } +2562 PathTimer::PathDrained => { +2563 // The path was abandoned and 3*PTO has expired since. Clean up all +2564 // remaining state and install stateless reset token. +2565 self.timers.stop_per_path(path_id, self.qlog.with_time(now)); +2566 if let Some(local_cid_state) = self.local_cid_state.remove(&path_id) { +2567 debug_assert!(!self.state.is_drained()); // requirement for endpoint_events. All timers should be cleared in drained connections. +2568 let (min_seq, max_seq) = local_cid_state.active_seq(); +2569 for seq in min_seq..=max_seq { +2570 self.endpoint_events.push_back( +2571 EndpointEventInner::RetireConnectionId( +2572 now, path_id, seq, false, +2573 ), +2574 ); +2575 } +2576 } +2577 self.discard_path(path_id, now); +2578 } +2579 } +2580 } +2581 } +2582 } +2583 } +2584 +2585 /// Close a connection immediately +2586 /// +2587 /// This does not ensure delivery of outstanding data. It is the application's responsibility to +2588 /// call this only when all important communications have been completed, e.g. by calling +2589 /// [`SendStream::finish`] on outstanding streams and waiting for the corresponding +2590 /// [`StreamEvent::Finished`] event. +2591 /// +2592 /// If [`Streams::send_streams`] returns 0, all outstanding stream data has been +2593 /// delivered. There may still be data from the peer that has not been received. +2594 /// +2595 /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished +2596 pub fn close(&mut self, now: Instant, error_code: VarInt, reason: Bytes) { +2597 self.close_inner( +2598 now, +2599 Close::Application(frame::ApplicationClose { error_code, reason }), +2600 ) +2601 } +2602 +2603 /// Close the connection immediately, initiated by an API call. +2604 /// +2605 /// This will not produce a [`ConnectionLost`] event propagated by the +2606 /// [`Connection::poll`] call, because the API call already propagated the error to the +2607 /// user. +2608 /// +2609 /// Not to be used when entering immediate close due to an internal state change based +2610 /// on an event. See [`State::move_to_closed_local`] for details. +2611 /// +2612 /// This initiates immediate close from +2613 /// <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2>, moving to the closed +2614 /// state. +2615 /// +2616 /// [`ConnectionLost`]: crate::Event::ConnectionLost +2617 /// [`Connection::poll`]: super::Connection::poll +2618 fn close_inner(&mut self, now: Instant, reason: Close) { +2619 let was_closed = self.state.is_closed(); +2620 if !was_closed { +2621 self.close_common(); +2622 self.set_close_timer(now); +2623 self.connection_close_pending = true; +2624 self.state.move_to_closed_local(reason); +2625 } +2626 } +2627 +2628 /// Control datagrams +2629 pub fn datagrams(&mut self) -> Datagrams<'_> { +2630 Datagrams { conn: self } +2631 } 2632 -2633 stats -2634 } -2635 -2636 /// Returns path statistics -2637 pub fn path_stats(&mut self, path_id: PathId) -> Option<PathStats> { -2638 let path = self.paths.get(&path_id)?; -2639 let stats = self.path_stats.for_path(path_id); -2640 stats.rtt = path.data.rtt.get(); -2641 stats.cwnd = path.data.congestion.window(); -2642 stats.current_mtu = path.data.mtud.current_mtu(); -2643 Some(*stats) -2644 } -2645 -2646 /// Ping the remote endpoint -2647 /// -2648 /// Causes an ACK-eliciting packet to be transmitted on the connection. -2649 pub fn ping(&mut self) { -2650 // TODO(flub): This is very brute-force: it pings *all* the paths. Instead it would -2651 // be nice if we could only send a single packet for this. -2652 for path_data in self.spaces[self.highest_space].number_spaces.values_mut() { -2653 path_data.ping_pending = true; -2654 } -2655 } -2656 -2657 /// Ping the remote endpoint over a specific path -2658 /// -2659 /// Causes an ACK-eliciting packet to be transmitted on the path. -2660 pub fn ping_path(&mut self, path: PathId) -> Result<(), ClosedPath> { -2661 let path_data = self.spaces[self.highest_space] -2662 .number_spaces -2663 .get_mut(&path) -2664 .ok_or(ClosedPath { _private: () })?; -2665 path_data.ping_pending = true; -2666 Ok(()) +2633 /// Returns connection statistics +2634 pub fn stats(&mut self) -> ConnectionStats { +2635 let mut stats = self.partial_stats.clone(); +2636 +2637 for path_stats in self.path_stats.iter_stats() { +2638 // Self::path_stats() computes the path rtt, cwnd and current_mtu on access +2639 // because they are not simple counters. When computing the connection stats we +2640 // can skip that effort since those fields are not used in the `impl +2641 // Add<PathStats> for ConnectionStats`. +2642 stats += *path_stats; +2643 } +2644 +2645 stats +2646 } +2647 +2648 /// Returns path statistics +2649 pub fn path_stats(&mut self, path_id: PathId) -> Option<PathStats> { +2650 let path = self.paths.get(&path_id)?; +2651 let stats = self.path_stats.for_path(path_id); +2652 stats.rtt = path.data.rtt.get(); +2653 stats.cwnd = path.data.congestion.window(); +2654 stats.current_mtu = path.data.mtud.current_mtu(); +2655 Some(*stats) +2656 } +2657 +2658 /// Ping the remote endpoint +2659 /// +2660 /// Causes an ACK-eliciting packet to be transmitted on the connection. +2661 pub fn ping(&mut self) { +2662 // TODO(flub): This is very brute-force: it pings *all* the paths. Instead it would +2663 // be nice if we could only send a single packet for this. +2664 for path_data in self.spaces[self.highest_space].number_spaces.values_mut() { +2665 path_data.pending_ping = true; +2666 } 2667 } 2668 -2669 /// Update traffic keys spontaneously +2669 /// Ping the remote endpoint over a specific path 2670 /// -2671 /// This can be useful for testing key updates, as they otherwise only happen infrequently. -2672 pub fn force_key_update(&mut self) { -2673 if !self.state.is_established() { -2674 debug!("ignoring forced key update in illegal state"); -2675 return; -2676 } -2677 if self.crypto_state.prev_crypto.is_some() { -2678 // We already just updated, or are currently updating, the keys. Concurrent key updates -2679 // are illegal. -2680 debug!("ignoring redundant forced key update"); -2681 return; -2682 } -2683 self.crypto_state.update_keys(None, false); -2684 } -2685 -2686 /// Get a session reference -2687 pub fn crypto_session(&self) -> &dyn crypto::Session { -2688 self.crypto_state.session.as_ref() -2689 } -2690 -2691 /// Whether the connection is in the process of being established -2692 /// -2693 /// If this returns `false`, the connection may be either established or closed, signaled by the -2694 /// emission of a [`Connected`](Event::Connected) or [`ConnectionLost`](Event::ConnectionLost) -2695 /// event respectively. Note that locally-initiated closes via [`close()`](Self::close) do not -2696 /// emit a `ConnectionLost` event. -2697 /// -2698 /// For an established connection this essentially means the handshake is **completed**, -2699 /// but not necessarily yet confirmed. -2700 pub fn is_handshaking(&self) -> bool { -2701 self.state.is_handshake() -2702 } -2703 -2704 /// Whether the connection is closed -2705 /// -2706 /// Closed connections cannot transport any further data. A connection becomes closed when -2707 /// either peer application intentionally closes it, or when either transport layer detects an -2708 /// error such as a time-out or certificate validation failure. +2671 /// Causes an ACK-eliciting packet to be transmitted on the path. +2672 pub fn ping_path(&mut self, path: PathId) -> Result<(), ClosedPath> { +2673 let path_data = self.spaces[self.highest_space] +2674 .number_spaces +2675 .get_mut(&path) +2676 .ok_or(ClosedPath { _private: () })?; +2677 path_data.pending_ping = true; +2678 Ok(()) +2679 } +2680 +2681 /// Update traffic keys spontaneously +2682 /// +2683 /// This can be useful for testing key updates, as they otherwise only happen infrequently. +2684 pub fn force_key_update(&mut self) { +2685 if !self.state.is_established() { +2686 debug!("ignoring forced key update in illegal state"); +2687 return; +2688 } +2689 if self.crypto_state.prev_crypto.is_some() { +2690 // We already just updated, or are currently updating, the keys. Concurrent key updates +2691 // are illegal. +2692 debug!("ignoring redundant forced key update"); +2693 return; +2694 } +2695 self.crypto_state.update_keys(None, false); +2696 } +2697 +2698 /// Get a session reference +2699 pub fn crypto_session(&self) -> &dyn crypto::Session { +2700 self.crypto_state.session.as_ref() +2701 } +2702 +2703 /// Whether the connection is in the process of being established +2704 /// +2705 /// If this returns `false`, the connection may be either established or closed, signaled by the +2706 /// emission of a [`Connected`](Event::Connected) or [`ConnectionLost`](Event::ConnectionLost) +2707 /// event respectively. Note that locally-initiated closes via [`close()`](Self::close) do not +2708 /// emit a `ConnectionLost` event. 2709 /// -2710 /// A [`ConnectionLost`](Event::ConnectionLost) event is emitted with details when the -2711 /// connection is closed by the peer or due to an error. When the local application closes -2712 /// the connection via [`close()`](Self::close), no `ConnectionLost` event is emitted; -2713 /// instead, pending operations fail with [`ConnectionError::LocallyClosed`]. -2714 pub fn is_closed(&self) -> bool { -2715 self.state.is_closed() -2716 } -2717 -2718 /// Whether there is no longer any need to keep the connection around -2719 /// -2720 /// Closed connections become drained after a brief timeout to absorb any remaining in-flight -2721 /// packets from the peer. All drained connections have been closed. -2722 pub fn is_drained(&self) -> bool { -2723 self.state.is_drained() -2724 } -2725 -2726 /// For clients, if the peer accepted the 0-RTT data packets -2727 /// -2728 /// The value is meaningless until after the handshake completes. -2729 pub fn accepted_0rtt(&self) -> bool { -2730 self.crypto_state.accepted_0rtt -2731 } -2732 -2733 /// Whether 0-RTT is/was possible during the handshake -2734 pub fn has_0rtt(&self) -> bool { -2735 self.crypto_state.zero_rtt_enabled +2710 /// For an established connection this essentially means the handshake is **completed**, +2711 /// but not necessarily yet confirmed. +2712 pub fn is_handshaking(&self) -> bool { +2713 self.state.is_handshake() +2714 } +2715 +2716 /// Whether the connection is closed +2717 /// +2718 /// Closed connections cannot transport any further data. A connection becomes closed when +2719 /// either peer application intentionally closes it, or when either transport layer detects an +2720 /// error such as a time-out or certificate validation failure. +2721 /// +2722 /// A [`ConnectionLost`](Event::ConnectionLost) event is emitted with details when the +2723 /// connection is closed by the peer or due to an error. When the local application closes +2724 /// the connection via [`close()`](Self::close), no `ConnectionLost` event is emitted; +2725 /// instead, pending operations fail with [`ConnectionError::LocallyClosed`]. +2726 pub fn is_closed(&self) -> bool { +2727 self.state.is_closed() +2728 } +2729 +2730 /// Whether there is no longer any need to keep the connection around +2731 /// +2732 /// Closed connections become drained after a brief timeout to absorb any remaining in-flight +2733 /// packets from the peer. All drained connections have been closed. +2734 pub fn is_drained(&self) -> bool { +2735 self.state.is_drained() 2736 } 2737 -2738 /// Whether there are any pending retransmits -2739 pub fn has_pending_retransmits(&self) -> bool { -2740 !self.spaces[SpaceId::Data].pending.is_empty(&self.streams) -2741 } -2742 -2743 /// Look up whether we're the client or server of this Connection -2744 pub fn side(&self) -> Side { -2745 self.side.side() -2746 } -2747 -2748 /// Get the address observed by the remote over the given path -2749 pub fn path_observed_address(&self, path_id: PathId) -> Result<Option<SocketAddr>, ClosedPath> { -2750 self.path(path_id) -2751 .map(|path_data| { -2752 path_data -2753 .last_observed_addr_report -2754 .as_ref() -2755 .map(|observed| observed.socket_addr()) -2756 }) -2757 .ok_or(ClosedPath { _private: () }) +2738 /// For clients, if the peer accepted the 0-RTT data packets +2739 /// +2740 /// The value is meaningless until after the handshake completes. +2741 pub fn accepted_0rtt(&self) -> bool { +2742 self.crypto_state.accepted_0rtt +2743 } +2744 +2745 /// Whether 0-RTT is/was possible during the handshake +2746 pub fn has_0rtt(&self) -> bool { +2747 self.crypto_state.zero_rtt_enabled +2748 } +2749 +2750 /// Whether there are any pending retransmits +2751 pub fn has_pending_retransmits(&self) -> bool { +2752 !self.spaces[SpaceId::Data].pending.is_empty(&self.streams) +2753 } +2754 +2755 /// Look up whether we're the client or server of this Connection +2756 pub fn side(&self) -> Side { +2757 self.side.side() 2758 } 2759 -2760 /// Current best estimate of this connection's latency (round-trip-time) -2761 pub fn rtt(&self, path_id: PathId) -> Option<Duration> { -2762 self.path(path_id).map(|d| d.rtt.get()) -2763 } -2764 -2765 /// Current state of this connection's congestion controller, for debugging purposes -2766 pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller> { -2767 self.path(path_id).map(|d| d.congestion.as_ref()) -2768 } -2769 -2770 /// Modify the number of remotely initiated streams that may be concurrently open -2771 /// -2772 /// No streams may be opened by the peer unless fewer than `count` are already open. Large -2773 /// `count`s increase both minimum and worst-case memory consumption. -2774 pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt) { -2775 self.streams.set_max_concurrent(dir, count); -2776 // If the limit was reduced, then a flow control update previously deemed insignificant may -2777 // now be significant. -2778 let pending = &mut self.spaces[SpaceId::Data].pending; -2779 self.streams.queue_max_stream_id(pending); +2760 /// Get the address observed by the remote over the given path +2761 pub fn path_observed_address(&self, path_id: PathId) -> Result<Option<SocketAddr>, ClosedPath> { +2762 self.path(path_id) +2763 .map(|path_data| { +2764 path_data +2765 .last_observed_addr_report +2766 .as_ref() +2767 .map(|observed| observed.socket_addr()) +2768 }) +2769 .ok_or(ClosedPath { _private: () }) +2770 } +2771 +2772 /// Current best estimate of this connection's latency (round-trip-time) +2773 pub fn rtt(&self, path_id: PathId) -> Option<Duration> { +2774 self.path(path_id).map(|d| d.rtt.get()) +2775 } +2776 +2777 /// Current state of this connection's congestion controller, for debugging purposes +2778 pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller> { +2779 self.path(path_id).map(|d| d.congestion.as_ref()) 2780 } 2781 -2782 /// Modify the number of open paths allowed when multipath is enabled +2782 /// Modify the number of remotely initiated streams that may be concurrently open 2783 /// -2784 /// When reducing the number of concurrent paths this will only affect delaying sending -2785 /// new MAX_PATH_ID frames until fewer than this number of paths are possible. To -2786 /// actively reduce paths they must be closed using [`Connection::close_path`], which -2787 /// can also be used to close not-yet-opened paths. -2788 /// -2789 /// If multipath is not negotiated (see the [`TransportConfig`]) this can not enable -2790 /// multipath and will fail. -2791 pub fn set_max_concurrent_paths( -2792 &mut self, -2793 now: Instant, -2794 count: NonZeroU32, -2795 ) -> Result<(), MultipathNotNegotiated> { -2796 if !self.is_multipath_negotiated() { -2797 return Err(MultipathNotNegotiated { _private: () }); -2798 } -2799 self.max_concurrent_paths = count; -2800 -2801 let in_use_count = self -2802 .local_max_path_id -2803 .next() -2804 .saturating_sub(self.abandoned_paths.len()) -2805 .as_u32(); -2806 let extra_needed = count.get().saturating_sub(in_use_count); -2807 let new_max_path_id = self.local_max_path_id.saturating_add(extra_needed); -2808 -2809 self.set_max_path_id(now, new_max_path_id); -2810 -2811 Ok(()) -2812 } -2813 -2814 /// If needed, issues a new MAX_PATH_ID frame and new CIDs for any newly allowed paths -2815 fn set_max_path_id(&mut self, now: Instant, max_path_id: PathId) { -2816 if max_path_id <= self.local_max_path_id { -2817 return; -2818 } -2819 -2820 self.local_max_path_id = max_path_id; -2821 self.spaces[SpaceId::Data].pending.max_path_id = true; +2784 /// No streams may be opened by the peer unless fewer than `count` are already open. Large +2785 /// `count`s increase both minimum and worst-case memory consumption. +2786 pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt) { +2787 self.streams.set_max_concurrent(dir, count); +2788 // If the limit was reduced, then a flow control update previously deemed insignificant may +2789 // now be significant. +2790 let pending = &mut self.spaces[SpaceId::Data].pending; +2791 self.streams.queue_max_stream_id(pending); +2792 } +2793 +2794 /// Modify the number of open paths allowed when multipath is enabled +2795 /// +2796 /// When reducing the number of concurrent paths this will only affect delaying sending +2797 /// new MAX_PATH_ID frames until fewer than this number of paths are possible. To +2798 /// actively reduce paths they must be closed using [`Connection::close_path`], which +2799 /// can also be used to close not-yet-opened paths. +2800 /// +2801 /// If multipath is not negotiated (see the [`TransportConfig`]) this can not enable +2802 /// multipath and will fail. +2803 pub fn set_max_concurrent_paths( +2804 &mut self, +2805 now: Instant, +2806 count: NonZeroU32, +2807 ) -> Result<(), MultipathNotNegotiated> { +2808 if !self.is_multipath_negotiated() { +2809 return Err(MultipathNotNegotiated { _private: () }); +2810 } +2811 self.max_concurrent_paths = count; +2812 +2813 let in_use_count = self +2814 .local_max_path_id +2815 .next() +2816 .saturating_sub(self.abandoned_paths.len()) +2817 .as_u32(); +2818 let extra_needed = count.get().saturating_sub(in_use_count); +2819 let new_max_path_id = self.local_max_path_id.saturating_add(extra_needed); +2820 +2821 self.set_max_path_id(now, new_max_path_id); 2822 -2823 self.issue_first_path_cids(now); +2823 Ok(()) 2824 } 2825 -2826 /// Current number of remotely initiated streams that may be concurrently open -2827 /// -2828 /// If the target for this limit is reduced using [`set_max_concurrent_streams`](Self::set_max_concurrent_streams), -2829 /// it will not change immediately, even if fewer streams are open. Instead, it will -2830 /// decrement by one for each time a remotely initiated stream of matching directionality is closed. -2831 pub fn max_concurrent_streams(&self, dir: Dir) -> u64 { -2832 self.streams.max_concurrent(dir) -2833 } +2826 /// If needed, issues a new MAX_PATH_ID frame and new CIDs for any newly allowed paths +2827 fn set_max_path_id(&mut self, now: Instant, max_path_id: PathId) { +2828 if max_path_id <= self.local_max_path_id { +2829 return; +2830 } +2831 +2832 self.local_max_path_id = max_path_id; +2833 self.spaces[SpaceId::Data].pending.max_path_id = true; 2834 -2835 /// See [`TransportConfig::send_window()`] -2836 pub fn set_send_window(&mut self, send_window: u64) { -2837 self.streams.set_send_window(send_window); -2838 } -2839 -2840 /// See [`TransportConfig::receive_window()`] -2841 pub fn set_receive_window(&mut self, receive_window: VarInt) { -2842 if self.streams.set_receive_window(receive_window) { -2843 self.spaces[SpaceId::Data].pending.max_data = true; -2844 } +2835 self.issue_first_path_cids(now); +2836 } +2837 +2838 /// Current number of remotely initiated streams that may be concurrently open +2839 /// +2840 /// If the target for this limit is reduced using [`set_max_concurrent_streams`](Self::set_max_concurrent_streams), +2841 /// it will not change immediately, even if fewer streams are open. Instead, it will +2842 /// decrement by one for each time a remotely initiated stream of matching directionality is closed. +2843 pub fn max_concurrent_streams(&self, dir: Dir) -> u64 { +2844 self.streams.max_concurrent(dir) 2845 } 2846 -2847 /// Whether the Multipath for QUIC extension is enabled. -2848 /// -2849 /// Multipath is only enabled after the handshake is completed and if it was enabled by both -2850 /// peers. -2851 pub fn is_multipath_negotiated(&self) -> bool { -2852 !self.is_handshaking() -2853 && self.config.max_concurrent_multipath_paths.is_some() -2854 && self.peer_params.initial_max_path_id.is_some() -2855 } -2856 -2857 fn on_ack_received( -2858 &mut self, -2859 now: Instant, -2860 space: SpaceId, -2861 ack: frame::Ack, -2862 ) -> Result<(), TransportError> { -2863 // All ACKs are referencing path 0 -2864 let path = PathId::ZERO; -2865 self.inner_on_ack_received(now, space, path, ack) -2866 } -2867 -2868 fn on_path_ack_received( -2869 &mut self, -2870 now: Instant, -2871 space: SpaceId, -2872 path_ack: frame::PathAck, -2873 ) -> Result<(), TransportError> { -2874 let (ack, path) = path_ack.into_ack(); -2875 self.inner_on_ack_received(now, space, path, ack) -2876 } -2877 -2878 /// Handles an ACK frame acknowledging packets sent on *path*. -2879 fn inner_on_ack_received( -2880 &mut self, -2881 now: Instant, -2882 space: SpaceId, -2883 path: PathId, -2884 ack: frame::Ack, +2847 /// See [`TransportConfig::send_window()`] +2848 pub fn set_send_window(&mut self, send_window: u64) { +2849 self.streams.set_send_window(send_window); +2850 } +2851 +2852 /// See [`TransportConfig::receive_window()`] +2853 pub fn set_receive_window(&mut self, receive_window: VarInt) { +2854 if self.streams.set_receive_window(receive_window) { +2855 self.spaces[SpaceId::Data].pending.max_data = true; +2856 } +2857 } +2858 +2859 /// Whether the Multipath for QUIC extension is enabled. +2860 /// +2861 /// Multipath is only enabled after the handshake is completed and if it was enabled by both +2862 /// peers. +2863 pub fn is_multipath_negotiated(&self) -> bool { +2864 !self.is_handshaking() +2865 && self.config.max_concurrent_multipath_paths.is_some() +2866 && self.peer_params.initial_max_path_id.is_some() +2867 } +2868 +2869 fn on_ack_received( +2870 &mut self, +2871 now: Instant, +2872 space: SpaceId, +2873 ack: frame::Ack, +2874 ) -> Result<(), TransportError> { +2875 // All ACKs are referencing path 0 +2876 let path = PathId::ZERO; +2877 self.inner_on_ack_received(now, space, path, ack) +2878 } +2879 +2880 fn on_path_ack_received( +2881 &mut self, +2882 now: Instant, +2883 space: SpaceId, +2884 path_ack: frame::PathAck, 2885 ) -> Result<(), TransportError> { -2886 if !self.spaces[space].number_spaces.contains_key(&path) { -2887 if self.abandoned_paths.contains(&path) { -2888 // See also -2889 // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-21.html#section-3.4.3-3 -2890 // > When an endpoint finally deletes all state associated with the path [...] -2891 // > PATH_ACK frames received with an abandoned path ID are silently ignored, -2892 // > as specified in Section 4. -2893 trace!("silently ignoring PATH_ACK on discarded path"); -2894 return Ok(()); -2895 } else { -2896 return Err(TransportError::PROTOCOL_VIOLATION( -2897 "received PATH_ACK with path ID never used", -2898 )); -2899 } -2900 } -2901 if ack.largest >= self.spaces[space].for_path(path).next_packet_number { -2902 return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked")); -2903 } -2904 // `Some(pn)` if this ACK raised `largest_acked_packet_pn`. -2905 let new_largest_pn = { -2906 let space = &mut self.spaces[space].for_path(path); -2907 if space -2908 .largest_acked_packet_pn -2909 .is_none_or(|pn| ack.largest > pn) -2910 { -2911 space.largest_acked_packet_pn = Some(ack.largest); -2912 if let Some(info) = space.sent_packets.get(ack.largest) { -2913 // This should always succeed, but a misbehaving peer might ACK a packet we -2914 // haven't sent. At worst, that will result in us spuriously reducing the -2915 // congestion window. -2916 space.largest_acked_packet_send_time = info.time_sent; -2917 } -2918 Some(ack.largest) -2919 } else { -2920 None -2921 } -2922 }; -2923 -2924 if self.detect_spurious_loss(&ack, space, path) { -2925 self.path_stats.for_path(path).spurious_congestion_events += 1; -2926 self.path_data_mut(path) -2927 .congestion -2928 .on_spurious_congestion_event(); -2929 } -2930 -2931 // Avoid DoS from unreasonably huge ack ranges by filtering out just the new acks. -2932 let mut newly_acked: ArrayRangeSet = ArrayRangeSet::new(); -2933 for range in ack.iter() { -2934 self.spaces[space].for_path(path).check_ack(range.clone())?; -2935 for (pn, _) in self.spaces[space] -2936 .for_path(path) -2937 .sent_packets -2938 .iter_range(range) -2939 { -2940 newly_acked.insert_one(pn); -2941 } -2942 } -2943 -2944 if newly_acked.is_empty() { -2945 return Ok(()); -2946 } -2947 -2948 let mut ack_eliciting_acked = false; -2949 for packet in newly_acked.elts() { -2950 if let Some(info) = self.spaces[space].for_path(path).take(packet) { -2951 for (acked_path_id, acked_pn) in info.largest_acked.iter() { -2952 // Assume ACKs for all packets below the largest acknowledged in -2953 // `packet` have been received. This can cause the peer to spuriously -2954 // retransmit if some of our earlier ACKs were lost, but allows for -2955 // simpler state tracking. See discussion at -2956 // https://www.rfc-editor.org/rfc/rfc9000.html#name-limiting-ranges-by-tracking -2957 if let Some(pns) = self.spaces[space].path_space_mut(*acked_path_id) { -2958 pns.pending_acks.subtract_below(*acked_pn); -2959 } -2960 } -2961 ack_eliciting_acked |= info.ack_eliciting; -2962 -2963 // Notify MTU discovery that a packet was acked, because it might be an MTU probe -2964 let path_data = self.path_data_mut(path); -2965 let mtu_updated = path_data.mtud.on_acked(space.kind(), packet, info.size); -2966 if mtu_updated { -2967 path_data -2968 .congestion -2969 .on_mtu_update(path_data.mtud.current_mtu()); -2970 } -2971 -2972 // Notify ack frequency that a packet was acked, because it might contain an ACK_FREQUENCY frame -2973 self.ack_frequency.on_acked(path, packet); +2886 let (ack, path) = path_ack.into_ack(); +2887 self.inner_on_ack_received(now, space, path, ack) +2888 } +2889 +2890 /// Handles an ACK frame acknowledging packets sent on *path*. +2891 fn inner_on_ack_received( +2892 &mut self, +2893 now: Instant, +2894 space: SpaceId, +2895 path: PathId, +2896 ack: frame::Ack, +2897 ) -> Result<(), TransportError> { +2898 if !self.spaces[space].number_spaces.contains_key(&path) { +2899 if self.abandoned_paths.contains(&path) { +2900 // See also +2901 // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-21.html#section-3.4.3-3 +2902 // > When an endpoint finally deletes all state associated with the path [...] +2903 // > PATH_ACK frames received with an abandoned path ID are silently ignored, +2904 // > as specified in Section 4. +2905 trace!("silently ignoring PATH_ACK on discarded path"); +2906 return Ok(()); +2907 } else { +2908 return Err(TransportError::PROTOCOL_VIOLATION( +2909 "received PATH_ACK with path ID never used", +2910 )); +2911 } +2912 } +2913 if ack.largest >= self.spaces[space].for_path(path).next_packet_number { +2914 return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked")); +2915 } +2916 // `Some(pn)` if this ACK raised `largest_acked_packet_pn`. +2917 let new_largest_pn = { +2918 let space = &mut self.spaces[space].for_path(path); +2919 if space +2920 .largest_acked_packet_pn +2921 .is_none_or(|pn| ack.largest > pn) +2922 { +2923 space.largest_acked_packet_pn = Some(ack.largest); +2924 if let Some(info) = space.sent_packets.get(ack.largest) { +2925 // This should always succeed, but a misbehaving peer might ACK a packet we +2926 // haven't sent. At worst, that will result in us spuriously reducing the +2927 // congestion window. +2928 space.largest_acked_packet_send_time = info.time_sent; +2929 } +2930 Some(ack.largest) +2931 } else { +2932 None +2933 } +2934 }; +2935 +2936 if self.detect_spurious_loss(&ack, space, path) { +2937 self.path_stats.for_path(path).spurious_congestion_events += 1; +2938 self.path_data_mut(path) +2939 .congestion +2940 .on_spurious_congestion_event(); +2941 } +2942 +2943 // Avoid DoS from unreasonably huge ack ranges by filtering out just the new acks. +2944 let mut newly_acked: ArrayRangeSet = ArrayRangeSet::new(); +2945 for range in ack.iter() { +2946 self.spaces[space].for_path(path).check_ack(range.clone())?; +2947 for (pn, _) in self.spaces[space] +2948 .for_path(path) +2949 .sent_packets +2950 .iter_range(range) +2951 { +2952 newly_acked.insert_one(pn); +2953 } +2954 } +2955 +2956 if newly_acked.is_empty() { +2957 return Ok(()); +2958 } +2959 +2960 let mut ack_eliciting_acked = false; +2961 for packet in newly_acked.elts() { +2962 if let Some(info) = self.spaces[space].for_path(path).take(packet) { +2963 for (acked_path_id, acked_pn) in info.largest_acked.iter() { +2964 // Assume ACKs for all packets below the largest acknowledged in +2965 // `packet` have been received. This can cause the peer to spuriously +2966 // retransmit if some of our earlier ACKs were lost, but allows for +2967 // simpler state tracking. See discussion at +2968 // https://www.rfc-editor.org/rfc/rfc9000.html#name-limiting-ranges-by-tracking +2969 if let Some(pns) = self.spaces[space].path_space_mut(*acked_path_id) { +2970 pns.pending_acks.subtract_below(*acked_pn); +2971 } +2972 } +2973 ack_eliciting_acked |= info.ack_eliciting; 2974 -2975 self.on_packet_acked(now, path, packet, info); -2976 } -2977 } -2978 -2979 let largest_ackd = self.spaces[space].for_path(path).largest_acked_packet_pn; -2980 let path_data = self.path_data_mut(path); -2981 let app_limited = path_data.app_limited; -2982 let in_flight = path_data.in_flight.bytes; +2975 // Notify MTU discovery that a packet was acked, because it might be an MTU probe +2976 let path_data = self.path_data_mut(path); +2977 let mtu_updated = path_data.mtud.on_acked(space.kind(), packet, info.size); +2978 if mtu_updated { +2979 path_data +2980 .congestion +2981 .on_mtu_update(path_data.mtud.current_mtu()); +2982 } 2983 -2984 path_data -2985 .congestion -2986 .on_end_acks(now, in_flight, app_limited, largest_ackd); -2987 -2988 if new_largest_pn.is_some() && ack_eliciting_acked { -2989 let ack_delay = if space != SpaceId::Data { -2990 Duration::from_micros(0) -2991 } else { -2992 cmp::min( -2993 self.ack_frequency.peer_max_ack_delay, -2994 Duration::from_micros(ack.delay << self.peer_params.ack_delay_exponent.0), -2995 ) -2996 }; -2997 let rtt = now.saturating_duration_since( -2998 self.spaces[space] -2999 .for_path(path) -3000 .largest_acked_packet_send_time, -3001 ); -3002 -3003 let next_pn = self.spaces[space].for_path(path).next_packet_number; -3004 let path_data = self.path_data_mut(path); -3005 // TODO(@divma): should be a method of path, should be contained in a single place -3006 path_data.rtt.update(ack_delay, rtt); -3007 if path_data.first_packet_after_rtt_sample.is_none() { -3008 path_data.first_packet_after_rtt_sample = Some((space.kind(), next_pn)); -3009 } -3010 } -3011 -3012 // Must be called before crypto/pto_count are clobbered -3013 self.detect_lost_packets(now, space, path, true); +2984 // Notify ack frequency that a packet was acked, because it might contain an ACK_FREQUENCY frame +2985 self.ack_frequency.on_acked(path, packet); +2986 +2987 self.on_packet_acked(now, path, packet, info); +2988 } +2989 } +2990 +2991 let largest_ackd = self.spaces[space].for_path(path).largest_acked_packet_pn; +2992 let path_data = self.path_data_mut(path); +2993 let app_limited = path_data.app_limited; +2994 let in_flight = path_data.in_flight.bytes; +2995 +2996 path_data +2997 .congestion +2998 .on_end_acks(now, in_flight, app_limited, largest_ackd); +2999 +3000 if new_largest_pn.is_some() && ack_eliciting_acked { +3001 let ack_delay = if space != SpaceId::Data { +3002 Duration::from_micros(0) +3003 } else { +3004 cmp::min( +3005 self.ack_frequency.peer_max_ack_delay, +3006 Duration::from_micros(ack.delay << self.peer_params.ack_delay_exponent.0), +3007 ) +3008 }; +3009 let rtt = now.saturating_duration_since( +3010 self.spaces[space] +3011 .for_path(path) +3012 .largest_acked_packet_send_time, +3013 ); 3014 -3015 // If the peer did not complete the handshake address validation the ACK could be -3016 // spoofed, e.g. in the Initial space. Setting the pto_count back to 0 removes the -3017 // exponential backoff from the PTO timer and would result in too many tail-loss -3018 // probes being sent. -3019 if self.peer_completed_handshake_address_validation() { -3020 self.path_data_mut(path).pto_count = 0; -3021 } -3022 -3023 // Explicit congestion notification -3024 // TODO(@divma): this code is a good example of logic that should be contained in a single -3025 // place but it's split between the path data and the packet number space data, we should -3026 // find a way to make this work without two lookups -3027 if self.path_data(path).sending_ecn { -3028 if let Some(ecn) = ack.ecn { -3029 // We only examine ECN counters from ACKs that we are certain we received in transmit -3030 // order, allowing us to compute an increase in ECN counts to compare against the number -3031 // of newly acked packets that remains well-defined in the presence of arbitrary packet -3032 // reordering. -3033 if let Some(largest_sent_pn) = new_largest_pn { -3034 let sent = self.spaces[space] -3035 .for_path(path) -3036 .largest_acked_packet_send_time; -3037 self.process_ecn( -3038 now, -3039 space, -3040 path, -3041 newly_acked.range_count() as u64, -3042 ecn, -3043 sent, -3044 largest_sent_pn, -3045 ); -3046 } -3047 } else { -3048 // We always start out sending ECN, so any ack that doesn't acknowledge it disables it. -3049 debug!("ECN not acknowledged by peer"); -3050 self.path_data_mut(path).sending_ecn = false; -3051 } -3052 } -3053 -3054 self.set_loss_detection_timer(now, path); -3055 Ok(()) -3056 } -3057 -3058 fn detect_spurious_loss(&mut self, ack: &frame::Ack, space: SpaceId, path: PathId) -> bool { -3059 let lost_packets = &mut self.spaces[space].for_path(path).lost_packets; -3060 -3061 if lost_packets.is_empty() { -3062 return false; -3063 } -3064 -3065 for range in ack.iter() { -3066 let spurious_losses: Vec<u64> = lost_packets -3067 .iter_range(range.clone()) -3068 .map(|(pn, _info)| pn) -3069 .collect(); -3070 -3071 for pn in spurious_losses { -3072 lost_packets.remove(pn); -3073 } -3074 } -3075 -3076 // If this ACK frame acknowledged all deemed lost packets, -3077 // then we have raised a spurious congestion event in the past. -3078 // We cannot conclude when there are remaining packets, -3079 // but future ACK frames might indicate a spurious loss detection. -3080 lost_packets.is_empty() -3081 } +3015 let next_pn = self.spaces[space].for_path(path).next_packet_number; +3016 let path_data = self.path_data_mut(path); +3017 // TODO(@divma): should be a method of path, should be contained in a single place +3018 path_data.rtt.update(ack_delay, rtt); +3019 if path_data.first_packet_after_rtt_sample.is_none() { +3020 path_data.first_packet_after_rtt_sample = Some((space.kind(), next_pn)); +3021 } +3022 } +3023 +3024 // Must be called before crypto/pto_count are clobbered +3025 self.detect_lost_packets(now, space, path, true); +3026 +3027 // If the peer did not complete the handshake address validation the ACK could be +3028 // spoofed, e.g. in the Initial space. Setting the pto_count back to 0 removes the +3029 // exponential backoff from the PTO timer and would result in too many tail-loss +3030 // probes being sent. +3031 if self.peer_completed_handshake_address_validation() { +3032 self.path_data_mut(path).pto_count = 0; +3033 } +3034 +3035 // Explicit congestion notification +3036 // TODO(@divma): this code is a good example of logic that should be contained in a single +3037 // place but it's split between the path data and the packet number space data, we should +3038 // find a way to make this work without two lookups +3039 if self.path_data(path).sending_ecn { +3040 if let Some(ecn) = ack.ecn { +3041 // We only examine ECN counters from ACKs that we are certain we received in transmit +3042 // order, allowing us to compute an increase in ECN counts to compare against the number +3043 // of newly acked packets that remains well-defined in the presence of arbitrary packet +3044 // reordering. +3045 if let Some(largest_sent_pn) = new_largest_pn { +3046 let sent = self.spaces[space] +3047 .for_path(path) +3048 .largest_acked_packet_send_time; +3049 self.process_ecn( +3050 now, +3051 space, +3052 path, +3053 newly_acked.range_count() as u64, +3054 ecn, +3055 sent, +3056 largest_sent_pn, +3057 ); +3058 } +3059 } else { +3060 // We always start out sending ECN, so any ack that doesn't acknowledge it disables it. +3061 debug!("ECN not acknowledged by peer"); +3062 self.path_data_mut(path).sending_ecn = false; +3063 } +3064 } +3065 +3066 self.set_loss_detection_timer(now, path); +3067 Ok(()) +3068 } +3069 +3070 fn detect_spurious_loss(&mut self, ack: &frame::Ack, space: SpaceId, path: PathId) -> bool { +3071 let lost_packets = &mut self.spaces[space].for_path(path).lost_packets; +3072 +3073 if lost_packets.is_empty() { +3074 return false; +3075 } +3076 +3077 for range in ack.iter() { +3078 let spurious_losses: Vec<u64> = lost_packets +3079 .iter_range(range.clone()) +3080 .map(|(pn, _info)| pn) +3081 .collect(); 3082 -3083 /// Drain lost packets that we reasonably think will never arrive -3084 /// -3085 /// The current criterion is copied from `msquic`: -3086 /// discard packets that were sent earlier than 2 probe timeouts ago. -3087 fn drain_lost_packets(&mut self, now: Instant, space: SpaceId, path: PathId) { -3088 let two_pto = 2 * self.path_data(path).rtt.pto_base(); -3089 -3090 let lost_packets = &mut self.spaces[space].for_path(path).lost_packets; -3091 lost_packets.retain(|_pn, info| now.saturating_duration_since(info.time_sent) <= two_pto); -3092 } -3093 -3094 /// Process a new ECN block from an in-order ACK -3095 fn process_ecn( -3096 &mut self, -3097 now: Instant, -3098 space: SpaceId, -3099 path: PathId, -3100 newly_acked_pn: u64, -3101 ecn: frame::EcnCounts, -3102 largest_sent_time: Instant, -3103 largest_sent_pn: u64, -3104 ) { -3105 match self.spaces[space] -3106 .for_path(path) -3107 .detect_ecn(newly_acked_pn, ecn) -3108 { -3109 Err(e) => { -3110 debug!("halting ECN due to verification failure: {}", e); -3111 -3112 self.path_data_mut(path).sending_ecn = false; -3113 // Wipe out the existing value because it might be garbage and could interfere with -3114 // future attempts to use ECN on new paths. -3115 self.spaces[space].for_path(path).ecn_feedback = frame::EcnCounts::ZERO; -3116 } -3117 Ok(false) => {} -3118 Ok(true) => { -3119 self.path_stats.for_path(path).congestion_events += 1; -3120 self.path_data_mut(path).congestion.on_congestion_event( -3121 now, -3122 largest_sent_time, -3123 false, -3124 true, -3125 0, -3126 largest_sent_pn, -3127 ); +3083 for pn in spurious_losses { +3084 lost_packets.remove(pn); +3085 } +3086 } +3087 +3088 // If this ACK frame acknowledged all deemed lost packets, +3089 // then we have raised a spurious congestion event in the past. +3090 // We cannot conclude when there are remaining packets, +3091 // but future ACK frames might indicate a spurious loss detection. +3092 lost_packets.is_empty() +3093 } +3094 +3095 /// Drain lost packets that we reasonably think will never arrive +3096 /// +3097 /// The current criterion is copied from `msquic`: +3098 /// discard packets that were sent earlier than 2 probe timeouts ago. +3099 fn drain_lost_packets(&mut self, now: Instant, space: SpaceId, path: PathId) { +3100 let two_pto = 2 * self.path_data(path).rtt.pto_base(); +3101 +3102 let lost_packets = &mut self.spaces[space].for_path(path).lost_packets; +3103 lost_packets.retain(|_pn, info| now.saturating_duration_since(info.time_sent) <= two_pto); +3104 } +3105 +3106 /// Process a new ECN block from an in-order ACK +3107 fn process_ecn( +3108 &mut self, +3109 now: Instant, +3110 space: SpaceId, +3111 path: PathId, +3112 newly_acked_pn: u64, +3113 ecn: frame::EcnCounts, +3114 largest_sent_time: Instant, +3115 largest_sent_pn: u64, +3116 ) { +3117 match self.spaces[space] +3118 .for_path(path) +3119 .detect_ecn(newly_acked_pn, ecn) +3120 { +3121 Err(e) => { +3122 debug!("halting ECN due to verification failure: {}", e); +3123 +3124 self.path_data_mut(path).sending_ecn = false; +3125 // Wipe out the existing value because it might be garbage and could interfere with +3126 // future attempts to use ECN on new paths. +3127 self.spaces[space].for_path(path).ecn_feedback = frame::EcnCounts::ZERO; 3128 } -3129 } -3130 } -3131 -3132 // Not timing-aware, so it's safe to call this for inferred acks, such as arise from -3133 // high-latency handshakes -3134 fn on_packet_acked(&mut self, now: Instant, path_id: PathId, pn: u64, info: SentPacket) { -3135 let path = self.path_data_mut(path_id); -3136 let app_limited = path.app_limited; -3137 path.remove_in_flight(&info); -3138 if info.ack_eliciting && info.path_generation == path.generation() { -3139 // Only pass ACKs to the congestion controller if it belongs to this exact -3140 // generation of the path. Otherwise we might be feeding ACKs from the previous -3141 // 4-tuple into our congestion controller. -3142 let rtt = path.rtt; -3143 path.congestion -3144 .on_ack(now, info.time_sent, info.size.into(), pn, app_limited, &rtt); -3145 } -3146 -3147 // Update state for confirmed delivery of frames -3148 if let Some(retransmits) = info.retransmits.get() { -3149 for (id, _) in retransmits.reset_stream.iter() { -3150 self.streams.reset_acked(*id); -3151 } -3152 } -3153 -3154 for frame in info.stream_frames { -3155 self.streams.received_ack_of(frame); -3156 } -3157 } +3129 Ok(false) => {} +3130 Ok(true) => { +3131 self.path_stats.for_path(path).congestion_events += 1; +3132 self.path_data_mut(path).congestion.on_congestion_event( +3133 now, +3134 largest_sent_time, +3135 false, +3136 true, +3137 0, +3138 largest_sent_pn, +3139 ); +3140 } +3141 } +3142 } +3143 +3144 // Not timing-aware, so it's safe to call this for inferred acks, such as arise from +3145 // high-latency handshakes +3146 fn on_packet_acked(&mut self, now: Instant, path_id: PathId, pn: u64, info: SentPacket) { +3147 let path = self.path_data_mut(path_id); +3148 let app_limited = path.app_limited; +3149 path.remove_in_flight(&info); +3150 if info.ack_eliciting && info.path_generation == path.generation() { +3151 // Only pass ACKs to the congestion controller if it belongs to this exact +3152 // generation of the path. Otherwise we might be feeding ACKs from the previous +3153 // 4-tuple into our congestion controller. +3154 let rtt = path.rtt; +3155 path.congestion +3156 .on_ack(now, info.time_sent, info.size.into(), pn, app_limited, &rtt); +3157 } 3158 -3159 fn set_key_discard_timer(&mut self, now: Instant, space: SpaceKind) { -3160 let start = if self.crypto_state.has_keys(EncryptionLevel::ZeroRtt) { -3161 now -3162 } else { -3163 self.crypto_state -3164 .prev_crypto -3165 .as_ref() -3166 .expect("no previous keys") -3167 .end_packet -3168 .as_ref() -3169 .expect("update not acknowledged yet") -3170 .1 -3171 }; -3172 -3173 // QUIC-MULTIPATH § 2.5 Key Phase Update Process: use largest PTO of all paths. -3174 self.timers.set( -3175 Timer::Conn(ConnTimer::KeyDiscard), -3176 start + self.max_pto_for_space(space) * 3, -3177 self.qlog.with_time(now), -3178 ); -3179 } -3180 -3181 /// Handle a [`PathTimer::LossDetection`] timeout. -3182 /// -3183 /// This timer expires for two reasons: -3184 /// - An ACK-eliciting packet we sent should be considered lost. -3185 /// - The PTO may have expired and a tail-loss probe needs to be scheduled. -3186 /// -3187 /// The former needs us to schedule re-transmission of the lost data. -3188 /// -3189 /// The latter means we have not received an ACK for an ack-eliciting packet we sent -3190 /// within the PTO time-window. We need to schedule a tail-loss probe, an ack-eliciting -3191 /// packet, to try and elicit new acknowledgements. These new acknowledgements will -3192 /// indicate whether the previously sent packets were lost or not. -3193 fn on_loss_detection_timeout(&mut self, now: Instant, path_id: PathId) { -3194 if let Some((_, pn_space)) = self.loss_time_and_space(path_id) { -3195 // Time threshold loss Detection -3196 self.detect_lost_packets(now, pn_space, path_id, false); -3197 self.set_loss_detection_timer(now, path_id); -3198 return; -3199 } -3200 -3201 let Some((_, space)) = self.pto_time_and_space(now, path_id) else { -3202 error!(%path_id, "PTO expired while unset"); -3203 return; -3204 }; -3205 trace!( -3206 in_flight = self.path_data(path_id).in_flight.bytes, -3207 count = self.path_data(path_id).pto_count, -3208 ?space, -3209 %path_id, -3210 "PTO fired" -3211 ); +3159 // Update state for confirmed delivery of frames +3160 if let Some(retransmits) = info.retransmits.get() { +3161 for (id, _) in retransmits.reset_stream.iter() { +3162 self.streams.reset_acked(*id); +3163 } +3164 } +3165 +3166 for frame in info.stream_frames { +3167 self.streams.received_ack_of(frame); +3168 } +3169 } +3170 +3171 fn set_key_discard_timer(&mut self, now: Instant, space: SpaceKind) { +3172 let start = if self.crypto_state.has_keys(EncryptionLevel::ZeroRtt) { +3173 now +3174 } else { +3175 self.crypto_state +3176 .prev_crypto +3177 .as_ref() +3178 .expect("no previous keys") +3179 .end_packet +3180 .as_ref() +3181 .expect("update not acknowledged yet") +3182 .1 +3183 }; +3184 +3185 // QUIC-MULTIPATH § 2.5 Key Phase Update Process: use largest PTO of all paths. +3186 self.timers.set( +3187 Timer::Conn(ConnTimer::KeyDiscard), +3188 start + self.max_pto_for_space(space) * 3, +3189 self.qlog.with_time(now), +3190 ); +3191 } +3192 +3193 /// Handle a [`PathTimer::LossDetection`] timeout. +3194 /// +3195 /// This timer expires for two reasons: +3196 /// - An ACK-eliciting packet we sent should be considered lost. +3197 /// - The PTO may have expired and a tail-loss probe needs to be scheduled. +3198 /// +3199 /// The former needs us to schedule re-transmission of the lost data. +3200 /// +3201 /// The latter means we have not received an ACK for an ack-eliciting packet we sent +3202 /// within the PTO time-window. We need to schedule a tail-loss probe, an ack-eliciting +3203 /// packet, to try and elicit new acknowledgements. These new acknowledgements will +3204 /// indicate whether the previously sent packets were lost or not. +3205 fn on_loss_detection_timeout(&mut self, now: Instant, path_id: PathId) { +3206 if let Some((_, pn_space)) = self.loss_time_and_space(path_id) { +3207 // Time threshold loss Detection +3208 self.detect_lost_packets(now, pn_space, path_id, false); +3209 self.set_loss_detection_timer(now, path_id); +3210 return; +3211 } 3212 -3213 let count = match self.path_data(path_id).in_flight.ack_eliciting { -3214 // A PTO when we're not expecting any ACKs must be due to handshake -3215 // anti-amplification deadlock prevention. -3216 0 => { -3217 debug_assert!(!self.peer_completed_handshake_address_validation()); -3218 1 -3219 } -3220 // Conventional loss probe -3221 _ => 2, -3222 }; -3223 let pns = self.spaces[space].for_path(path_id); -3224 pns.loss_probes = pns.loss_probes.saturating_add(count); -3225 let path_data = self.path_data_mut(path_id); -3226 path_data.pto_count = path_data.pto_count.saturating_add(1); -3227 self.set_loss_detection_timer(now, path_id); -3228 } -3229 -3230 /// Detect any lost packets -3231 /// -3232 /// There are two cases in which we detects lost packets: -3233 /// -3234 /// - We received an ACK packet. -3235 /// - The [`PathTimer::LossDetection`] timer expired. So there is an un-acknowledged packet -3236 /// that was followed by an acknowledged packet. The loss timer for this -3237 /// un-acknowledged packet expired and we need to detect that packet as lost. -3238 /// -3239 /// Packets are lost if they are both (See RFC9002 §6.1): -3240 /// -3241 /// - Unacknowledged, in flight and sent prior to an acknowledged packet. -3242 /// - Old enough by either: -3243 /// - Having a packet number [`TransportConfig::packet_threshold`] lower then the last -3244 /// acknowledged packet. -3245 /// - Being sent [`TransportConfig::time_threshold`] * RTT in the past. -3246 fn detect_lost_packets( -3247 &mut self, -3248 now: Instant, -3249 pn_space: SpaceId, -3250 path_id: PathId, -3251 due_to_ack: bool, -3252 ) { -3253 let mut lost_packets = Vec::<u64>::new(); -3254 let mut lost_mtu_probe = None; -3255 let mut in_persistent_congestion = false; -3256 let mut size_of_lost_packets = 0u64; -3257 self.spaces[pn_space].for_path(path_id).loss_time = None; -3258 -3259 // Find all the lost packets, populating all variables initialised above. -3260 -3261 let path = self.path_data(path_id); -3262 let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe(); -3263 let loss_delay = path -3264 .rtt -3265 .conservative() -3266 .mul_f32(self.config.time_threshold) -3267 .max(TIMER_GRANULARITY); -3268 let first_packet_after_rtt_sample = path.first_packet_after_rtt_sample; -3269 -3270 let largest_acked_packet_pn = self.spaces[pn_space] -3271 .for_path(path_id) -3272 .largest_acked_packet_pn -3273 .expect("detect_lost_packets only to be called if path received at least one ACK"); -3274 let packet_threshold = self.config.packet_threshold as u64; -3275 -3276 // InPersistentCongestion: Determine if all packets in the time period before the newest -3277 // lost packet, including the edges, are marked lost. PTO computation must always -3278 // include max ACK delay, i.e. operate as if in Data space (see RFC9001 §7.6.1). -3279 let congestion_period = self -3280 .pto(SpaceKind::Data, path_id) -3281 .saturating_mul(self.config.persistent_congestion_threshold); -3282 let mut persistent_congestion_start: Option<Instant> = None; -3283 let mut prev_packet = None; -3284 let space = self.spaces[pn_space].for_path(path_id); -3285 -3286 for (packet, info) in space.sent_packets.iter_range(0..largest_acked_packet_pn) { -3287 if prev_packet != Some(packet.wrapping_sub(1)) { -3288 // An intervening packet was acknowledged -3289 persistent_congestion_start = None; -3290 } -3291 -3292 // Packets sent before now - loss_delay are deemed lost. -3293 // However, we avoid subtraction as it can panic and there's no -3294 // saturating equivalent of this subtraction operation with a Duration. -3295 let packet_too_old = now.saturating_duration_since(info.time_sent) >= loss_delay; -3296 if packet_too_old || largest_acked_packet_pn >= packet + packet_threshold { -3297 // The packet should be declared lost. -3298 if Some(packet) == in_flight_mtu_probe { -3299 // Lost MTU probes are not included in `lost_packets`, because they -3300 // should not trigger a congestion control response -3301 lost_mtu_probe = in_flight_mtu_probe; -3302 } else { -3303 lost_packets.push(packet); -3304 size_of_lost_packets += info.size as u64; -3305 if info.ack_eliciting && due_to_ack { -3306 match persistent_congestion_start { -3307 // Two ACK-eliciting packets lost more than -3308 // congestion_period apart, with no ACKed packets in between -3309 Some(start) if info.time_sent - start > congestion_period => { -3310 in_persistent_congestion = true; -3311 } -3312 // Persistent congestion must start after the first RTT sample -3313 None if first_packet_after_rtt_sample -3314 .is_some_and(|x| x < (pn_space.kind(), packet)) => -3315 { -3316 persistent_congestion_start = Some(info.time_sent); -3317 } -3318 _ => {} -3319 } -3320 } -3321 } -3322 } else { -3323 // The packet should not yet be declared lost. -3324 if space.loss_time.is_none() { -3325 // Since we iterate in order the lowest packet number's loss time will -3326 // always be the earliest. -3327 space.loss_time = Some(info.time_sent + loss_delay); -3328 } -3329 persistent_congestion_start = None; -3330 } -3331 -3332 prev_packet = Some(packet); -3333 } -3334 -3335 self.handle_lost_packets( -3336 pn_space, -3337 path_id, -3338 now, -3339 lost_packets, -3340 lost_mtu_probe, -3341 loss_delay, -3342 in_persistent_congestion, -3343 size_of_lost_packets, -3344 ); -3345 } +3213 let Some((_, space)) = self.pto_time_and_space(now, path_id) else { +3214 error!(%path_id, "PTO expired while unset"); +3215 return; +3216 }; +3217 trace!( +3218 in_flight = self.path_data(path_id).in_flight.bytes, +3219 count = self.path_data(path_id).pto_count, +3220 ?space, +3221 %path_id, +3222 "PTO fired" +3223 ); +3224 +3225 let count = match self.path_data(path_id).in_flight.ack_eliciting { +3226 // A PTO when we're not expecting any ACKs must be due to handshake +3227 // anti-amplification deadlock prevention. +3228 0 => { +3229 debug_assert!(!self.peer_completed_handshake_address_validation()); +3230 1 +3231 } +3232 // Conventional loss probe +3233 _ => 2, +3234 }; +3235 let pns = self.spaces[space].for_path(path_id); +3236 pns.loss_probes = pns.loss_probes.saturating_add(count); +3237 let path_data = self.path_data_mut(path_id); +3238 path_data.pto_count = path_data.pto_count.saturating_add(1); +3239 self.set_loss_detection_timer(now, path_id); +3240 } +3241 +3242 /// Detect any lost packets +3243 /// +3244 /// There are two cases in which we detects lost packets: +3245 /// +3246 /// - We received an ACK packet. +3247 /// - The [`PathTimer::LossDetection`] timer expired. So there is an un-acknowledged packet +3248 /// that was followed by an acknowledged packet. The loss timer for this +3249 /// un-acknowledged packet expired and we need to detect that packet as lost. +3250 /// +3251 /// Packets are lost if they are both (See RFC9002 §6.1): +3252 /// +3253 /// - Unacknowledged, in flight and sent prior to an acknowledged packet. +3254 /// - Old enough by either: +3255 /// - Having a packet number [`TransportConfig::packet_threshold`] lower then the last +3256 /// acknowledged packet. +3257 /// - Being sent [`TransportConfig::time_threshold`] * RTT in the past. +3258 fn detect_lost_packets( +3259 &mut self, +3260 now: Instant, +3261 pn_space: SpaceId, +3262 path_id: PathId, +3263 due_to_ack: bool, +3264 ) { +3265 let mut lost_packets = Vec::<u64>::new(); +3266 let mut lost_mtu_probe = None; +3267 let mut in_persistent_congestion = false; +3268 let mut size_of_lost_packets = 0u64; +3269 self.spaces[pn_space].for_path(path_id).loss_time = None; +3270 +3271 // Find all the lost packets, populating all variables initialised above. +3272 +3273 let path = self.path_data(path_id); +3274 let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe(); +3275 let loss_delay = path +3276 .rtt +3277 .conservative() +3278 .mul_f32(self.config.time_threshold) +3279 .max(TIMER_GRANULARITY); +3280 let first_packet_after_rtt_sample = path.first_packet_after_rtt_sample; +3281 +3282 let largest_acked_packet_pn = self.spaces[pn_space] +3283 .for_path(path_id) +3284 .largest_acked_packet_pn +3285 .expect("detect_lost_packets only to be called if path received at least one ACK"); +3286 let packet_threshold = self.config.packet_threshold as u64; +3287 +3288 // InPersistentCongestion: Determine if all packets in the time period before the newest +3289 // lost packet, including the edges, are marked lost. PTO computation must always +3290 // include max ACK delay, i.e. operate as if in Data space (see RFC9001 §7.6.1). +3291 let congestion_period = self +3292 .pto(SpaceKind::Data, path_id) +3293 .saturating_mul(self.config.persistent_congestion_threshold); +3294 let mut persistent_congestion_start: Option<Instant> = None; +3295 let mut prev_packet = None; +3296 let space = self.spaces[pn_space].for_path(path_id); +3297 +3298 for (packet, info) in space.sent_packets.iter_range(0..largest_acked_packet_pn) { +3299 if prev_packet != Some(packet.wrapping_sub(1)) { +3300 // An intervening packet was acknowledged +3301 persistent_congestion_start = None; +3302 } +3303 +3304 // Packets sent before now - loss_delay are deemed lost. +3305 // However, we avoid subtraction as it can panic and there's no +3306 // saturating equivalent of this subtraction operation with a Duration. +3307 let packet_too_old = now.saturating_duration_since(info.time_sent) >= loss_delay; +3308 if packet_too_old || largest_acked_packet_pn >= packet + packet_threshold { +3309 // The packet should be declared lost. +3310 if Some(packet) == in_flight_mtu_probe { +3311 // Lost MTU probes are not included in `lost_packets`, because they +3312 // should not trigger a congestion control response +3313 lost_mtu_probe = in_flight_mtu_probe; +3314 } else { +3315 lost_packets.push(packet); +3316 size_of_lost_packets += info.size as u64; +3317 if info.ack_eliciting && due_to_ack { +3318 match persistent_congestion_start { +3319 // Two ACK-eliciting packets lost more than +3320 // congestion_period apart, with no ACKed packets in between +3321 Some(start) if info.time_sent - start > congestion_period => { +3322 in_persistent_congestion = true; +3323 } +3324 // Persistent congestion must start after the first RTT sample +3325 None if first_packet_after_rtt_sample +3326 .is_some_and(|x| x < (pn_space.kind(), packet)) => +3327 { +3328 persistent_congestion_start = Some(info.time_sent); +3329 } +3330 _ => {} +3331 } +3332 } +3333 } +3334 } else { +3335 // The packet should not yet be declared lost. +3336 if space.loss_time.is_none() { +3337 // Since we iterate in order the lowest packet number's loss time will +3338 // always be the earliest. +3339 space.loss_time = Some(info.time_sent + loss_delay); +3340 } +3341 persistent_congestion_start = None; +3342 } +3343 +3344 prev_packet = Some(packet); +3345 } 3346 -3347 /// Drops the path state, declaring any remaining in-flight packets as lost -3348 fn discard_path(&mut self, path_id: PathId, now: Instant) { -3349 trace!(%path_id, "dropping path state"); -3350 let path = self.path_data(path_id); -3351 let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe(); -3352 -3353 let mut size_of_lost_packets = 0u64; // add to path_stats.lost_bytes; -3354 let lost_pns: Vec<_> = self.spaces[SpaceId::Data] -3355 .for_path(path_id) -3356 .sent_packets -3357 .iter() -3358 .filter(|(pn, _info)| Some(*pn) != in_flight_mtu_probe) -3359 .map(|(pn, info)| { -3360 size_of_lost_packets += info.size as u64; -3361 pn -3362 }) -3363 .collect(); +3347 self.handle_lost_packets( +3348 pn_space, +3349 path_id, +3350 now, +3351 lost_packets, +3352 lost_mtu_probe, +3353 loss_delay, +3354 in_persistent_congestion, +3355 size_of_lost_packets, +3356 ); +3357 } +3358 +3359 /// Drops the path state, declaring any remaining in-flight packets as lost +3360 fn discard_path(&mut self, path_id: PathId, now: Instant) { +3361 trace!(%path_id, "dropping path state"); +3362 let path = self.path_data(path_id); +3363 let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe(); 3364 -3365 if !lost_pns.is_empty() { -3366 trace!( -3367 %path_id, -3368 count = lost_pns.len(), -3369 lost_bytes = size_of_lost_packets, -3370 "packets lost on path abandon" -3371 ); -3372 self.handle_lost_packets( -3373 SpaceId::Data, -3374 path_id, -3375 now, -3376 lost_pns, -3377 in_flight_mtu_probe, -3378 Duration::ZERO, -3379 false, -3380 size_of_lost_packets, -3381 ); -3382 } -3383 // Before removing the path, we fetch the final path stats via `Self::path_stats`. -3384 // This updates some values for the last time. -3385 let path_stats = self.path_stats.discard(&path_id); -3386 self.partial_stats += path_stats; -3387 self.paths.remove(&path_id); -3388 self.spaces[SpaceId::Data].number_spaces.remove(&path_id); -3389 -3390 self.events.push_back( -3391 PathEvent::Discarded { -3392 id: path_id, -3393 path_stats: Box::new(path_stats), -3394 } -3395 .into(), -3396 ); -3397 } -3398 -3399 fn handle_lost_packets( -3400 &mut self, -3401 pn_space: SpaceId, -3402 path_id: PathId, -3403 now: Instant, -3404 lost_packets: Vec<u64>, -3405 lost_mtu_probe: Option<u64>, -3406 loss_delay: Duration, -3407 in_persistent_congestion: bool, -3408 size_of_lost_packets: u64, -3409 ) { -3410 debug_assert!(lost_packets.is_sorted(), "lost_packets must be sorted"); -3411 -3412 self.drain_lost_packets(now, pn_space, path_id); -3413 -3414 // OnPacketsLost -3415 if let Some(largest_lost) = lost_packets.last().cloned() { -3416 let old_bytes_in_flight = self.path_data_mut(path_id).in_flight.bytes; -3417 let largest_lost_sent = self.spaces[pn_space] -3418 .for_path(path_id) -3419 .sent_packets -3420 .get(largest_lost) -3421 .unwrap() -3422 .time_sent; -3423 let path_stats = self.path_stats.for_path(path_id); -3424 path_stats.lost_packets += lost_packets.len() as u64; -3425 path_stats.lost_bytes += size_of_lost_packets; -3426 trace!( -3427 %path_id, -3428 count = lost_packets.len(), -3429 lost_bytes = size_of_lost_packets, -3430 "packets lost", -3431 ); -3432 -3433 for &packet in &lost_packets { -3434 let Some(info) = self.spaces[pn_space].for_path(path_id).take(packet) else { -3435 continue; -3436 }; -3437 self.qlog -3438 .emit_packet_lost(packet, &info, loss_delay, pn_space.kind(), now); -3439 self.paths -3440 .get_mut(&path_id) -3441 .unwrap() -3442 .remove_in_flight(&info); -3443 -3444 for frame in info.stream_frames { -3445 self.streams.retransmit(frame); -3446 } -3447 self.spaces[pn_space].pending |= info.retransmits; -3448 let path = self.path_data_mut(path_id); -3449 path.mtud.on_non_probe_lost(packet, info.size); -3450 path.congestion.on_packet_lost(info.size, packet, now); -3451 -3452 self.spaces[pn_space].for_path(path_id).lost_packets.insert( -3453 packet, -3454 LostPacket { -3455 time_sent: info.time_sent, -3456 }, -3457 ); -3458 } -3459 -3460 let path = self.path_data_mut(path_id); -3461 if path.mtud.black_hole_detected(now) { -3462 path.congestion.on_mtu_update(path.mtud.current_mtu()); -3463 if let Some(max_datagram_size) = self.datagrams().max_size() -3464 && self.datagrams.drop_oversized(max_datagram_size) -3465 && self.datagrams.send_blocked -3466 { -3467 self.datagrams.send_blocked = false; -3468 self.events.push_back(Event::DatagramsUnblocked); -3469 } -3470 self.path_stats.for_path(path_id).black_holes_detected += 1; -3471 } -3472 -3473 // Don't apply congestion penalty for lost ack-only packets -3474 let lost_ack_eliciting = -3475 old_bytes_in_flight != self.path_data_mut(path_id).in_flight.bytes; -3476 -3477 if lost_ack_eliciting { -3478 self.path_stats.for_path(path_id).congestion_events += 1; -3479 self.path_data_mut(path_id).congestion.on_congestion_event( -3480 now, -3481 largest_lost_sent, -3482 in_persistent_congestion, -3483 false, -3484 size_of_lost_packets, -3485 largest_lost, -3486 ); -3487 } -3488 } -3489 -3490 // Handle a lost MTU probe -3491 if let Some(packet) = lost_mtu_probe { -3492 let info = self.spaces[SpaceId::Data] -3493 .for_path(path_id) -3494 .take(packet) -3495 .unwrap(); // safe: lost_mtu_probe is omitted from lost_packets, and -3496 // therefore must not have been removed yet -3497 self.paths -3498 .get_mut(&path_id) -3499 .unwrap() -3500 .remove_in_flight(&info); -3501 self.path_data_mut(path_id).mtud.on_probe_lost(); -3502 self.path_stats.for_path(path_id).lost_plpmtud_probes += 1; -3503 } -3504 } -3505 -3506 /// Returns the earliest time packets should be declared lost for all spaces on a path. -3507 /// -3508 /// If a path has an acknowledged packet with any prior un-acknowledged packets, the -3509 /// earliest un-acknowledged packet can be declared lost after a timeout has elapsed. -3510 /// The time returned is when this packet should be declared lost. -3511 fn loss_time_and_space(&self, path_id: PathId) -> Option<(Instant, SpaceId)> { -3512 SpaceId::iter() -3513 .filter_map(|id| { -3514 self.spaces[id] -3515 .number_spaces -3516 .get(&path_id) -3517 .and_then(|pns| pns.loss_time) -3518 .map(|time| (time, id)) -3519 }) -3520 .min_by_key(|&(time, _)| time) -3521 } -3522 -3523 /// Returns the earliest next PTO should fire for all spaces on a path. -3524 /// -3525 /// This needs to be fully deterministic because it is also used to determine the PTO -3526 /// that fired, not just to set the next timer. So if it fired in the past it needs to -3527 /// return the time from the past at which it fired. -3528 /// -3529 /// This is the next time a tail-loss probe should be sent. -3530 fn pto_time_and_space(&mut self, now: Instant, path_id: PathId) -> Option<(Instant, SpaceId)> { -3531 let path = self.path(path_id)?; -3532 let pto_count = path.pto_count; -3533 -3534 // Cap the maximum interval between two tail-loss probes. -3535 let max_interval = if path.rtt.get() > SLOW_RTT_THRESHOLD { -3536 // For slow links we want to increase the interval beyond 2s. -3537 (path.rtt.get() * 3) / 2 -3538 } else if let Some(idle) = path.idle_timeout.or(self.idle_timeout) -3539 && idle <= MIN_IDLE_FOR_FAST_PTO -3540 { -3541 // If the idle timeout is relatively low, cap at 1s so we get plenty of retries -3542 // before the idle timeout fires. -3543 MAX_PTO_FAST_INTERVAL -3544 } else { -3545 // Otherwise cap to 2s. -3546 MAX_PTO_INTERVAL -3547 }; -3548 -3549 if path_id == PathId::ZERO -3550 && path.in_flight.ack_eliciting == 0 -3551 && !self.peer_completed_handshake_address_validation() +3365 let mut size_of_lost_packets = 0u64; // add to path_stats.lost_bytes; +3366 let lost_pns: Vec<_> = self.spaces[SpaceId::Data] +3367 .for_path(path_id) +3368 .sent_packets +3369 .iter() +3370 .filter(|(pn, _info)| Some(*pn) != in_flight_mtu_probe) +3371 .map(|(pn, info)| { +3372 size_of_lost_packets += info.size as u64; +3373 pn +3374 }) +3375 .collect(); +3376 +3377 if !lost_pns.is_empty() { +3378 trace!( +3379 %path_id, +3380 count = lost_pns.len(), +3381 lost_bytes = size_of_lost_packets, +3382 "packets lost on path abandon" +3383 ); +3384 self.handle_lost_packets( +3385 SpaceId::Data, +3386 path_id, +3387 now, +3388 lost_pns, +3389 in_flight_mtu_probe, +3390 Duration::ZERO, +3391 false, +3392 size_of_lost_packets, +3393 ); +3394 } +3395 // Before removing the path, we fetch the final path stats via `Self::path_stats`. +3396 // This updates some values for the last time. +3397 let path_stats = self.path_stats.discard(&path_id); +3398 self.partial_stats += path_stats; +3399 self.paths.remove(&path_id); +3400 self.spaces[SpaceId::Data].number_spaces.remove(&path_id); +3401 +3402 self.events.push_back( +3403 PathEvent::Discarded { +3404 id: path_id, +3405 path_stats: Box::new(path_stats), +3406 } +3407 .into(), +3408 ); +3409 } +3410 +3411 fn handle_lost_packets( +3412 &mut self, +3413 pn_space: SpaceId, +3414 path_id: PathId, +3415 now: Instant, +3416 lost_packets: Vec<u64>, +3417 lost_mtu_probe: Option<u64>, +3418 loss_delay: Duration, +3419 in_persistent_congestion: bool, +3420 size_of_lost_packets: u64, +3421 ) { +3422 debug_assert!(lost_packets.is_sorted(), "lost_packets must be sorted"); +3423 +3424 self.drain_lost_packets(now, pn_space, path_id); +3425 +3426 // OnPacketsLost +3427 if let Some(largest_lost) = lost_packets.last().cloned() { +3428 let old_bytes_in_flight = self.path_data_mut(path_id).in_flight.bytes; +3429 let largest_lost_sent = self.spaces[pn_space] +3430 .for_path(path_id) +3431 .sent_packets +3432 .get(largest_lost) +3433 .unwrap() +3434 .time_sent; +3435 let path_stats = self.path_stats.for_path(path_id); +3436 path_stats.lost_packets += lost_packets.len() as u64; +3437 path_stats.lost_bytes += size_of_lost_packets; +3438 trace!( +3439 %path_id, +3440 count = lost_packets.len(), +3441 lost_bytes = size_of_lost_packets, +3442 "packets lost", +3443 ); +3444 +3445 for &packet in &lost_packets { +3446 let Some(info) = self.spaces[pn_space].for_path(path_id).take(packet) else { +3447 continue; +3448 }; +3449 self.qlog +3450 .emit_packet_lost(packet, &info, loss_delay, pn_space.kind(), now); +3451 self.paths +3452 .get_mut(&path_id) +3453 .unwrap() +3454 .remove_in_flight(&info); +3455 +3456 for frame in info.stream_frames { +3457 self.streams.retransmit(frame); +3458 } +3459 self.spaces[pn_space].pending |= info.retransmits; +3460 let path = self.path_data_mut(path_id); +3461 path.mtud.on_non_probe_lost(packet, info.size); +3462 path.congestion.on_packet_lost(info.size, packet, now); +3463 +3464 self.spaces[pn_space].for_path(path_id).lost_packets.insert( +3465 packet, +3466 LostPacket { +3467 time_sent: info.time_sent, +3468 }, +3469 ); +3470 } +3471 +3472 let path = self.path_data_mut(path_id); +3473 if path.mtud.black_hole_detected(now) { +3474 path.congestion.on_mtu_update(path.mtud.current_mtu()); +3475 if let Some(max_datagram_size) = self.datagrams().max_size() +3476 && self.datagrams.drop_oversized(max_datagram_size) +3477 && self.datagrams.send_blocked +3478 { +3479 self.datagrams.send_blocked = false; +3480 self.events.push_back(Event::DatagramsUnblocked); +3481 } +3482 self.path_stats.for_path(path_id).black_holes_detected += 1; +3483 } +3484 +3485 // Don't apply congestion penalty for lost ack-only packets +3486 let lost_ack_eliciting = +3487 old_bytes_in_flight != self.path_data_mut(path_id).in_flight.bytes; +3488 +3489 if lost_ack_eliciting { +3490 self.path_stats.for_path(path_id).congestion_events += 1; +3491 self.path_data_mut(path_id).congestion.on_congestion_event( +3492 now, +3493 largest_lost_sent, +3494 in_persistent_congestion, +3495 false, +3496 size_of_lost_packets, +3497 largest_lost, +3498 ); +3499 } +3500 } +3501 +3502 // Handle a lost MTU probe +3503 if let Some(packet) = lost_mtu_probe { +3504 let info = self.spaces[SpaceId::Data] +3505 .for_path(path_id) +3506 .take(packet) +3507 .unwrap(); // safe: lost_mtu_probe is omitted from lost_packets, and +3508 // therefore must not have been removed yet +3509 self.paths +3510 .get_mut(&path_id) +3511 .unwrap() +3512 .remove_in_flight(&info); +3513 self.path_data_mut(path_id).mtud.on_probe_lost(); +3514 self.path_stats.for_path(path_id).lost_plpmtud_probes += 1; +3515 } +3516 } +3517 +3518 /// Returns the earliest time packets should be declared lost for all spaces on a path. +3519 /// +3520 /// If a path has an acknowledged packet with any prior un-acknowledged packets, the +3521 /// earliest un-acknowledged packet can be declared lost after a timeout has elapsed. +3522 /// The time returned is when this packet should be declared lost. +3523 fn loss_time_and_space(&self, path_id: PathId) -> Option<(Instant, SpaceId)> { +3524 SpaceId::iter() +3525 .filter_map(|id| { +3526 self.spaces[id] +3527 .number_spaces +3528 .get(&path_id) +3529 .and_then(|pns| pns.loss_time) +3530 .map(|time| (time, id)) +3531 }) +3532 .min_by_key(|&(time, _)| time) +3533 } +3534 +3535 /// Returns the earliest next PTO should fire for all spaces on a path. +3536 /// +3537 /// This needs to be fully deterministic because it is also used to determine the PTO +3538 /// that fired, not just to set the next timer. So if it fired in the past it needs to +3539 /// return the time from the past at which it fired. +3540 /// +3541 /// This is the next time a tail-loss probe should be sent. +3542 fn pto_time_and_space(&mut self, now: Instant, path_id: PathId) -> Option<(Instant, SpaceId)> { +3543 let path = self.path(path_id)?; +3544 let pto_count = path.pto_count; +3545 +3546 // Cap the maximum interval between two tail-loss probes. +3547 let max_interval = if path.rtt.get() > SLOW_RTT_THRESHOLD { +3548 // For slow links we want to increase the interval beyond 2s. +3549 (path.rtt.get() * 3) / 2 +3550 } else if let Some(idle) = path.idle_timeout.or(self.idle_timeout) +3551 && idle <= MIN_IDLE_FOR_FAST_PTO 3552 { -3553 // Address Validation during Connection Establishment: -3554 // https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1. To prevent a -3555 // deadlock if an Initial or Handshake packet from the server is lost and the -3556 // server can not send more due to its anti-amplification limit the client must -3557 // send another packet on PTO. -3558 let space = match self.highest_space { -3559 SpaceKind::Handshake => SpaceId::Handshake, -3560 _ => SpaceId::Initial, -3561 }; -3562 -3563 let backoff = 2u32.pow(path.pto_count.min(MAX_BACKOFF_EXPONENT)); -3564 let duration = path.rtt.pto_base() * backoff; -3565 let duration = duration.min(max_interval); -3566 return Some((now + duration, space)); -3567 } -3568 -3569 let mut result = None; -3570 for space in SpaceId::iter() { -3571 let Some(pns) = self.spaces[space].number_spaces.get(&path_id) else { -3572 continue; +3553 // If the idle timeout is relatively low, cap at 1s so we get plenty of retries +3554 // before the idle timeout fires. +3555 MAX_PTO_FAST_INTERVAL +3556 } else { +3557 // Otherwise cap to 2s. +3558 MAX_PTO_INTERVAL +3559 }; +3560 +3561 if path_id == PathId::ZERO +3562 && path.in_flight.ack_eliciting == 0 +3563 && !self.peer_completed_handshake_address_validation() +3564 { +3565 // Address Validation during Connection Establishment: +3566 // https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1. To prevent a +3567 // deadlock if an Initial or Handshake packet from the server is lost and the +3568 // server can not send more due to its anti-amplification limit the client must +3569 // send another packet on PTO. +3570 let space = match self.highest_space { +3571 SpaceKind::Handshake => SpaceId::Handshake, +3572 _ => SpaceId::Initial, 3573 }; 3574 -3575 if space == SpaceId::Data && !self.is_handshake_confirmed() { -3576 // https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.1-7: -3577 // An endpoint MUST NOT set its PTO timer for the Application Data packet -3578 // number space until the handshake is confirmed. -3579 continue; -3580 } -3581 -3582 if !pns.has_in_flight() { -3583 continue; -3584 } -3585 -3586 // Compute the PTO duration for this space, we want to cap the maximum interval -3587 // between two tail-loss probes so to not do a simple exponential backoff but -3588 // rather iterate through the probes to compute the capped increment for an -3589 // exponential backoff at each step. -3590 let duration = { -3591 let max_ack_delay = if space == SpaceId::Data { -3592 self.ack_frequency.max_ack_delay_for_pto() -3593 } else { -3594 Duration::ZERO -3595 }; -3596 let pto_base = path.rtt.pto_base() + max_ack_delay; -3597 let mut duration = pto_base; -3598 for i in 1..=pto_count { -3599 let exponential_duration = pto_base * 2u32.pow(i.min(MAX_BACKOFF_EXPONENT)); -3600 let max_duration = duration + max_interval; -3601 duration = exponential_duration.min(max_duration); -3602 } -3603 duration -3604 }; -3605 -3606 let Some(last_ack_eliciting) = pns.time_of_last_ack_eliciting_packet else { -3607 continue; -3608 }; -3609 // Base the deadline on when the last probe was sent, so the PTO -3610 // doesn't fire before the response has had time to arrive. -3611 let pto = last_ack_eliciting + duration; -3612 if result.is_none_or(|(earliest_pto, _)| pto < earliest_pto) { -3613 if path.anti_amplification_blocked(1) { -3614 // Nothing would be able to be sent. -3615 continue; -3616 } -3617 if path.in_flight.ack_eliciting == 0 { -3618 // Nothing ack-eliciting, no PTO to arm/fire. -3619 continue; -3620 } -3621 result = Some((pto, space)); -3622 } -3623 } -3624 result -3625 } -3626 -3627 /// Whether the peer validated our address in the connection handshake. -3628 fn peer_completed_handshake_address_validation(&self) -> bool { -3629 if self.side.is_server() || self.state.is_closed() { -3630 return true; -3631 } -3632 // The server is guaranteed to have validated our address if any of our handshake or -3633 // 1-RTT packets are acknowledged or we've seen HANDSHAKE_DONE and discarded -3634 // handshake keys. -3635 self.spaces[SpaceId::Handshake] -3636 .path_space(PathId::ZERO) -3637 .and_then(|pns| pns.largest_acked_packet_pn) -3638 .is_some() -3639 || self.spaces[SpaceId::Data] -3640 .path_space(PathId::ZERO) -3641 .and_then(|pns| pns.largest_acked_packet_pn) -3642 .is_some() -3643 || (self.crypto_state.has_keys(EncryptionLevel::OneRtt) -3644 && !self.crypto_state.has_keys(EncryptionLevel::Handshake)) -3645 } -3646 -3647 /// Resets the the [`PathTimer::LossDetection`] timer to the next instant it may be needed -3648 /// -3649 /// The timer must fire if either: -3650 /// - An ack-eliciting packet we sent needs to be declared lost. -3651 /// - A tail-loss probe needs to be sent. -3652 /// -3653 /// See [`Connection::on_loss_detection_timeout`] for details. -3654 fn set_loss_detection_timer(&mut self, now: Instant, path_id: PathId) { -3655 if self.state.is_closed() { -3656 // No loss detection takes place on closed connections, and `close_common` already -3657 // stopped time timer. Ensure we don't restart it inadvertently, e.g. in response to a -3658 // reordered packet being handled by state-insensitive code. -3659 return; -3660 } -3661 -3662 if let Some((loss_time, _)) = self.loss_time_and_space(path_id) { -3663 // Time threshold loss detection. -3664 self.timers.set( -3665 Timer::PerPath(path_id, PathTimer::LossDetection), -3666 loss_time, -3667 self.qlog.with_time(now), -3668 ); -3669 return; -3670 } -3671 -3672 // Determine which PN space to arm PTO for. -3673 // We can only send tail-loss probes on paths that aren't abandoned yet. -3674 if !self.abandoned_paths.contains(&path_id) -3675 && let Some((timeout, _)) = self.pto_time_and_space(now, path_id) -3676 { -3677 self.timers.set( -3678 Timer::PerPath(path_id, PathTimer::LossDetection), -3679 timeout, -3680 self.qlog.with_time(now), -3681 ); -3682 } else { -3683 self.timers.stop( -3684 Timer::PerPath(path_id, PathTimer::LossDetection), -3685 self.qlog.with_time(now), -3686 ); -3687 } -3688 } -3689 -3690 /// The maximum probe timeout across all paths -3691 /// -3692 /// See [`Connection::pto`] -3693 fn max_pto_for_space(&self, space: SpaceKind) -> Duration { -3694 self.paths -3695 .keys() -3696 .map(|path_id| self.pto(space, *path_id)) -3697 .max() -3698 .unwrap_or_else(|| { -3699 // No paths remain (e.g. last path was abandoned and the NoAvailablePath grace timer -3700 // fired before any new path was opened). Fall back to a PTO derived from the -3701 // configured initial RTT, matching RFC 9002 §6.2.2 initial values. -3702 let rtt = self.config.initial_rtt; -3703 let max_ack_delay = match space { -3704 SpaceKind::Initial | SpaceKind::Handshake => Duration::ZERO, -3705 SpaceKind::Data => self.ack_frequency.max_ack_delay_for_pto(), -3706 }; -3707 rtt + cmp::max(4 * (rtt / 2), TIMER_GRANULARITY) + max_ack_delay -3708 }) -3709 } -3710 -3711 /// Probe Timeout -3712 /// -3713 /// The PTO is logically the time in which you'd expect to receive an acknowledgement -3714 /// for a packet. So approximately RTT + max_ack_delay. -3715 fn pto(&self, space: SpaceKind, path_id: PathId) -> Duration { -3716 let max_ack_delay = match space { -3717 SpaceKind::Initial | SpaceKind::Handshake => Duration::ZERO, -3718 SpaceKind::Data => self.ack_frequency.max_ack_delay_for_pto(), -3719 }; -3720 self.path_data(path_id).rtt.pto_base() + max_ack_delay +3575 let backoff = 2u32.pow(path.pto_count.min(MAX_BACKOFF_EXPONENT)); +3576 let duration = path.rtt.pto_base() * backoff; +3577 let duration = duration.min(max_interval); +3578 return Some((now + duration, space)); +3579 } +3580 +3581 let mut result = None; +3582 for space in SpaceId::iter() { +3583 let Some(pns) = self.spaces[space].number_spaces.get(&path_id) else { +3584 continue; +3585 }; +3586 +3587 if space == SpaceId::Data && !self.is_handshake_confirmed() { +3588 // https://www.rfc-editor.org/rfc/rfc9002.html#section-6.2.1-7: +3589 // An endpoint MUST NOT set its PTO timer for the Application Data packet +3590 // number space until the handshake is confirmed. +3591 continue; +3592 } +3593 +3594 if !pns.has_in_flight() { +3595 continue; +3596 } +3597 +3598 // Compute the PTO duration for this space, we want to cap the maximum interval +3599 // between two tail-loss probes so to not do a simple exponential backoff but +3600 // rather iterate through the probes to compute the capped increment for an +3601 // exponential backoff at each step. +3602 let duration = { +3603 let max_ack_delay = if space == SpaceId::Data { +3604 self.ack_frequency.max_ack_delay_for_pto() +3605 } else { +3606 Duration::ZERO +3607 }; +3608 let pto_base = path.rtt.pto_base() + max_ack_delay; +3609 let mut duration = pto_base; +3610 for i in 1..=pto_count { +3611 let exponential_duration = pto_base * 2u32.pow(i.min(MAX_BACKOFF_EXPONENT)); +3612 let max_duration = duration + max_interval; +3613 duration = exponential_duration.min(max_duration); +3614 } +3615 duration +3616 }; +3617 +3618 let Some(last_ack_eliciting) = pns.time_of_last_ack_eliciting_packet else { +3619 continue; +3620 }; +3621 // Base the deadline on when the last probe was sent, so the PTO +3622 // doesn't fire before the response has had time to arrive. +3623 let pto = last_ack_eliciting + duration; +3624 if result.is_none_or(|(earliest_pto, _)| pto < earliest_pto) { +3625 if path.anti_amplification_blocked(1) { +3626 // Nothing would be able to be sent. +3627 continue; +3628 } +3629 if path.in_flight.ack_eliciting == 0 { +3630 // Nothing ack-eliciting, no PTO to arm/fire. +3631 continue; +3632 } +3633 result = Some((pto, space)); +3634 } +3635 } +3636 result +3637 } +3638 +3639 /// Whether the peer validated our address in the connection handshake. +3640 fn peer_completed_handshake_address_validation(&self) -> bool { +3641 if self.side.is_server() || self.state.is_closed() { +3642 return true; +3643 } +3644 // The server is guaranteed to have validated our address if any of our handshake or +3645 // 1-RTT packets are acknowledged or we've seen HANDSHAKE_DONE and discarded +3646 // handshake keys. +3647 self.spaces[SpaceId::Handshake] +3648 .path_space(PathId::ZERO) +3649 .and_then(|pns| pns.largest_acked_packet_pn) +3650 .is_some() +3651 || self.spaces[SpaceId::Data] +3652 .path_space(PathId::ZERO) +3653 .and_then(|pns| pns.largest_acked_packet_pn) +3654 .is_some() +3655 || (self.crypto_state.has_keys(EncryptionLevel::OneRtt) +3656 && !self.crypto_state.has_keys(EncryptionLevel::Handshake)) +3657 } +3658 +3659 /// Resets the the [`PathTimer::LossDetection`] timer to the next instant it may be needed +3660 /// +3661 /// The timer must fire if either: +3662 /// - An ack-eliciting packet we sent needs to be declared lost. +3663 /// - A tail-loss probe needs to be sent. +3664 /// +3665 /// See [`Connection::on_loss_detection_timeout`] for details. +3666 fn set_loss_detection_timer(&mut self, now: Instant, path_id: PathId) { +3667 if self.state.is_closed() { +3668 // No loss detection takes place on closed connections, and `close_common` already +3669 // stopped time timer. Ensure we don't restart it inadvertently, e.g. in response to a +3670 // reordered packet being handled by state-insensitive code. +3671 return; +3672 } +3673 +3674 if let Some((loss_time, _)) = self.loss_time_and_space(path_id) { +3675 // Time threshold loss detection. +3676 self.timers.set( +3677 Timer::PerPath(path_id, PathTimer::LossDetection), +3678 loss_time, +3679 self.qlog.with_time(now), +3680 ); +3681 return; +3682 } +3683 +3684 // Determine which PN space to arm PTO for. +3685 // We can only send tail-loss probes on paths that aren't abandoned yet. +3686 if !self.abandoned_paths.contains(&path_id) +3687 && let Some((timeout, _)) = self.pto_time_and_space(now, path_id) +3688 { +3689 self.timers.set( +3690 Timer::PerPath(path_id, PathTimer::LossDetection), +3691 timeout, +3692 self.qlog.with_time(now), +3693 ); +3694 } else { +3695 self.timers.stop( +3696 Timer::PerPath(path_id, PathTimer::LossDetection), +3697 self.qlog.with_time(now), +3698 ); +3699 } +3700 } +3701 +3702 /// The maximum probe timeout across all paths +3703 /// +3704 /// See [`Connection::pto`] +3705 fn max_pto_for_space(&self, space: SpaceKind) -> Duration { +3706 self.paths +3707 .keys() +3708 .map(|path_id| self.pto(space, *path_id)) +3709 .max() +3710 .unwrap_or_else(|| { +3711 // No paths remain (e.g. last path was abandoned and the NoAvailablePath grace timer +3712 // fired before any new path was opened). Fall back to a PTO derived from the +3713 // configured initial RTT, matching RFC 9002 §6.2.2 initial values. +3714 let rtt = self.config.initial_rtt; +3715 let max_ack_delay = match space { +3716 SpaceKind::Initial | SpaceKind::Handshake => Duration::ZERO, +3717 SpaceKind::Data => self.ack_frequency.max_ack_delay_for_pto(), +3718 }; +3719 rtt + cmp::max(4 * (rtt / 2), TIMER_GRANULARITY) + max_ack_delay +3720 }) 3721 } 3722 -3723 fn on_packet_authenticated( -3724 &mut self, -3725 now: Instant, -3726 space_id: SpaceKind, -3727 path_id: PathId, -3728 ecn: Option<EcnCodepoint>, -3729 packet_number: Option<u64>, -3730 spin: bool, -3731 is_1rtt: bool, -3732 remote: &FourTuple, -3733 ) { -3734 // During the handshake we already have discarded packets that do not match the path -3735 // remote. So any off-path packet here is either a probing packet or a -3736 // migration. Handling probing packets here means that the path's idle timeout will -3737 // be reset and will delay detecting the path as idle. However tail-loss probes -3738 // would still not get acknowledged if the path was broken so eventually the path -3739 // would still become idle. -3740 let is_on_path = self -3741 .path_data(path_id) -3742 .network_path -3743 .is_probably_same_path(remote); -3744 -3745 self.total_authed_packets += 1; -3746 self.reset_keep_alive(path_id, now); -3747 self.reset_idle_timeout(now, space_id, path_id); -3748 self.path_data_mut(path_id).permit_idle_reset = true; -3749 -3750 // Do not process ECN for off-path packets. If this is a migration we'll get ECN -3751 // back once we've migrated. -3752 if is_on_path { -3753 self.receiving_ecn |= ecn.is_some(); -3754 if let Some(x) = ecn { -3755 let space = &mut self.spaces[space_id]; -3756 space.for_path(path_id).ecn_counters += x; -3757 -3758 if x.is_ce() { -3759 space -3760 .for_path(path_id) -3761 .pending_acks -3762 .set_immediate_ack_required(); -3763 } -3764 } -3765 } -3766 -3767 let Some(packet_number) = packet_number else { -3768 return; -3769 }; -3770 match &self.side { -3771 ConnectionSide::Client { .. } => { -3772 // If we received a handshake packet that authenticated, then we're talking to -3773 // the real server. From now on we should no longer allow the server to migrate -3774 // its address. -3775 if space_id == SpaceKind::Handshake -3776 && let Some(hs) = self.state.as_handshake_mut() -3777 { -3778 hs.allow_server_migration = false; -3779 } -3780 } -3781 ConnectionSide::Server { .. } => { -3782 if self.crypto_state.has_keys(EncryptionLevel::Initial) -3783 && space_id == SpaceKind::Handshake -3784 { -3785 // A server stops sending and processing Initial packets when it receives its first Handshake packet. -3786 self.discard_space(now, SpaceKind::Initial); -3787 } -3788 if self.crypto_state.has_keys(EncryptionLevel::ZeroRtt) && is_1rtt { -3789 // Discard 0-RTT keys soon after receiving a 1-RTT packet -3790 self.set_key_discard_timer(now, space_id) +3723 /// Probe Timeout +3724 /// +3725 /// The PTO is logically the time in which you'd expect to receive an acknowledgement +3726 /// for a packet. So approximately RTT + max_ack_delay. +3727 fn pto(&self, space: SpaceKind, path_id: PathId) -> Duration { +3728 let max_ack_delay = match space { +3729 SpaceKind::Initial | SpaceKind::Handshake => Duration::ZERO, +3730 SpaceKind::Data => self.ack_frequency.max_ack_delay_for_pto(), +3731 }; +3732 self.path_data(path_id).rtt.pto_base() + max_ack_delay +3733 } +3734 +3735 fn on_packet_authenticated( +3736 &mut self, +3737 now: Instant, +3738 space_id: SpaceKind, +3739 path_id: PathId, +3740 ecn: Option<EcnCodepoint>, +3741 packet_number: Option<u64>, +3742 spin: bool, +3743 is_1rtt: bool, +3744 remote: &FourTuple, +3745 ) { +3746 // During the handshake we already have discarded packets that do not match the path +3747 // remote. So any off-path packet here is either a probing packet or a +3748 // migration. Handling probing packets here means that the path's idle timeout will +3749 // be reset and will delay detecting the path as idle. However tail-loss probes +3750 // would still not get acknowledged if the path was broken so eventually the path +3751 // would still become idle. +3752 let is_on_path = self +3753 .path_data(path_id) +3754 .network_path +3755 .is_probably_same_path(remote); +3756 +3757 self.total_authed_packets += 1; +3758 self.reset_keep_alive(path_id, now); +3759 self.reset_idle_timeout(now, space_id, path_id); +3760 self.path_data_mut(path_id).permit_idle_reset = true; +3761 +3762 // Do not process ECN for off-path packets. If this is a migration we'll get ECN +3763 // back once we've migrated. +3764 if is_on_path { +3765 self.receiving_ecn |= ecn.is_some(); +3766 if let Some(x) = ecn { +3767 let space = &mut self.spaces[space_id]; +3768 space.for_path(path_id).ecn_counters += x; +3769 +3770 if x.is_ce() { +3771 space +3772 .for_path(path_id) +3773 .pending_acks +3774 .set_immediate_ack_required(); +3775 } +3776 } +3777 } +3778 +3779 let Some(packet_number) = packet_number else { +3780 return; +3781 }; +3782 match &self.side { +3783 ConnectionSide::Client { .. } => { +3784 // If we received a handshake packet that authenticated, then we're talking to +3785 // the real server. From now on we should no longer allow the server to migrate +3786 // its address. +3787 if space_id == SpaceKind::Handshake +3788 && let Some(hs) = self.state.as_handshake_mut() +3789 { +3790 hs.allow_server_migration = false; 3791 } 3792 } -3793 } -3794 let space = self.spaces[space_id].for_path(path_id); -3795 -3796 space.pending_acks.insert_one(packet_number, now); -3797 if packet_number >= space.largest_received_packet_number.unwrap_or_default() { -3798 space.largest_received_packet_number = Some(packet_number); -3799 -3800 // Update outgoing spin bit for on-path packets, inverting iff we're the client -3801 if is_on_path { -3802 self.spin = self.side.is_client() ^ spin; -3803 } -3804 } -3805 } -3806 -3807 /// Resets the idle timeout timers -3808 /// -3809 /// Without multipath there is only the connection-wide idle timeout. When multipath is -3810 /// enabled there is an additional per-path idle timeout. -3811 fn reset_idle_timeout(&mut self, now: Instant, space: SpaceKind, path_id: PathId) { -3812 // First reset the global idle timeout. -3813 if let Some(timeout) = self.idle_timeout { -3814 if self.state.is_closed() { -3815 self.timers -3816 .stop(Timer::Conn(ConnTimer::Idle), self.qlog.with_time(now)); -3817 } else { -3818 let dt = cmp::max(timeout, 3 * self.max_pto_for_space(space)); -3819 self.timers.set( -3820 Timer::Conn(ConnTimer::Idle), -3821 now + dt, -3822 self.qlog.with_time(now), -3823 ); -3824 } -3825 } -3826 -3827 // Now handle the per-path state -3828 if let Some(timeout) = self.path_data(path_id).idle_timeout { -3829 if self.state.is_closed() { -3830 self.timers.stop( -3831 Timer::PerPath(path_id, PathTimer::PathIdle), -3832 self.qlog.with_time(now), -3833 ); -3834 } else { -3835 let dt = cmp::max(timeout, 3 * self.pto(space, path_id)); -3836 self.timers.set( -3837 Timer::PerPath(path_id, PathTimer::PathIdle), -3838 now + dt, -3839 self.qlog.with_time(now), -3840 ); -3841 } -3842 } -3843 } -3844 -3845 /// Resets both the [`ConnTimer::KeepAlive`] and [`PathTimer::PathKeepAlive`] timers -3846 fn reset_keep_alive(&mut self, path_id: PathId, now: Instant) { -3847 if !self.state.is_established() { -3848 return; -3849 } -3850 -3851 if let Some(interval) = self.config.keep_alive_interval { -3852 self.timers.set( -3853 Timer::Conn(ConnTimer::KeepAlive), -3854 now + interval, -3855 self.qlog.with_time(now), -3856 ); -3857 } -3858 -3859 if let Some(interval) = self.path_data(path_id).keep_alive { -3860 self.timers.set( -3861 Timer::PerPath(path_id, PathTimer::PathKeepAlive), -3862 now + interval, -3863 self.qlog.with_time(now), -3864 ); -3865 } -3866 } -3867 -3868 /// Sets the timer for when a previously issued CID should be retired next -3869 fn reset_cid_retirement(&mut self, now: Instant) { -3870 if let Some((_path, t)) = self.next_cid_retirement() { -3871 self.timers.set( -3872 Timer::Conn(ConnTimer::PushNewCid), -3873 t, -3874 self.qlog.with_time(now), -3875 ); -3876 } -3877 } -3878 -3879 /// The next time when a previously issued CID should be retired -3880 fn next_cid_retirement(&self) -> Option<(PathId, Instant)> { -3881 self.local_cid_state -3882 .iter() -3883 .filter_map(|(path_id, cid_state)| cid_state.next_timeout().map(|t| (*path_id, t))) -3884 .min_by_key(|(_path_id, timeout)| *timeout) -3885 } -3886 -3887 /// Handle the already-decrypted first packet from the client -3888 /// -3889 /// Decrypting the first packet in the `Endpoint` allows stateless packet handling to be more -3890 /// efficient. -3891 pub(crate) fn handle_first_packet( -3892 &mut self, -3893 now: Instant, -3894 network_path: FourTuple, -3895 ecn: Option<EcnCodepoint>, -3896 packet_number: u64, -3897 packet: InitialPacket, -3898 remaining: Option<BytesMut>, -3899 ) -> Result<(), ConnectionError> { -3900 let span = trace_span!("first recv"); -3901 let _guard = span.enter(); -3902 debug_assert!(self.side.is_server()); -3903 let len = packet.header_data.len() + packet.payload.len(); -3904 let path_id = PathId::ZERO; -3905 self.path_data_mut(path_id).total_recvd = len as u64; -3906 -3907 if let Some(hs) = self.state.as_handshake_mut() { -3908 hs.expected_token = packet.header.token.clone(); -3909 } else { -3910 unreachable!("first packet must be delivered in Handshake state"); -3911 } -3912 -3913 // The first packet is always on PathId::ZERO -3914 self.on_packet_authenticated( -3915 now, -3916 SpaceKind::Initial, -3917 path_id, -3918 ecn, -3919 Some(packet_number), -3920 false, -3921 false, -3922 &network_path, -3923 ); +3793 ConnectionSide::Server { .. } => { +3794 if self.crypto_state.has_keys(EncryptionLevel::Initial) +3795 && space_id == SpaceKind::Handshake +3796 { +3797 // A server stops sending and processing Initial packets when it receives its first Handshake packet. +3798 self.discard_space(now, SpaceKind::Initial); +3799 } +3800 if self.crypto_state.has_keys(EncryptionLevel::ZeroRtt) && is_1rtt { +3801 // Discard 0-RTT keys soon after receiving a 1-RTT packet +3802 self.set_key_discard_timer(now, space_id) +3803 } +3804 } +3805 } +3806 let space = self.spaces[space_id].for_path(path_id); +3807 +3808 space.pending_acks.insert_one(packet_number, now); +3809 if packet_number >= space.largest_received_packet_number.unwrap_or_default() { +3810 space.largest_received_packet_number = Some(packet_number); +3811 +3812 // Update outgoing spin bit for on-path packets, inverting iff we're the client +3813 if is_on_path { +3814 self.spin = self.side.is_client() ^ spin; +3815 } +3816 } +3817 } +3818 +3819 /// Resets the idle timeout timers +3820 /// +3821 /// Without multipath there is only the connection-wide idle timeout. When multipath is +3822 /// enabled there is an additional per-path idle timeout. +3823 fn reset_idle_timeout(&mut self, now: Instant, space: SpaceKind, path_id: PathId) { +3824 // First reset the global idle timeout. +3825 if let Some(timeout) = self.idle_timeout { +3826 if self.state.is_closed() { +3827 self.timers +3828 .stop(Timer::Conn(ConnTimer::Idle), self.qlog.with_time(now)); +3829 } else { +3830 let dt = cmp::max(timeout, 3 * self.max_pto_for_space(space)); +3831 self.timers.set( +3832 Timer::Conn(ConnTimer::Idle), +3833 now + dt, +3834 self.qlog.with_time(now), +3835 ); +3836 } +3837 } +3838 +3839 // Now handle the per-path state +3840 if let Some(timeout) = self.path_data(path_id).idle_timeout { +3841 if self.state.is_closed() { +3842 self.timers.stop( +3843 Timer::PerPath(path_id, PathTimer::PathIdle), +3844 self.qlog.with_time(now), +3845 ); +3846 } else { +3847 let dt = cmp::max(timeout, 3 * self.pto(space, path_id)); +3848 self.timers.set( +3849 Timer::PerPath(path_id, PathTimer::PathIdle), +3850 now + dt, +3851 self.qlog.with_time(now), +3852 ); +3853 } +3854 } +3855 } +3856 +3857 /// Resets both the [`ConnTimer::KeepAlive`] and [`PathTimer::PathKeepAlive`] timers +3858 fn reset_keep_alive(&mut self, path_id: PathId, now: Instant) { +3859 if !self.state.is_established() { +3860 return; +3861 } +3862 +3863 if let Some(interval) = self.config.keep_alive_interval { +3864 self.timers.set( +3865 Timer::Conn(ConnTimer::KeepAlive), +3866 now + interval, +3867 self.qlog.with_time(now), +3868 ); +3869 } +3870 +3871 if let Some(interval) = self.path_data(path_id).keep_alive { +3872 self.timers.set( +3873 Timer::PerPath(path_id, PathTimer::PathKeepAlive), +3874 now + interval, +3875 self.qlog.with_time(now), +3876 ); +3877 } +3878 } +3879 +3880 /// Sets the timer for when a previously issued CID should be retired next +3881 fn reset_cid_retirement(&mut self, now: Instant) { +3882 if let Some((_path, t)) = self.next_cid_retirement() { +3883 self.timers.set( +3884 Timer::Conn(ConnTimer::PushNewCid), +3885 t, +3886 self.qlog.with_time(now), +3887 ); +3888 } +3889 } +3890 +3891 /// The next time when a previously issued CID should be retired +3892 fn next_cid_retirement(&self) -> Option<(PathId, Instant)> { +3893 self.local_cid_state +3894 .iter() +3895 .filter_map(|(path_id, cid_state)| cid_state.next_timeout().map(|t| (*path_id, t))) +3896 .min_by_key(|(_path_id, timeout)| *timeout) +3897 } +3898 +3899 /// Handle the already-decrypted first packet from the client +3900 /// +3901 /// Decrypting the first packet in the `Endpoint` allows stateless packet handling to be more +3902 /// efficient. +3903 pub(crate) fn handle_first_packet( +3904 &mut self, +3905 now: Instant, +3906 network_path: FourTuple, +3907 ecn: Option<EcnCodepoint>, +3908 packet_number: u64, +3909 packet: InitialPacket, +3910 remaining: Option<BytesMut>, +3911 ) -> Result<(), ConnectionError> { +3912 let span = trace_span!("first recv"); +3913 let _guard = span.enter(); +3914 debug_assert!(self.side.is_server()); +3915 let len = packet.header_data.len() + packet.payload.len(); +3916 let path_id = PathId::ZERO; +3917 self.path_data_mut(path_id).total_recvd = len as u64; +3918 +3919 if let Some(hs) = self.state.as_handshake_mut() { +3920 hs.expected_token = packet.header.token.clone(); +3921 } else { +3922 unreachable!("first packet must be delivered in Handshake state"); +3923 } 3924 -3925 let packet: Packet = packet.into(); -3926 -3927 let mut qlog = QlogRecvPacket::new(len); -3928 qlog.header(&packet.header, Some(packet_number), path_id); -3929 -3930 self.process_decrypted_packet( -3931 now, -3932 network_path, -3933 path_id, -3934 Some(packet_number), -3935 packet, -3936 &mut qlog, -3937 )?; -3938 self.qlog.emit_packet_received(qlog, now); -3939 if let Some(data) = remaining { -3940 self.handle_coalesced(now, network_path, path_id, ecn, data); -3941 } -3942 -3943 self.qlog.emit_recovery_metrics( -3944 path_id, -3945 &mut self -3946 .paths -3947 .get_mut(&path_id) -3948 .expect("path_id was supplied by the caller for an active path") -3949 .data, -3950 now, -3951 ); -3952 -3953 Ok(()) -3954 } -3955 -3956 fn init_0rtt(&mut self, now: Instant) { -3957 let Some((header, packet)) = self.crypto_state.session.early_crypto() else { -3958 return; -3959 }; -3960 if self.side.is_client() { -3961 match self.crypto_state.session.transport_parameters() { -3962 Ok(params) => { -3963 let params = params -3964 .expect("crypto layer didn't supply transport parameters with ticket"); -3965 // Certain values must not be cached -3966 let params = TransportParameters { -3967 initial_src_cid: None, -3968 original_dst_cid: None, -3969 preferred_address: None, -3970 retry_src_cid: None, -3971 stateless_reset_token: None, -3972 min_ack_delay: None, -3973 ack_delay_exponent: TransportParameters::default().ack_delay_exponent, -3974 max_ack_delay: TransportParameters::default().max_ack_delay, -3975 initial_max_path_id: None, -3976 ..params -3977 }; -3978 self.set_peer_params(params); -3979 self.qlog.emit_peer_transport_params_restored(self, now); -3980 } -3981 Err(e) => { -3982 error!("session ticket has malformed transport parameters: {}", e); -3983 return; -3984 } -3985 } -3986 } -3987 trace!("0-RTT enabled"); -3988 self.crypto_state.enable_zero_rtt(header, packet); -3989 } -3990 -3991 fn read_crypto( -3992 &mut self, -3993 space: SpaceId, -3994 crypto: &frame::Crypto, -3995 payload_len: usize, -3996 ) -> Result<(), TransportError> { -3997 let expected = if !self.state.is_handshake() { -3998 SpaceId::Data -3999 } else if self.highest_space == SpaceKind::Initial { -4000 SpaceId::Initial -4001 } else { -4002 // On the server, self.highest_space can be Data after receiving the client's first -4003 // flight, but we expect Handshake CRYPTO until the handshake is complete. -4004 SpaceId::Handshake -4005 }; -4006 // We can't decrypt Handshake packets when highest_space is Initial, CRYPTO frames in 0-RTT -4007 // packets are illegal, and we don't process 1-RTT packets until the handshake is -4008 // complete. Therefore, we will never see CRYPTO data from a later-than-expected space. -4009 debug_assert!(space <= expected, "received out-of-order CRYPTO data"); -4010 -4011 let end = crypto.offset + crypto.data.len() as u64; -4012 if space < expected -4013 && end -4014 > self.crypto_state.spaces[space.kind()] -4015 .crypto_stream -4016 .bytes_read() -4017 { -4018 warn!( -4019 "received new {:?} CRYPTO data when expecting {:?}", -4020 space, expected -4021 ); -4022 return Err(TransportError::PROTOCOL_VIOLATION( -4023 "new data at unexpected encryption level", -4024 )); -4025 } -4026 -4027 let crypto_space = &mut self.crypto_state.spaces[space.kind()]; -4028 let max = end.saturating_sub(crypto_space.crypto_stream.bytes_read()); -4029 if max > self.config.crypto_buffer_size as u64 { -4030 return Err(TransportError::CRYPTO_BUFFER_EXCEEDED("")); -4031 } -4032 -4033 crypto_space -4034 .crypto_stream -4035 .insert(crypto.offset, crypto.data.clone(), payload_len); -4036 while let Some(chunk) = crypto_space.crypto_stream.read(usize::MAX, true) { -4037 trace!("consumed {} CRYPTO bytes", chunk.bytes.len()); -4038 if self.crypto_state.session.read_handshake(&chunk.bytes)? { -4039 self.events.push_back(Event::HandshakeDataReady); -4040 } -4041 } -4042 -4043 Ok(()) -4044 } -4045 -4046 fn write_crypto(&mut self) { -4047 loop { -4048 let space = self.highest_space; -4049 let mut outgoing = Vec::new(); -4050 if let Some(crypto) = self.crypto_state.session.write_handshake(&mut outgoing) { -4051 match space { -4052 SpaceKind::Initial => { -4053 self.upgrade_crypto(SpaceKind::Handshake, crypto); -4054 } -4055 SpaceKind::Handshake => { -4056 self.upgrade_crypto(SpaceKind::Data, crypto); -4057 } -4058 SpaceKind::Data => unreachable!("got updated secrets during 1-RTT"), -4059 } -4060 } -4061 if outgoing.is_empty() { -4062 if space == self.highest_space { -4063 break; -4064 } else { -4065 // Keys updated, check for more data to send -4066 continue; -4067 } -4068 } -4069 let offset = self.crypto_state.spaces[space].crypto_offset; -4070 let outgoing = Bytes::from(outgoing); -4071 if let Some(hs) = self.state.as_handshake_mut() -4072 && space == SpaceKind::Initial -4073 && offset == 0 -4074 && self.side.is_client() -4075 { -4076 hs.client_hello = Some(outgoing.clone()); -4077 } -4078 self.crypto_state.spaces[space].crypto_offset += outgoing.len() as u64; -4079 trace!("wrote {} {:?} CRYPTO bytes", outgoing.len(), space); -4080 self.spaces[space].pending.crypto.push_back(frame::Crypto { -4081 offset, -4082 data: outgoing, -4083 }); -4084 } -4085 } -4086 -4087 /// Switch to stronger cryptography during handshake -4088 fn upgrade_crypto(&mut self, space: SpaceKind, crypto: Keys) { -4089 debug_assert!( -4090 !self.crypto_state.has_keys(space.encryption_level()), -4091 "already reached packet space {space:?}" -4092 ); -4093 trace!("{:?} keys ready", space); -4094 if space == SpaceKind::Data { -4095 // Precompute the first key update -4096 self.crypto_state.next_crypto = Some( -4097 self.crypto_state -4098 .session -4099 .next_1rtt_keys() -4100 .expect("handshake should be complete"), -4101 ); -4102 } -4103 -4104 self.crypto_state.spaces[space].keys = Some(crypto); -4105 debug_assert!(space > self.highest_space); -4106 self.highest_space = space; -4107 if space == SpaceKind::Data && self.side.is_client() { -4108 // Discard 0-RTT keys because 1-RTT keys are available. -4109 self.crypto_state.discard_zero_rtt(); -4110 } -4111 } -4112 -4113 fn discard_space(&mut self, now: Instant, space: SpaceKind) { -4114 debug_assert!(space != SpaceKind::Data); -4115 trace!("discarding {:?} keys", space); -4116 if space == SpaceKind::Initial { -4117 // No longer needed -4118 if let ConnectionSide::Client { token, .. } = &mut self.side { -4119 *token = Bytes::new(); -4120 } -4121 } -4122 self.crypto_state.spaces[space].keys = None; -4123 let space = &mut self.spaces[space]; -4124 let pns = space.for_path(PathId::ZERO); -4125 pns.time_of_last_ack_eliciting_packet = None; -4126 pns.loss_time = None; -4127 pns.loss_probes = 0; -4128 let sent_packets = mem::take(&mut pns.sent_packets); -4129 let path = self -4130 .paths -4131 .get_mut(&PathId::ZERO) -4132 .expect("PathId::ZERO is alive while Initial/Handshake spaces exist"); -4133 for (_, packet) in sent_packets.into_iter() { -4134 path.data.remove_in_flight(&packet); -4135 } -4136 -4137 self.set_loss_detection_timer(now, PathId::ZERO) -4138 } -4139 -4140 fn handle_coalesced( -4141 &mut self, -4142 now: Instant, -4143 network_path: FourTuple, -4144 path_id: PathId, -4145 ecn: Option<EcnCodepoint>, -4146 data: BytesMut, -4147 ) { -4148 self.path_data_mut(path_id) -4149 .inc_total_recvd(data.len() as u64); -4150 let mut remaining = Some(data); -4151 let cid_len = self -4152 .local_cid_state -4153 .values() -4154 .map(|cid_state| cid_state.cid_len()) -4155 .next() -4156 .expect("one cid_state must exist"); -4157 while let Some(data) = remaining { -4158 match PartialDecode::new( -4159 data, -4160 &FixedLengthConnectionIdParser::new(cid_len), -4161 &[self.version], -4162 self.endpoint_config.grease_quic_bit, -4163 ) { -4164 Ok((partial_decode, rest)) => { -4165 remaining = rest; -4166 self.handle_decode(now, network_path, path_id, ecn, partial_decode); -4167 } -4168 Err(e) => { -4169 trace!("malformed header: {}", e); -4170 return; -4171 } -4172 } -4173 } -4174 } -4175 -4176 /// Decrypts the packet and processes the payload. -4177 /// -4178 /// Processes the entire packet, starting with removing header protection, then handling -4179 /// a stateless reset if needed, and decrypting and processing the frames in the payload -4180 /// if not a stateless reset. -4181 fn handle_decode( -4182 &mut self, -4183 now: Instant, -4184 network_path: FourTuple, -4185 path_id: PathId, -4186 ecn: Option<EcnCodepoint>, -4187 partial_decode: PartialDecode, -4188 ) { -4189 let qlog = QlogRecvPacket::new(partial_decode.len()); -4190 if let Some(decoded) = self -4191 .crypto_state -4192 .unprotect_header(partial_decode, self.peer_params.stateless_reset_token) -4193 { -4194 self.handle_packet( -4195 now, -4196 network_path, -4197 path_id, -4198 ecn, -4199 decoded.packet, -4200 decoded.stateless_reset, -4201 qlog, -4202 ); -4203 } -4204 } -4205 -4206 /// Handles a packet with header protection removed. -4207 /// -4208 /// The packet body is still encrypted at this point. -4209 /// -4210 /// If the datagram was a stateless reset we may have failed to remove header protection -4211 /// and thus `packet` may be `None`. -4212 fn handle_packet( -4213 &mut self, -4214 now: Instant, -4215 network_path: FourTuple, -4216 path_id: PathId, -4217 ecn: Option<EcnCodepoint>, -4218 packet: Option<Packet>, -4219 stateless_reset: bool, -4220 mut qlog: QlogRecvPacket, -4221 ) { -4222 self.path_stats.for_path(path_id).udp_rx.ios += 1; -4223 -4224 if let Some(ref packet) = packet { -4225 trace!( -4226 "got {:?} packet ({} bytes) from {} using id {}", -4227 packet.header.space(), -4228 packet.payload.len() + packet.header_data.len(), -4229 network_path, -4230 packet.header.dst_cid(), -4231 ); -4232 } -4233 -4234 let was_closed = self.state.is_closed(); -4235 let was_drained = self.state.is_drained(); -4236 -4237 // Now decrypt the packet payload in-place. -4238 let decrypted = match packet { -4239 None => Err(None), -4240 Some(mut packet) => self -4241 .decrypt_packet(now, path_id, &mut packet) -4242 .map(move |number| (packet, number)), -4243 }; -4244 let result = match decrypted { -4245 _ if stateless_reset => { -4246 debug!("got stateless reset"); -4247 Err(ConnectionError::Reset) -4248 } -4249 Err(Some(e)) => { -4250 warn!("illegal packet: {}", e); -4251 Err(e.into()) -4252 } -4253 Err(None) => { -4254 debug!("failed to authenticate packet"); -4255 self.authentication_failures += 1; -4256 let integrity_limit = self -4257 .crypto_state -4258 .integrity_limit(self.highest_space) -4259 .unwrap(); -4260 if self.authentication_failures > integrity_limit { -4261 Err(TransportError::AEAD_LIMIT_REACHED("integrity limit violated").into()) -4262 } else { -4263 return; -4264 } -4265 } -4266 Ok((packet, pn)) => { -4267 // We received an authenticated packet and decrypted it. -4268 qlog.header(&packet.header, pn, path_id); -4269 let span = match pn { -4270 Some(pn) => trace_span!("recv", space = ?packet.header.space(), pn), -4271 None => trace_span!("recv", space = ?packet.header.space()), -4272 }; -4273 let _guard = span.enter(); -4274 -4275 // Now the packet is authenticated we do the migration during the handshake, -4276 // see Handshake::allow_server_migration for details. Be careful here to -4277 // not yet rely on the path existing however, new paths are accepted and -4278 // created later. -4279 // Note that we can't do any other migrations yet, for those we need to know -4280 // whether this was a probing packet or not. See the end of -4281 // Self::process_packet for that. -4282 if self.is_handshaking() -4283 && self -4284 .path(path_id) -4285 .map(|path_data| { -4286 !path_data.network_path.is_probably_same_path(&network_path) -4287 }) -4288 .unwrap_or(false) -4289 { -4290 if let Some(hs) = self.state.as_handshake() -4291 && hs.allow_server_migration -4292 { -4293 trace!( -4294 %network_path, -4295 prev = %self.path_data(path_id).network_path, -4296 "server migrated to new remote", -4297 ); -4298 self.path_data_mut(path_id).network_path = network_path; -4299 self.qlog.emit_tuple_assigned(path_id, network_path, now); -4300 } else { -4301 debug!( -4302 recv_path = %network_path, -4303 expected_path = %self.path_data_mut(path_id).network_path, -4304 "discarding packet with unexpected remote during handshake", -4305 ); -4306 return; -4307 } -4308 } -4309 -4310 let dedup = self.spaces[packet.header.space()] -4311 .path_space_mut(path_id) -4312 .map(|pns| &mut pns.dedup); -4313 if pn.zip(dedup).is_some_and(|(n, d)| d.insert(n)) { -4314 debug!("discarding possible duplicate packet"); -4315 self.qlog.emit_packet_received(qlog, now); -4316 return; -4317 } else if self.state.is_handshake() && packet.header.is_short() { -4318 // TODO: SHOULD buffer these to improve reordering tolerance. -4319 trace!("dropping short packet during handshake"); -4320 self.qlog.emit_packet_received(qlog, now); -4321 return; -4322 } else { -4323 if let Header::Initial(InitialHeader { ref token, .. }) = packet.header -4324 && let Some(hs) = self.state.as_handshake() -4325 && self.side.is_server() -4326 && token != &hs.expected_token -4327 { -4328 // Clients must send the same retry token in every Initial. Initial -4329 // packets can be spoofed, so we discard rather than killing the -4330 // connection. -4331 warn!("discarding Initial with invalid retry token"); -4332 self.qlog.emit_packet_received(qlog, now); -4333 return; -4334 } -4335 -4336 if !self.state.is_closed() { -4337 let spin = match packet.header { -4338 Header::Short { spin, .. } => spin, -4339 _ => false, -4340 }; -4341 -4342 if self.side().is_server() && !self.abandoned_paths.contains(&path_id) { -4343 // Only the client is allowed to open paths -4344 self.ensure_path(path_id, network_path, now, pn); -4345 } -4346 if self.paths.contains_key(&path_id) { -4347 self.on_packet_authenticated( -4348 now, -4349 packet.header.space(), -4350 path_id, -4351 ecn, -4352 pn, -4353 spin, -4354 packet.header.is_1rtt(), -4355 &network_path, -4356 ); +3925 // The first packet is always on PathId::ZERO +3926 self.on_packet_authenticated( +3927 now, +3928 SpaceKind::Initial, +3929 path_id, +3930 ecn, +3931 Some(packet_number), +3932 false, +3933 false, +3934 &network_path, +3935 ); +3936 +3937 let packet: Packet = packet.into(); +3938 +3939 let mut qlog = QlogRecvPacket::new(len); +3940 qlog.header(&packet.header, Some(packet_number), path_id); +3941 +3942 self.process_decrypted_packet( +3943 now, +3944 network_path, +3945 path_id, +3946 Some(packet_number), +3947 packet, +3948 &mut qlog, +3949 )?; +3950 self.qlog.emit_packet_received(qlog, now); +3951 if let Some(data) = remaining { +3952 self.handle_coalesced(now, network_path, path_id, ecn, data); +3953 } +3954 +3955 self.qlog.emit_recovery_metrics( +3956 path_id, +3957 &mut self +3958 .paths +3959 .get_mut(&path_id) +3960 .expect("path_id was supplied by the caller for an active path") +3961 .data, +3962 now, +3963 ); +3964 +3965 Ok(()) +3966 } +3967 +3968 fn init_0rtt(&mut self, now: Instant) { +3969 let Some((header, packet)) = self.crypto_state.session.early_crypto() else { +3970 return; +3971 }; +3972 if self.side.is_client() { +3973 match self.crypto_state.session.transport_parameters() { +3974 Ok(params) => { +3975 let params = params +3976 .expect("crypto layer didn't supply transport parameters with ticket"); +3977 // Certain values must not be cached +3978 let params = TransportParameters { +3979 initial_src_cid: None, +3980 original_dst_cid: None, +3981 preferred_address: None, +3982 retry_src_cid: None, +3983 stateless_reset_token: None, +3984 min_ack_delay: None, +3985 ack_delay_exponent: TransportParameters::default().ack_delay_exponent, +3986 max_ack_delay: TransportParameters::default().max_ack_delay, +3987 initial_max_path_id: None, +3988 ..params +3989 }; +3990 self.set_peer_params(params); +3991 self.qlog.emit_peer_transport_params_restored(self, now); +3992 } +3993 Err(e) => { +3994 error!("session ticket has malformed transport parameters: {}", e); +3995 return; +3996 } +3997 } +3998 } +3999 trace!("0-RTT enabled"); +4000 self.crypto_state.enable_zero_rtt(header, packet); +4001 } +4002 +4003 fn read_crypto( +4004 &mut self, +4005 space: SpaceId, +4006 crypto: &frame::Crypto, +4007 payload_len: usize, +4008 ) -> Result<(), TransportError> { +4009 let expected = if !self.state.is_handshake() { +4010 SpaceId::Data +4011 } else if self.highest_space == SpaceKind::Initial { +4012 SpaceId::Initial +4013 } else { +4014 // On the server, self.highest_space can be Data after receiving the client's first +4015 // flight, but we expect Handshake CRYPTO until the handshake is complete. +4016 SpaceId::Handshake +4017 }; +4018 // We can't decrypt Handshake packets when highest_space is Initial, CRYPTO frames in 0-RTT +4019 // packets are illegal, and we don't process 1-RTT packets until the handshake is +4020 // complete. Therefore, we will never see CRYPTO data from a later-than-expected space. +4021 debug_assert!(space <= expected, "received out-of-order CRYPTO data"); +4022 +4023 let end = crypto.offset + crypto.data.len() as u64; +4024 if space < expected +4025 && end +4026 > self.crypto_state.spaces[space.kind()] +4027 .crypto_stream +4028 .bytes_read() +4029 { +4030 warn!( +4031 "received new {:?} CRYPTO data when expecting {:?}", +4032 space, expected +4033 ); +4034 return Err(TransportError::PROTOCOL_VIOLATION( +4035 "new data at unexpected encryption level", +4036 )); +4037 } +4038 +4039 let crypto_space = &mut self.crypto_state.spaces[space.kind()]; +4040 let max = end.saturating_sub(crypto_space.crypto_stream.bytes_read()); +4041 if max > self.config.crypto_buffer_size as u64 { +4042 return Err(TransportError::CRYPTO_BUFFER_EXCEEDED("")); +4043 } +4044 +4045 crypto_space +4046 .crypto_stream +4047 .insert(crypto.offset, crypto.data.clone(), payload_len); +4048 while let Some(chunk) = crypto_space.crypto_stream.read(usize::MAX, true) { +4049 trace!("consumed {} CRYPTO bytes", chunk.bytes.len()); +4050 if self.crypto_state.session.read_handshake(&chunk.bytes)? { +4051 self.events.push_back(Event::HandshakeDataReady); +4052 } +4053 } +4054 +4055 Ok(()) +4056 } +4057 +4058 fn write_crypto(&mut self) { +4059 loop { +4060 let space = self.highest_space; +4061 let mut outgoing = Vec::new(); +4062 if let Some(crypto) = self.crypto_state.session.write_handshake(&mut outgoing) { +4063 match space { +4064 SpaceKind::Initial => { +4065 self.upgrade_crypto(SpaceKind::Handshake, crypto); +4066 } +4067 SpaceKind::Handshake => { +4068 self.upgrade_crypto(SpaceKind::Data, crypto); +4069 } +4070 SpaceKind::Data => unreachable!("got updated secrets during 1-RTT"), +4071 } +4072 } +4073 if outgoing.is_empty() { +4074 if space == self.highest_space { +4075 break; +4076 } else { +4077 // Keys updated, check for more data to send +4078 continue; +4079 } +4080 } +4081 let offset = self.crypto_state.spaces[space].crypto_offset; +4082 let outgoing = Bytes::from(outgoing); +4083 if let Some(hs) = self.state.as_handshake_mut() +4084 && space == SpaceKind::Initial +4085 && offset == 0 +4086 && self.side.is_client() +4087 { +4088 hs.client_hello = Some(outgoing.clone()); +4089 } +4090 self.crypto_state.spaces[space].crypto_offset += outgoing.len() as u64; +4091 trace!("wrote {} {:?} CRYPTO bytes", outgoing.len(), space); +4092 self.spaces[space].pending.crypto.push_back(frame::Crypto { +4093 offset, +4094 data: outgoing, +4095 }); +4096 } +4097 } +4098 +4099 /// Switch to stronger cryptography during handshake +4100 fn upgrade_crypto(&mut self, space: SpaceKind, crypto: Keys) { +4101 debug_assert!( +4102 !self.crypto_state.has_keys(space.encryption_level()), +4103 "already reached packet space {space:?}" +4104 ); +4105 trace!("{:?} keys ready", space); +4106 if space == SpaceKind::Data { +4107 // Precompute the first key update +4108 self.crypto_state.next_crypto = Some( +4109 self.crypto_state +4110 .session +4111 .next_1rtt_keys() +4112 .expect("handshake should be complete"), +4113 ); +4114 } +4115 +4116 self.crypto_state.spaces[space].keys = Some(crypto); +4117 debug_assert!(space > self.highest_space); +4118 self.highest_space = space; +4119 if space == SpaceKind::Data && self.side.is_client() { +4120 // Discard 0-RTT keys because 1-RTT keys are available. +4121 self.crypto_state.discard_zero_rtt(); +4122 } +4123 } +4124 +4125 fn discard_space(&mut self, now: Instant, space: SpaceKind) { +4126 debug_assert!(space != SpaceKind::Data); +4127 trace!("discarding {:?} keys", space); +4128 if space == SpaceKind::Initial { +4129 // No longer needed +4130 if let ConnectionSide::Client { token, .. } = &mut self.side { +4131 *token = Bytes::new(); +4132 } +4133 } +4134 self.crypto_state.spaces[space].keys = None; +4135 let space = &mut self.spaces[space]; +4136 let pns = space.for_path(PathId::ZERO); +4137 pns.time_of_last_ack_eliciting_packet = None; +4138 pns.loss_time = None; +4139 pns.loss_probes = 0; +4140 let sent_packets = mem::take(&mut pns.sent_packets); +4141 let path = self +4142 .paths +4143 .get_mut(&PathId::ZERO) +4144 .expect("PathId::ZERO is alive while Initial/Handshake spaces exist"); +4145 for (_, packet) in sent_packets.into_iter() { +4146 path.data.remove_in_flight(&packet); +4147 } +4148 +4149 self.set_loss_detection_timer(now, PathId::ZERO) +4150 } +4151 +4152 fn handle_coalesced( +4153 &mut self, +4154 now: Instant, +4155 network_path: FourTuple, +4156 path_id: PathId, +4157 ecn: Option<EcnCodepoint>, +4158 data: BytesMut, +4159 ) { +4160 self.path_data_mut(path_id) +4161 .inc_total_recvd(data.len() as u64); +4162 let mut remaining = Some(data); +4163 let cid_len = self +4164 .local_cid_state +4165 .values() +4166 .map(|cid_state| cid_state.cid_len()) +4167 .next() +4168 .expect("one cid_state must exist"); +4169 while let Some(data) = remaining { +4170 match PartialDecode::new( +4171 data, +4172 &FixedLengthConnectionIdParser::new(cid_len), +4173 &[self.version], +4174 self.endpoint_config.grease_quic_bit, +4175 ) { +4176 Ok((partial_decode, rest)) => { +4177 remaining = rest; +4178 self.handle_decode(now, network_path, path_id, ecn, partial_decode); +4179 } +4180 Err(e) => { +4181 trace!("malformed header: {}", e); +4182 return; +4183 } +4184 } +4185 } +4186 } +4187 +4188 /// Decrypts the packet and processes the payload. +4189 /// +4190 /// Processes the entire packet, starting with removing header protection, then handling +4191 /// a stateless reset if needed, and decrypting and processing the frames in the payload +4192 /// if not a stateless reset. +4193 fn handle_decode( +4194 &mut self, +4195 now: Instant, +4196 network_path: FourTuple, +4197 path_id: PathId, +4198 ecn: Option<EcnCodepoint>, +4199 partial_decode: PartialDecode, +4200 ) { +4201 let qlog = QlogRecvPacket::new(partial_decode.len()); +4202 if let Some(decoded) = self +4203 .crypto_state +4204 .unprotect_header(partial_decode, self.peer_params.stateless_reset_token) +4205 { +4206 self.handle_packet( +4207 now, +4208 network_path, +4209 path_id, +4210 ecn, +4211 decoded.packet, +4212 decoded.stateless_reset, +4213 qlog, +4214 ); +4215 } +4216 } +4217 +4218 /// Handles a packet with header protection removed. +4219 /// +4220 /// The packet body is still encrypted at this point. +4221 /// +4222 /// If the datagram was a stateless reset we may have failed to remove header protection +4223 /// and thus `packet` may be `None`. +4224 fn handle_packet( +4225 &mut self, +4226 now: Instant, +4227 network_path: FourTuple, +4228 path_id: PathId, +4229 ecn: Option<EcnCodepoint>, +4230 packet: Option<Packet>, +4231 stateless_reset: bool, +4232 mut qlog: QlogRecvPacket, +4233 ) { +4234 self.path_stats.for_path(path_id).udp_rx.ios += 1; +4235 +4236 if let Some(ref packet) = packet { +4237 trace!( +4238 "got {:?} packet ({} bytes) from {} using id {}", +4239 packet.header.space(), +4240 packet.payload.len() + packet.header_data.len(), +4241 network_path, +4242 packet.header.dst_cid(), +4243 ); +4244 } +4245 +4246 let was_closed = self.state.is_closed(); +4247 let was_drained = self.state.is_drained(); +4248 +4249 // Now decrypt the packet payload in-place. +4250 let decrypted = match packet { +4251 None => Err(None), +4252 Some(mut packet) => self +4253 .decrypt_packet(now, path_id, &mut packet) +4254 .map(move |number| (packet, number)), +4255 }; +4256 let result = match decrypted { +4257 _ if stateless_reset => { +4258 debug!("got stateless reset"); +4259 Err(ConnectionError::Reset) +4260 } +4261 Err(Some(e)) => { +4262 warn!("illegal packet: {}", e); +4263 Err(e.into()) +4264 } +4265 Err(None) => { +4266 debug!("failed to authenticate packet"); +4267 self.authentication_failures += 1; +4268 let integrity_limit = self +4269 .crypto_state +4270 .integrity_limit(self.highest_space) +4271 .unwrap(); +4272 if self.authentication_failures > integrity_limit { +4273 Err(TransportError::AEAD_LIMIT_REACHED("integrity limit violated").into()) +4274 } else { +4275 return; +4276 } +4277 } +4278 Ok((packet, pn)) => { +4279 // We received an authenticated packet and decrypted it. +4280 qlog.header(&packet.header, pn, path_id); +4281 let span = match pn { +4282 Some(pn) => trace_span!("recv", space = ?packet.header.space(), pn), +4283 None => trace_span!("recv", space = ?packet.header.space()), +4284 }; +4285 let _guard = span.enter(); +4286 +4287 // Now the packet is authenticated we do the migration during the handshake, +4288 // see Handshake::allow_server_migration for details. Be careful here to +4289 // not yet rely on the path existing however, new paths are accepted and +4290 // created later. +4291 // Note that we can't do any other migrations yet, for those we need to know +4292 // whether this was a probing packet or not. See the end of +4293 // Self::process_packet for that. +4294 if self.is_handshaking() +4295 && self +4296 .path(path_id) +4297 .map(|path_data| { +4298 !path_data.network_path.is_probably_same_path(&network_path) +4299 }) +4300 .unwrap_or(false) +4301 { +4302 if let Some(hs) = self.state.as_handshake() +4303 && hs.allow_server_migration +4304 { +4305 trace!( +4306 %network_path, +4307 prev = %self.path_data(path_id).network_path, +4308 "server migrated to new remote", +4309 ); +4310 self.path_data_mut(path_id).network_path = network_path; +4311 self.qlog.emit_tuple_assigned(path_id, network_path, now); +4312 } else { +4313 debug!( +4314 recv_path = %network_path, +4315 expected_path = %self.path_data_mut(path_id).network_path, +4316 "discarding packet with unexpected remote during handshake", +4317 ); +4318 return; +4319 } +4320 } +4321 +4322 let dedup = self.spaces[packet.header.space()] +4323 .path_space_mut(path_id) +4324 .map(|pns| &mut pns.dedup); +4325 if pn.zip(dedup).is_some_and(|(n, d)| d.insert(n)) { +4326 debug!("discarding possible duplicate packet"); +4327 self.qlog.emit_packet_received(qlog, now); +4328 return; +4329 } else if self.state.is_handshake() && packet.header.is_short() { +4330 // TODO: SHOULD buffer these to improve reordering tolerance. +4331 trace!("dropping short packet during handshake"); +4332 self.qlog.emit_packet_received(qlog, now); +4333 return; +4334 } else { +4335 if let Header::Initial(InitialHeader { ref token, .. }) = packet.header +4336 && let Some(hs) = self.state.as_handshake() +4337 && self.side.is_server() +4338 && token != &hs.expected_token +4339 { +4340 // Clients must send the same retry token in every Initial. Initial +4341 // packets can be spoofed, so we discard rather than killing the +4342 // connection. +4343 warn!("discarding Initial with invalid retry token"); +4344 self.qlog.emit_packet_received(qlog, now); +4345 return; +4346 } +4347 +4348 if !self.state.is_closed() { +4349 let spin = match packet.header { +4350 Header::Short { spin, .. } => spin, +4351 _ => false, +4352 }; +4353 +4354 if self.side().is_server() && !self.abandoned_paths.contains(&path_id) { +4355 // Only the client is allowed to open paths +4356 self.ensure_path(path_id, network_path, now, pn); 4357 } -4358 } -4359 -4360 let res = self.process_decrypted_packet( -4361 now, -4362 network_path, -4363 path_id, -4364 pn, -4365 packet, -4366 &mut qlog, -4367 ); -4368 -4369 self.qlog.emit_packet_received(qlog, now); -4370 res -4371 } -4372 } -4373 }; -4374 -4375 // State transitions for error cases -4376 if let Err(conn_err) = result { -4377 match conn_err { -4378 ConnectionError::ApplicationClosed(reason) => self.state.move_to_closed(reason), -4379 ConnectionError::ConnectionClosed(reason) => self.state.move_to_closed(reason), -4380 ConnectionError::Reset -4381 | ConnectionError::TransportError(TransportError { -4382 code: TransportErrorCode::AEAD_LIMIT_REACHED, -4383 .. -4384 }) => { -4385 let was_draining = self.state.move_to_drained(Some(conn_err)); -4386 if !was_draining { -4387 self.endpoint_events.push_back(EndpointEventInner::Draining); -4388 } -4389 } -4390 ConnectionError::TimedOut => { -4391 unreachable!("timeouts aren't generated by packet processing"); -4392 } -4393 ConnectionError::TransportError(err) => { -4394 debug!("closing connection due to transport error: {}", err); -4395 self.state.move_to_closed(err); -4396 } -4397 ConnectionError::VersionMismatch => { -4398 self.state.move_to_draining(Some(conn_err)); -4399 self.endpoint_events.push_back(EndpointEventInner::Draining); -4400 } -4401 ConnectionError::LocallyClosed => { -4402 unreachable!("LocallyClosed isn't generated by packet processing"); -4403 } -4404 ConnectionError::CidsExhausted => { -4405 unreachable!("CidsExhausted isn't generated by packet processing"); -4406 } -4407 }; -4408 } -4409 -4410 if !was_closed && self.state.is_closed() { -4411 self.close_common(); -4412 if !self.state.is_drained() { -4413 self.set_close_timer(now); -4414 } -4415 } -4416 if !was_drained && self.state.is_drained() { -4417 self.endpoint_events.push_back(EndpointEventInner::Drained); -4418 // Close timer may have been started previously, e.g. if we sent a close and got a -4419 // stateless reset in response -4420 self.timers -4421 .stop(Timer::Conn(ConnTimer::Close), self.qlog.with_time(now)); -4422 } -4423 -4424 // Transmit CONNECTION_CLOSE if necessary. -4425 // -4426 // If we received a valid packet and we are in the closed state we should respond -4427 // with a CONNECTION_CLOSE frame. -4428 // TODO: This SHOULD be rate-limited according to §10.2.1 of QUIC-TRANSPORT, but -4429 // that does not yet happen. This is triggered by each received packet. -4430 if matches!(self.state.as_type(), StateType::Closed) { -4431 // From https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.1-7 -4432 // -4433 // While in the closing state we must either: -4434 // - discard packets coming from an un-validated remote OR -4435 // - ensure we do not send more than 3 times the received data -4436 // -4437 // Doing the 2nd would mean we would be able to send CONNECTION_CLOSE to a peer -4438 // who was (involuntary) migrated just at the time we initiated immediate -4439 // close. It is a lot more work though. So while we would like to do this for -4440 // now we only do 1. -4441 // -4442 // Another shortcoming of the current implementation is that when we have a -4443 // previous PathData which is validated and the remote matches that path, we -4444 // should schedule CONNECTION_CLOSE on that path. However currently we can not -4445 // schedule such a packet. We should also fix this some day. This makes us -4446 // vulnerable to an attacker faking a migration at the right time and then we'd -4447 // be unable to send the CONNECTION_CLOSE to the real remote. -4448 if self -4449 .paths -4450 .get(&path_id) -4451 .map(|p| p.data.validated && p.data.network_path == network_path) -4452 .unwrap_or(false) -4453 { -4454 self.connection_close_pending = true; -4455 } -4456 } -4457 } -4458 -4459 fn process_decrypted_packet( -4460 &mut self, -4461 now: Instant, -4462 network_path: FourTuple, -4463 path_id: PathId, -4464 number: Option<u64>, -4465 packet: Packet, -4466 qlog: &mut QlogRecvPacket, -4467 ) -> Result<(), ConnectionError> { -4468 if !self.paths.contains_key(&path_id) { -4469 // There is a chance this is a server side, first (for this path) packet, which would -4470 // be a protocol violation. It's more likely, however, that this is a packet of a -4471 // pruned path -4472 trace!(%path_id, ?number, "discarding packet for unknown path"); -4473 return Ok(()); -4474 } -4475 let state = match self.state.as_type() { -4476 StateType::Established => { -4477 match packet.header.space() { -4478 SpaceKind::Data => self.process_payload( -4479 now, -4480 network_path, -4481 path_id, -4482 number.unwrap(), -4483 packet, -4484 qlog, -4485 )?, -4486 _ if packet.header.has_frames() => { -4487 self.process_early_payload(now, path_id, packet, qlog)? -4488 } -4489 _ => { -4490 trace!("discarding unexpected pre-handshake packet"); -4491 } -4492 } -4493 return Ok(()); -4494 } -4495 StateType::Closed => { -4496 for result in frame::Iter::new(packet.payload.freeze())? { -4497 let frame = match result { -4498 Ok(frame) => frame, -4499 Err(err) => { -4500 debug!("frame decoding error: {err:?}"); -4501 continue; -4502 } -4503 }; -4504 qlog.frame(&frame); -4505 -4506 if let Frame::Padding = frame { -4507 continue; -4508 }; -4509 -4510 trace!(?frame, "processing frame in closed state"); -4511 -4512 self.path_stats -4513 .for_path(path_id) -4514 .frame_rx -4515 .record(frame.ty()); -4516 -4517 if let Frame::Close(_error) = frame { -4518 self.state.move_to_draining(None); -4519 self.endpoint_events.push_back(EndpointEventInner::Draining); -4520 break; -4521 } -4522 } -4523 return Ok(()); -4524 } -4525 StateType::Draining | StateType::Drained => return Ok(()), -4526 StateType::Handshake => self.state.as_handshake_mut().expect("checked"), -4527 }; +4358 if self.paths.contains_key(&path_id) { +4359 self.on_packet_authenticated( +4360 now, +4361 packet.header.space(), +4362 path_id, +4363 ecn, +4364 pn, +4365 spin, +4366 packet.header.is_1rtt(), +4367 &network_path, +4368 ); +4369 } +4370 } +4371 +4372 let res = self.process_decrypted_packet( +4373 now, +4374 network_path, +4375 path_id, +4376 pn, +4377 packet, +4378 &mut qlog, +4379 ); +4380 +4381 self.qlog.emit_packet_received(qlog, now); +4382 res +4383 } +4384 } +4385 }; +4386 +4387 // State transitions for error cases +4388 if let Err(conn_err) = result { +4389 match conn_err { +4390 ConnectionError::ApplicationClosed(reason) => self.state.move_to_closed(reason), +4391 ConnectionError::ConnectionClosed(reason) => self.state.move_to_closed(reason), +4392 ConnectionError::Reset +4393 | ConnectionError::TransportError(TransportError { +4394 code: TransportErrorCode::AEAD_LIMIT_REACHED, +4395 .. +4396 }) => { +4397 let was_draining = self.state.move_to_drained(Some(conn_err)); +4398 if !was_draining { +4399 self.endpoint_events.push_back(EndpointEventInner::Draining); +4400 } +4401 } +4402 ConnectionError::TimedOut => { +4403 unreachable!("timeouts aren't generated by packet processing"); +4404 } +4405 ConnectionError::TransportError(err) => { +4406 debug!("closing connection due to transport error: {}", err); +4407 self.state.move_to_closed(err); +4408 } +4409 ConnectionError::VersionMismatch => { +4410 self.state.move_to_draining(Some(conn_err)); +4411 self.endpoint_events.push_back(EndpointEventInner::Draining); +4412 } +4413 ConnectionError::LocallyClosed => { +4414 unreachable!("LocallyClosed isn't generated by packet processing"); +4415 } +4416 ConnectionError::CidsExhausted => { +4417 unreachable!("CidsExhausted isn't generated by packet processing"); +4418 } +4419 }; +4420 } +4421 +4422 if !was_closed && self.state.is_closed() { +4423 self.close_common(); +4424 if !self.state.is_drained() { +4425 self.set_close_timer(now); +4426 } +4427 } +4428 if !was_drained && self.state.is_drained() { +4429 self.endpoint_events.push_back(EndpointEventInner::Drained); +4430 // Close timer may have been started previously, e.g. if we sent a close and got a +4431 // stateless reset in response +4432 self.timers +4433 .stop(Timer::Conn(ConnTimer::Close), self.qlog.with_time(now)); +4434 } +4435 +4436 // Transmit CONNECTION_CLOSE if necessary. +4437 // +4438 // If we received a valid packet and we are in the closed state we should respond +4439 // with a CONNECTION_CLOSE frame. +4440 // TODO: This SHOULD be rate-limited according to §10.2.1 of QUIC-TRANSPORT, but +4441 // that does not yet happen. This is triggered by each received packet. +4442 if matches!(self.state.as_type(), StateType::Closed) { +4443 // From https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.1-7 +4444 // +4445 // While in the closing state we must either: +4446 // - discard packets coming from an un-validated remote OR +4447 // - ensure we do not send more than 3 times the received data +4448 // +4449 // Doing the 2nd would mean we would be able to send CONNECTION_CLOSE to a peer +4450 // who was (involuntary) migrated just at the time we initiated immediate +4451 // close. It is a lot more work though. So while we would like to do this for +4452 // now we only do 1. +4453 // +4454 // Another shortcoming of the current implementation is that when we have a +4455 // previous PathData which is validated and the remote matches that path, we +4456 // should schedule CONNECTION_CLOSE on that path. However currently we can not +4457 // schedule such a packet. We should also fix this some day. This makes us +4458 // vulnerable to an attacker faking a migration at the right time and then we'd +4459 // be unable to send the CONNECTION_CLOSE to the real remote. +4460 if self +4461 .paths +4462 .get(&path_id) +4463 .map(|p| p.data.validated && p.data.network_path == network_path) +4464 .unwrap_or(false) +4465 { +4466 self.connection_close_pending = true; +4467 } +4468 } +4469 } +4470 +4471 fn process_decrypted_packet( +4472 &mut self, +4473 now: Instant, +4474 network_path: FourTuple, +4475 path_id: PathId, +4476 number: Option<u64>, +4477 packet: Packet, +4478 qlog: &mut QlogRecvPacket, +4479 ) -> Result<(), ConnectionError> { +4480 if !self.paths.contains_key(&path_id) { +4481 // There is a chance this is a server side, first (for this path) packet, which would +4482 // be a protocol violation. It's more likely, however, that this is a packet of a +4483 // pruned path +4484 trace!(%path_id, ?number, "discarding packet for unknown path"); +4485 return Ok(()); +4486 } +4487 let state = match self.state.as_type() { +4488 StateType::Established => { +4489 match packet.header.space() { +4490 SpaceKind::Data => self.process_payload( +4491 now, +4492 network_path, +4493 path_id, +4494 number.unwrap(), +4495 packet, +4496 qlog, +4497 )?, +4498 _ if packet.header.has_frames() => { +4499 self.process_early_payload(now, path_id, packet, qlog)? +4500 } +4501 _ => { +4502 trace!("discarding unexpected pre-handshake packet"); +4503 } +4504 } +4505 return Ok(()); +4506 } +4507 StateType::Closed => { +4508 for result in frame::Iter::new(packet.payload.freeze())? { +4509 let frame = match result { +4510 Ok(frame) => frame, +4511 Err(err) => { +4512 debug!("frame decoding error: {err:?}"); +4513 continue; +4514 } +4515 }; +4516 qlog.frame(&frame); +4517 +4518 if let Frame::Padding = frame { +4519 continue; +4520 }; +4521 +4522 trace!(?frame, "processing frame in closed state"); +4523 +4524 self.path_stats +4525 .for_path(path_id) +4526 .frame_rx +4527 .record(frame.ty()); 4528 -4529 match packet.header { -4530 Header::Retry { -4531 src_cid: remote_cid, -4532 .. -4533 } => { -4534 debug_assert_eq!(path_id, PathId::ZERO); -4535 if self.side.is_server() { -4536 return Err(TransportError::PROTOCOL_VIOLATION("client sent Retry").into()); -4537 } -4538 -4539 let is_valid_retry = self -4540 .remote_cids -4541 .get(&path_id) -4542 .map(|cids| cids.active()) -4543 .map(|orig_dst_cid| { -4544 self.crypto_state.session.is_valid_retry( -4545 orig_dst_cid, -4546 &packet.header_data, -4547 &packet.payload, -4548 ) -4549 }) -4550 .unwrap_or_default(); -4551 if self.total_authed_packets > 1 -4552 || packet.payload.len() <= 16 // token + 16 byte tag -4553 || !is_valid_retry -4554 { -4555 trace!("discarding invalid Retry"); -4556 // - After the client has received and processed an Initial or Retry -4557 // packet from the server, it MUST discard any subsequent Retry -4558 // packets that it receives. -4559 // - A client MUST discard a Retry packet with a zero-length Retry Token -4560 // field. -4561 // - Clients MUST discard Retry packets that have a Retry Integrity Tag -4562 // that cannot be validated -4563 return Ok(()); -4564 } -4565 -4566 trace!("retrying with CID {}", remote_cid); -4567 let client_hello = state.client_hello.take().unwrap(); -4568 self.retry_src_cid = Some(remote_cid); -4569 self.remote_cids -4570 .get_mut(&path_id) -4571 .expect("PathId::ZERO not yet abandoned, is_valid_retry would have been false") -4572 .update_initial_cid(remote_cid); -4573 self.remote_handshake_cid = remote_cid; -4574 -4575 let space = &mut self.spaces[SpaceId::Initial]; -4576 if let Some(info) = space.for_path(PathId::ZERO).take(0) { -4577 self.on_packet_acked(now, PathId::ZERO, 0, info); -4578 }; -4579 -4580 self.discard_space(now, SpaceKind::Initial); // Make sure we clean up after -4581 // any retransmitted Initials -4582 let crypto_space = &mut self.crypto_state.spaces[SpaceKind::Initial]; -4583 crypto_space.keys = Some( -4584 self.crypto_state -4585 .session -4586 .initial_keys(remote_cid, self.side.side()), -4587 ); -4588 crypto_space.crypto_offset = client_hello.len() as u64; -4589 -4590 let next_pn = self.spaces[SpaceId::Initial] -4591 .for_path(path_id) -4592 .next_packet_number; -4593 self.spaces[SpaceId::Initial] = { -4594 let mut space = PacketSpace::new(now, SpaceId::Initial, &mut self.rng); -4595 space.for_path(path_id).next_packet_number = next_pn; -4596 space.pending.crypto.push_back(frame::Crypto { -4597 offset: 0, -4598 data: client_hello, -4599 }); -4600 space -4601 }; -4602 -4603 // Retransmit all 0-RTT data -4604 let zero_rtt = mem::take( -4605 &mut self.spaces[SpaceId::Data] -4606 .for_path(PathId::ZERO) -4607 .sent_packets, -4608 ); -4609 for (_, info) in zero_rtt.into_iter() { -4610 self.paths -4611 .get_mut(&PathId::ZERO) -4612 .unwrap() -4613 .remove_in_flight(&info); -4614 self.spaces[SpaceId::Data].pending |= info.retransmits; -4615 } -4616 self.streams.retransmit_all_for_0rtt(); -4617 -4618 let token_len = packet.payload.len() - 16; -4619 let ConnectionSide::Client { ref mut token, .. } = self.side else { -4620 unreachable!("we already short-circuited if we're server"); -4621 }; -4622 *token = packet.payload.freeze().split_to(token_len); -4623 -4624 self.state = State::handshake(state::Handshake { -4625 expected_token: Bytes::new(), -4626 remote_cid_set: false, -4627 client_hello: None, -4628 allow_server_migration: self.config.server_handshake_migration, -4629 }); -4630 Ok(()) -4631 } -4632 Header::Long { -4633 ty: LongType::Handshake, -4634 src_cid: remote_cid, -4635 dst_cid: local_cid, -4636 .. -4637 } => { -4638 debug_assert_eq!(path_id, PathId::ZERO); -4639 if remote_cid != self.remote_handshake_cid { -4640 debug!( -4641 "discarding packet with mismatched remote CID: {} != {}", -4642 self.remote_handshake_cid, remote_cid -4643 ); -4644 return Ok(()); -4645 } -4646 self.on_path_validated(path_id); -4647 -4648 self.process_early_payload(now, path_id, packet, qlog)?; -4649 if self.state.is_closed() { -4650 return Ok(()); -4651 } -4652 -4653 if self.crypto_state.session.is_handshaking() { -4654 trace!("handshake ongoing"); -4655 return Ok(()); -4656 } -4657 -4658 if self.side.is_client() { -4659 // Client-only because server params were set from the client's Initial -4660 let params = self -4661 .crypto_state -4662 .session -4663 .transport_parameters()? -4664 .ok_or_else(|| { -4665 TransportError::new( -4666 TransportErrorCode::crypto(0x6d), -4667 "transport parameters missing".to_owned(), -4668 ) -4669 })?; -4670 -4671 if self.has_0rtt() { -4672 if !self.crypto_state.session.early_data_accepted().unwrap() { -4673 debug_assert!(self.side.is_client()); -4674 debug!("0-RTT rejected"); -4675 self.crypto_state.accepted_0rtt = false; -4676 self.streams.zero_rtt_rejected(); -4677 -4678 // Discard already-queued frames -4679 self.spaces[SpaceId::Data].pending = Retransmits::default(); -4680 -4681 // Discard 0-RTT packets -4682 let sent_packets = mem::take( -4683 &mut self.spaces[SpaceId::Data].for_path(path_id).sent_packets, -4684 ); -4685 for (_, packet) in sent_packets.into_iter() { -4686 self.paths -4687 .get_mut(&path_id) -4688 .unwrap() -4689 .remove_in_flight(&packet); -4690 } -4691 } else { -4692 self.crypto_state.accepted_0rtt = true; -4693 params.validate_resumption_from(&self.peer_params)?; -4694 } -4695 } -4696 if let Some(token) = params.stateless_reset_token { -4697 let remote = self.path_data(path_id).network_path.remote; -4698 debug_assert!(!self.state.is_drained()); // requirement for endpoint events, checked above -4699 self.endpoint_events -4700 .push_back(EndpointEventInner::ResetToken(path_id, remote, token)); -4701 } -4702 self.handle_peer_params(params, local_cid, remote_cid, now)?; -4703 self.issue_first_cids(now); -4704 } else { -4705 // Server-only -4706 self.spaces[SpaceId::Data].pending.handshake_done = true; -4707 self.discard_space(now, SpaceKind::Handshake); -4708 self.events.push_back(Event::HandshakeConfirmed); -4709 trace!("handshake confirmed"); -4710 } -4711 -4712 self.events.push_back(Event::Connected); -4713 self.state.move_to_established(); -4714 trace!("established"); -4715 -4716 // Multipath can only be enabled after the state has reached Established. -4717 // So this can not happen any earlier. -4718 self.issue_first_path_cids(now); -4719 Ok(()) -4720 } -4721 Header::Initial(InitialHeader { -4722 src_cid: remote_cid, -4723 dst_cid: local_cid, -4724 .. -4725 }) => { -4726 debug_assert_eq!(path_id, PathId::ZERO); -4727 if !state.remote_cid_set { -4728 trace!("switching remote CID to {}", remote_cid); -4729 let mut state = state.clone(); -4730 self.remote_cids -4731 .get_mut(&path_id) -4732 .expect("PathId::ZERO not yet abandoned") -4733 .update_initial_cid(remote_cid); -4734 self.remote_handshake_cid = remote_cid; -4735 self.original_remote_cid = remote_cid; -4736 state.remote_cid_set = true; -4737 self.state.move_to_handshake(state); -4738 } else if remote_cid != self.remote_handshake_cid { -4739 debug!( -4740 "discarding packet with mismatched remote CID: {} != {}", -4741 self.remote_handshake_cid, remote_cid -4742 ); -4743 return Ok(()); -4744 } -4745 -4746 let starting_space = self.highest_space; -4747 self.process_early_payload(now, path_id, packet, qlog)?; -4748 -4749 if self.side.is_server() -4750 && starting_space == SpaceKind::Initial -4751 && self.highest_space != SpaceKind::Initial -4752 { -4753 let params = self -4754 .crypto_state -4755 .session -4756 .transport_parameters()? -4757 .ok_or_else(|| { -4758 TransportError::new( -4759 TransportErrorCode::crypto(0x6d), -4760 "transport parameters missing".to_owned(), -4761 ) -4762 })?; -4763 self.handle_peer_params(params, local_cid, remote_cid, now)?; -4764 self.issue_first_cids(now); -4765 self.init_0rtt(now); -4766 } -4767 Ok(()) -4768 } -4769 Header::Long { -4770 ty: LongType::ZeroRtt, -4771 .. -4772 } => { -4773 self.process_payload(now, network_path, path_id, number.unwrap(), packet, qlog)?; -4774 Ok(()) -4775 } -4776 Header::VersionNegotiate { .. } => { -4777 if self.total_authed_packets > 1 { -4778 return Ok(()); -4779 } -4780 let supported = packet -4781 .payload -4782 .chunks(4) -4783 .any(|x| match <[u8; 4]>::try_from(x) { -4784 Ok(version) => self.version == u32::from_be_bytes(version), -4785 Err(_) => false, -4786 }); -4787 if supported { -4788 return Ok(()); -4789 } -4790 debug!("remote doesn't support our version"); -4791 Err(ConnectionError::VersionMismatch) -4792 } -4793 Header::Short { .. } => unreachable!( -4794 "short packets received during handshake are discarded in handle_packet" -4795 ), -4796 } -4797 } -4798 -4799 /// Process an Initial or Handshake packet payload -4800 fn process_early_payload( -4801 &mut self, -4802 now: Instant, -4803 path_id: PathId, -4804 packet: Packet, -4805 #[allow(unused)] qlog: &mut QlogRecvPacket, -4806 ) -> Result<(), TransportError> { -4807 debug_assert_ne!(packet.header.space(), SpaceKind::Data); -4808 debug_assert_eq!(path_id, PathId::ZERO); -4809 let payload_len = packet.payload.len(); -4810 let mut ack_eliciting = false; -4811 for result in frame::Iter::new(packet.payload.freeze())? { -4812 let frame = result?; -4813 qlog.frame(&frame); -4814 let span = match frame { -4815 Frame::Padding => continue, -4816 _ => Some(trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty)), -4817 }; -4818 -4819 self.path_stats -4820 .for_path(path_id) -4821 .frame_rx -4822 .record(frame.ty()); -4823 -4824 let _guard = span.as_ref().map(|x| x.enter()); -4825 ack_eliciting |= frame.is_ack_eliciting(); -4826 -4827 // Process frames -4828 if frame.is_1rtt() && packet.header.space() != SpaceKind::Data { -4829 return Err(TransportError::PROTOCOL_VIOLATION( -4830 "illegal frame type in handshake", -4831 )); -4832 } -4833 -4834 match frame { -4835 Frame::Padding | Frame::Ping => {} -4836 Frame::Crypto(frame) => { -4837 self.read_crypto(packet.header.space().into(), &frame, payload_len)?; -4838 } -4839 Frame::Ack(ack) => { -4840 self.on_ack_received(now, packet.header.space().into(), ack)?; -4841 } -4842 Frame::PathAck(ack) => { -4843 span.as_ref() -4844 .map(|span| span.record("path", tracing::field::display(&ack.path_id))); -4845 self.on_path_ack_received(now, packet.header.space().into(), ack)?; -4846 } -4847 Frame::Close(reason) => { -4848 self.state.move_to_draining(Some(reason.into())); -4849 self.endpoint_events.push_back(EndpointEventInner::Draining); -4850 return Ok(()); -4851 } -4852 _ => { -4853 let mut err = -4854 TransportError::PROTOCOL_VIOLATION("illegal frame type in handshake"); -4855 err.frame = frame::MaybeFrame::Known(frame.ty()); -4856 return Err(err); -4857 } -4858 } -4859 } -4860 -4861 if ack_eliciting { -4862 // In the initial and handshake spaces, ACKs must be sent immediately -4863 self.spaces[packet.header.space()] -4864 .for_path(path_id) -4865 .pending_acks -4866 .set_immediate_ack_required(); -4867 } -4868 -4869 self.write_crypto(); -4870 Ok(()) -4871 } +4529 if let Frame::Close(_error) = frame { +4530 self.state.move_to_draining(None); +4531 self.endpoint_events.push_back(EndpointEventInner::Draining); +4532 break; +4533 } +4534 } +4535 return Ok(()); +4536 } +4537 StateType::Draining | StateType::Drained => return Ok(()), +4538 StateType::Handshake => self.state.as_handshake_mut().expect("checked"), +4539 }; +4540 +4541 match packet.header { +4542 Header::Retry { +4543 src_cid: remote_cid, +4544 .. +4545 } => { +4546 debug_assert_eq!(path_id, PathId::ZERO); +4547 if self.side.is_server() { +4548 return Err(TransportError::PROTOCOL_VIOLATION("client sent Retry").into()); +4549 } +4550 +4551 let is_valid_retry = self +4552 .remote_cids +4553 .get(&path_id) +4554 .map(|cids| cids.active()) +4555 .map(|orig_dst_cid| { +4556 self.crypto_state.session.is_valid_retry( +4557 orig_dst_cid, +4558 &packet.header_data, +4559 &packet.payload, +4560 ) +4561 }) +4562 .unwrap_or_default(); +4563 if self.total_authed_packets > 1 +4564 || packet.payload.len() <= 16 // token + 16 byte tag +4565 || !is_valid_retry +4566 { +4567 trace!("discarding invalid Retry"); +4568 // - After the client has received and processed an Initial or Retry +4569 // packet from the server, it MUST discard any subsequent Retry +4570 // packets that it receives. +4571 // - A client MUST discard a Retry packet with a zero-length Retry Token +4572 // field. +4573 // - Clients MUST discard Retry packets that have a Retry Integrity Tag +4574 // that cannot be validated +4575 return Ok(()); +4576 } +4577 +4578 trace!("retrying with CID {}", remote_cid); +4579 let client_hello = state.client_hello.take().unwrap(); +4580 self.retry_src_cid = Some(remote_cid); +4581 self.remote_cids +4582 .get_mut(&path_id) +4583 .expect("PathId::ZERO not yet abandoned, is_valid_retry would have been false") +4584 .update_initial_cid(remote_cid); +4585 self.remote_handshake_cid = remote_cid; +4586 +4587 let space = &mut self.spaces[SpaceId::Initial]; +4588 if let Some(info) = space.for_path(PathId::ZERO).take(0) { +4589 self.on_packet_acked(now, PathId::ZERO, 0, info); +4590 }; +4591 +4592 self.discard_space(now, SpaceKind::Initial); // Make sure we clean up after +4593 // any retransmitted Initials +4594 let crypto_space = &mut self.crypto_state.spaces[SpaceKind::Initial]; +4595 crypto_space.keys = Some( +4596 self.crypto_state +4597 .session +4598 .initial_keys(remote_cid, self.side.side()), +4599 ); +4600 crypto_space.crypto_offset = client_hello.len() as u64; +4601 +4602 let next_pn = self.spaces[SpaceId::Initial] +4603 .for_path(path_id) +4604 .next_packet_number; +4605 self.spaces[SpaceId::Initial] = { +4606 let mut space = PacketSpace::new(now, SpaceId::Initial, &mut self.rng); +4607 space.for_path(path_id).next_packet_number = next_pn; +4608 space.pending.crypto.push_back(frame::Crypto { +4609 offset: 0, +4610 data: client_hello, +4611 }); +4612 space +4613 }; +4614 +4615 // Retransmit all 0-RTT data +4616 let zero_rtt = mem::take( +4617 &mut self.spaces[SpaceId::Data] +4618 .for_path(PathId::ZERO) +4619 .sent_packets, +4620 ); +4621 for (_, info) in zero_rtt.into_iter() { +4622 self.paths +4623 .get_mut(&PathId::ZERO) +4624 .unwrap() +4625 .remove_in_flight(&info); +4626 self.spaces[SpaceId::Data].pending |= info.retransmits; +4627 } +4628 self.streams.retransmit_all_for_0rtt(); +4629 +4630 let token_len = packet.payload.len() - 16; +4631 let ConnectionSide::Client { ref mut token, .. } = self.side else { +4632 unreachable!("we already short-circuited if we're server"); +4633 }; +4634 *token = packet.payload.freeze().split_to(token_len); +4635 +4636 self.state = State::handshake(state::Handshake { +4637 expected_token: Bytes::new(), +4638 remote_cid_set: false, +4639 client_hello: None, +4640 allow_server_migration: self.config.server_handshake_migration, +4641 }); +4642 Ok(()) +4643 } +4644 Header::Long { +4645 ty: LongType::Handshake, +4646 src_cid: remote_cid, +4647 dst_cid: local_cid, +4648 .. +4649 } => { +4650 debug_assert_eq!(path_id, PathId::ZERO); +4651 if remote_cid != self.remote_handshake_cid { +4652 debug!( +4653 "discarding packet with mismatched remote CID: {} != {}", +4654 self.remote_handshake_cid, remote_cid +4655 ); +4656 return Ok(()); +4657 } +4658 self.on_path_validated(path_id); +4659 +4660 self.process_early_payload(now, path_id, packet, qlog)?; +4661 if self.state.is_closed() { +4662 return Ok(()); +4663 } +4664 +4665 if self.crypto_state.session.is_handshaking() { +4666 trace!("handshake ongoing"); +4667 return Ok(()); +4668 } +4669 +4670 if self.side.is_client() { +4671 // Client-only because server params were set from the client's Initial +4672 let params = self +4673 .crypto_state +4674 .session +4675 .transport_parameters()? +4676 .ok_or_else(|| { +4677 TransportError::new( +4678 TransportErrorCode::crypto(0x6d), +4679 "transport parameters missing".to_owned(), +4680 ) +4681 })?; +4682 +4683 if self.has_0rtt() { +4684 if !self.crypto_state.session.early_data_accepted().unwrap() { +4685 debug_assert!(self.side.is_client()); +4686 debug!("0-RTT rejected"); +4687 self.crypto_state.accepted_0rtt = false; +4688 self.streams.zero_rtt_rejected(); +4689 +4690 // Discard already-queued frames +4691 self.spaces[SpaceId::Data].pending = Retransmits::default(); +4692 +4693 // Discard 0-RTT packets +4694 let sent_packets = mem::take( +4695 &mut self.spaces[SpaceId::Data].for_path(path_id).sent_packets, +4696 ); +4697 for (_, packet) in sent_packets.into_iter() { +4698 self.paths +4699 .get_mut(&path_id) +4700 .unwrap() +4701 .remove_in_flight(&packet); +4702 } +4703 } else { +4704 self.crypto_state.accepted_0rtt = true; +4705 params.validate_resumption_from(&self.peer_params)?; +4706 } +4707 } +4708 if let Some(token) = params.stateless_reset_token { +4709 let remote = self.path_data(path_id).network_path.remote; +4710 debug_assert!(!self.state.is_drained()); // requirement for endpoint events, checked above +4711 self.endpoint_events +4712 .push_back(EndpointEventInner::ResetToken(path_id, remote, token)); +4713 } +4714 self.handle_peer_params(params, local_cid, remote_cid, now)?; +4715 self.issue_first_cids(now); +4716 } else { +4717 // Server-only +4718 self.spaces[SpaceId::Data].pending.handshake_done = true; +4719 self.discard_space(now, SpaceKind::Handshake); +4720 self.events.push_back(Event::HandshakeConfirmed); +4721 trace!("handshake confirmed"); +4722 } +4723 +4724 self.events.push_back(Event::Connected); +4725 self.state.move_to_established(); +4726 trace!("established"); +4727 +4728 // Multipath can only be enabled after the state has reached Established. +4729 // So this can not happen any earlier. +4730 self.issue_first_path_cids(now); +4731 Ok(()) +4732 } +4733 Header::Initial(InitialHeader { +4734 src_cid: remote_cid, +4735 dst_cid: local_cid, +4736 .. +4737 }) => { +4738 debug_assert_eq!(path_id, PathId::ZERO); +4739 if !state.remote_cid_set { +4740 trace!("switching remote CID to {}", remote_cid); +4741 let mut state = state.clone(); +4742 self.remote_cids +4743 .get_mut(&path_id) +4744 .expect("PathId::ZERO not yet abandoned") +4745 .update_initial_cid(remote_cid); +4746 self.remote_handshake_cid = remote_cid; +4747 self.original_remote_cid = remote_cid; +4748 state.remote_cid_set = true; +4749 self.state.move_to_handshake(state); +4750 } else if remote_cid != self.remote_handshake_cid { +4751 debug!( +4752 "discarding packet with mismatched remote CID: {} != {}", +4753 self.remote_handshake_cid, remote_cid +4754 ); +4755 return Ok(()); +4756 } +4757 +4758 let starting_space = self.highest_space; +4759 self.process_early_payload(now, path_id, packet, qlog)?; +4760 +4761 if self.side.is_server() +4762 && starting_space == SpaceKind::Initial +4763 && self.highest_space != SpaceKind::Initial +4764 { +4765 let params = self +4766 .crypto_state +4767 .session +4768 .transport_parameters()? +4769 .ok_or_else(|| { +4770 TransportError::new( +4771 TransportErrorCode::crypto(0x6d), +4772 "transport parameters missing".to_owned(), +4773 ) +4774 })?; +4775 self.handle_peer_params(params, local_cid, remote_cid, now)?; +4776 self.issue_first_cids(now); +4777 self.init_0rtt(now); +4778 } +4779 Ok(()) +4780 } +4781 Header::Long { +4782 ty: LongType::ZeroRtt, +4783 .. +4784 } => { +4785 self.process_payload(now, network_path, path_id, number.unwrap(), packet, qlog)?; +4786 Ok(()) +4787 } +4788 Header::VersionNegotiate { .. } => { +4789 if self.total_authed_packets > 1 { +4790 return Ok(()); +4791 } +4792 let supported = packet +4793 .payload +4794 .chunks(4) +4795 .any(|x| match <[u8; 4]>::try_from(x) { +4796 Ok(version) => self.version == u32::from_be_bytes(version), +4797 Err(_) => false, +4798 }); +4799 if supported { +4800 return Ok(()); +4801 } +4802 debug!("remote doesn't support our version"); +4803 Err(ConnectionError::VersionMismatch) +4804 } +4805 Header::Short { .. } => unreachable!( +4806 "short packets received during handshake are discarded in handle_packet" +4807 ), +4808 } +4809 } +4810 +4811 /// Process an Initial or Handshake packet payload +4812 fn process_early_payload( +4813 &mut self, +4814 now: Instant, +4815 path_id: PathId, +4816 packet: Packet, +4817 #[allow(unused)] qlog: &mut QlogRecvPacket, +4818 ) -> Result<(), TransportError> { +4819 debug_assert_ne!(packet.header.space(), SpaceKind::Data); +4820 debug_assert_eq!(path_id, PathId::ZERO); +4821 let payload_len = packet.payload.len(); +4822 let mut ack_eliciting = false; +4823 for result in frame::Iter::new(packet.payload.freeze())? { +4824 let frame = result?; +4825 qlog.frame(&frame); +4826 let span = match frame { +4827 Frame::Padding => continue, +4828 _ => Some(trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty)), +4829 }; +4830 +4831 self.path_stats +4832 .for_path(path_id) +4833 .frame_rx +4834 .record(frame.ty()); +4835 +4836 let _guard = span.as_ref().map(|x| x.enter()); +4837 ack_eliciting |= frame.is_ack_eliciting(); +4838 +4839 // Process frames +4840 if frame.is_1rtt() && packet.header.space() != SpaceKind::Data { +4841 return Err(TransportError::PROTOCOL_VIOLATION( +4842 "illegal frame type in handshake", +4843 )); +4844 } +4845 +4846 match frame { +4847 Frame::Padding | Frame::Ping => {} +4848 Frame::Crypto(frame) => { +4849 self.read_crypto(packet.header.space().into(), &frame, payload_len)?; +4850 } +4851 Frame::Ack(ack) => { +4852 self.on_ack_received(now, packet.header.space().into(), ack)?; +4853 } +4854 Frame::PathAck(ack) => { +4855 span.as_ref() +4856 .map(|span| span.record("path", tracing::field::display(&ack.path_id))); +4857 self.on_path_ack_received(now, packet.header.space().into(), ack)?; +4858 } +4859 Frame::Close(reason) => { +4860 self.state.move_to_draining(Some(reason.into())); +4861 self.endpoint_events.push_back(EndpointEventInner::Draining); +4862 return Ok(()); +4863 } +4864 _ => { +4865 let mut err = +4866 TransportError::PROTOCOL_VIOLATION("illegal frame type in handshake"); +4867 err.frame = frame::MaybeFrame::Known(frame.ty()); +4868 return Err(err); +4869 } +4870 } +4871 } 4872 -4873 /// Processes the decrypted packet payload, always in the data space. -4874 fn process_payload( -4875 &mut self, -4876 now: Instant, -4877 network_path: FourTuple, -4878 path_id: PathId, -4879 number: u64, -4880 packet: Packet, -4881 #[allow(unused)] qlog: &mut QlogRecvPacket, -4882 ) -> Result<(), TransportError> { -4883 let is_multipath_negotiated = self.is_multipath_negotiated(); -4884 let payload = packet.payload.freeze(); -4885 let mut is_probing_packet = true; -4886 let mut close = None; -4887 let payload_len = payload.len(); -4888 let mut ack_eliciting = false; -4889 // if this packet triggers a path migration and includes a observed address frame, it's -4890 // stored here -4891 let mut migration_observed_addr = None; -4892 for result in frame::Iter::new(payload)? { -4893 let frame = result?; -4894 qlog.frame(&frame); -4895 let span = match frame { -4896 Frame::Padding => continue, -4897 _ => trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty), -4898 }; -4899 -4900 self.path_stats -4901 .for_path(path_id) -4902 .frame_rx -4903 .record(frame.ty()); -4904 // Crypto, Stream and Datagram frames are special cased in order no pollute -4905 // the log with payload data -4906 match &frame { -4907 Frame::Crypto(f) => { -4908 trace!(offset = f.offset, len = f.data.len(), "got frame CRYPTO"); -4909 } -4910 Frame::Stream(f) => { -4911 trace!(id = %f.id, offset = f.offset, len = f.data.len(), fin = f.fin, "got frame STREAM"); -4912 } -4913 Frame::Datagram(f) => { -4914 trace!(len = f.data.len(), "got frame DATAGRAM"); -4915 } -4916 f => { -4917 trace!("got frame {f}"); -4918 } -4919 } -4920 -4921 let _guard = span.enter(); -4922 if packet.header.is_0rtt() { -4923 match frame { -4924 Frame::Crypto(_) | Frame::Close(Close::Application(_)) => { -4925 return Err(TransportError::PROTOCOL_VIOLATION( -4926 "illegal frame type in 0-RTT", -4927 )); -4928 } -4929 _ => { -4930 if frame.is_1rtt() { -4931 return Err(TransportError::PROTOCOL_VIOLATION( -4932 "illegal frame type in 0-RTT", -4933 )); -4934 } -4935 } -4936 } -4937 } -4938 ack_eliciting |= frame.is_ack_eliciting(); -4939 -4940 // Check whether this could be a probing packet -4941 match frame { -4942 Frame::Padding -4943 | Frame::PathChallenge(_) -4944 | Frame::PathResponse(_) -4945 | Frame::NewConnectionId(_) -4946 | Frame::ObservedAddr(_) => {} -4947 _ => { -4948 is_probing_packet = false; -4949 } -4950 } -4951 -4952 match frame { -4953 Frame::Crypto(frame) => { -4954 self.read_crypto(SpaceId::Data, &frame, payload_len)?; -4955 } -4956 Frame::Stream(frame) => { -4957 if self.streams.received(frame, payload_len)?.should_transmit() { -4958 self.spaces[SpaceId::Data].pending.max_data = true; -4959 } +4873 if ack_eliciting { +4874 // In the initial and handshake spaces, ACKs must be sent immediately +4875 self.spaces[packet.header.space()] +4876 .for_path(path_id) +4877 .pending_acks +4878 .set_immediate_ack_required(); +4879 } +4880 +4881 self.write_crypto(); +4882 Ok(()) +4883 } +4884 +4885 /// Processes the decrypted packet payload, always in the data space. +4886 fn process_payload( +4887 &mut self, +4888 now: Instant, +4889 network_path: FourTuple, +4890 path_id: PathId, +4891 number: u64, +4892 packet: Packet, +4893 #[allow(unused)] qlog: &mut QlogRecvPacket, +4894 ) -> Result<(), TransportError> { +4895 let payload = packet.payload.freeze(); +4896 let mut is_probing_packet = true; +4897 let mut close = None; +4898 let payload_len = payload.len(); +4899 let mut ack_eliciting = false; +4900 // if this packet triggers a path migration and includes a observed address frame, it's +4901 // stored here +4902 let mut migration_observed_addr = None; +4903 for result in frame::Iter::new(payload)? { +4904 let frame = result?; +4905 qlog.frame(&frame); +4906 let span = match frame { +4907 Frame::Padding => continue, +4908 _ => trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty), +4909 }; +4910 +4911 self.path_stats +4912 .for_path(path_id) +4913 .frame_rx +4914 .record(frame.ty()); +4915 // Crypto, Stream and Datagram frames are special cased in order no pollute +4916 // the log with payload data +4917 match &frame { +4918 Frame::Crypto(f) => { +4919 trace!(offset = f.offset, len = f.data.len(), "got frame CRYPTO"); +4920 } +4921 Frame::Stream(f) => { +4922 trace!(id = %f.id, offset = f.offset, len = f.data.len(), fin = f.fin, "got frame STREAM"); +4923 } +4924 Frame::Datagram(f) => { +4925 trace!(len = f.data.len(), "got frame DATAGRAM"); +4926 } +4927 f => { +4928 trace!("got frame {f}"); +4929 } +4930 } +4931 +4932 let _guard = span.enter(); +4933 if packet.header.is_0rtt() { +4934 match frame { +4935 Frame::Crypto(_) | Frame::Close(Close::Application(_)) => { +4936 return Err(TransportError::PROTOCOL_VIOLATION( +4937 "illegal frame type in 0-RTT", +4938 )); +4939 } +4940 _ => { +4941 if frame.is_1rtt() { +4942 return Err(TransportError::PROTOCOL_VIOLATION( +4943 "illegal frame type in 0-RTT", +4944 )); +4945 } +4946 } +4947 } +4948 } +4949 ack_eliciting |= frame.is_ack_eliciting(); +4950 +4951 // Check whether this could be a probing packet +4952 match frame { +4953 Frame::Padding +4954 | Frame::PathChallenge(_) +4955 | Frame::PathResponse(_) +4956 | Frame::NewConnectionId(_) +4957 | Frame::ObservedAddr(_) => {} +4958 _ => { +4959 is_probing_packet = false; 4960 } -4961 Frame::Ack(ack) => { -4962 self.on_ack_received(now, SpaceId::Data, ack)?; -4963 } -4964 Frame::PathAck(ack) => { -4965 if !self.is_multipath_negotiated() { -4966 return Err(TransportError::PROTOCOL_VIOLATION( -4967 "received PATH_ACK frame when multipath was not negotiated", -4968 )); -4969 } -4970 span.record("path", tracing::field::display(&ack.path_id)); -4971 self.on_path_ack_received(now, SpaceId::Data, ack)?; -4972 } -4973 Frame::Padding | Frame::Ping => {} -4974 Frame::Close(reason) => { -4975 close = Some(reason); -4976 } -4977 Frame::PathChallenge(challenge) => { -4978 let path = &mut self -4979 .path_mut(path_id) -4980 .expect("payload is processed only after the path becomes known"); -4981 path.path_responses.push(number, challenge.0, network_path); -4982 // If we were passively migrated (e.g. NAT rebinding), our local_ip will -4983 // not match. Once we processed a non-probing packet the local_ip will -4984 // finally be updated. -4985 if network_path.remote == path.network_path.remote { -4986 // PATH_CHALLENGE on active path, possible off-path packet -4987 // forwarding attack. Send a non-probing packet to recover the -4988 // active path. See -4989 // https://www.rfc-editor.org/rfc/rfc9000.html#section-9.3.3-3. In -4990 // rare cases NAT probes might also appear on-path and would also -4991 // get a non-probing packet as response. There is little harm in -4992 // this. -4993 match self.peer_supports_ack_frequency() { -4994 true => self.immediate_ack(path_id), -4995 false => { -4996 self.ping_path(path_id).ok(); -4997 } -4998 } -4999 } -5000 } -5001 Frame::PathResponse(response) => { -5002 // First try to see if this is a NAT probe response. -5003 if self -5004 .n0_nat_traversal -5005 .handle_path_response(network_path, response.0) -5006 { -5007 self.open_nat_traversed_paths(now); -5008 } else { -5009 // Try to see if this is a response to an on-path PATH_CHALLENGE. -5010 -5011 let path = self -5012 .paths -5013 .get_mut(&path_id) -5014 .expect("payload is processed only after the path becomes known"); -5015 -5016 use PathTimer::*; -5017 use paths::OnPathResponseReceived::*; -5018 match path -5019 .data -5020 .on_path_response_received(now, response.0, network_path) -5021 { -5022 OnPath { was_open } if !self.abandoned_paths.contains(&path_id) => { -5023 let qlog = self.qlog.with_time(now); -5024 -5025 self.timers.stop( -5026 Timer::PerPath(path_id, PathValidationFailed), -5027 qlog.clone(), -5028 ); -5029 self.timers.stop( -5030 Timer::PerPath(path_id, AbandonFromValidation), -5031 qlog.clone(), -5032 ); -5033 -5034 let next_challenge = path -5035 .data -5036 .earliest_on_path_expiring_challenge() -5037 .map(|time| time + self.ack_frequency.max_ack_delay_for_pto()); -5038 self.timers.set_or_stop( -5039 Timer::PerPath(path_id, PathChallengeLost), -5040 next_challenge, -5041 qlog, -5042 ); -5043 -5044 if !was_open { -5045 if is_multipath_negotiated { -5046 self.events.push_back(Event::Path( -5047 PathEvent::Established { id: path_id }, -5048 )); -5049 } -5050 if let Some(observed) = -5051 path.data.last_observed_addr_report.as_ref() -5052 { -5053 self.events.push_back(Event::Path( -5054 PathEvent::ObservedAddr { -5055 id: path_id, -5056 addr: observed.socket_addr(), -5057 }, -5058 )); -5059 } -5060 } -5061 if let Some((_, ref mut prev)) = path.prev { -5062 // If an on-path response was received while there is a -5063 // previous path from a migration, then the new path is -5064 // validated and we can stop sending challenges that try to -5065 // re-validate the previous path. -5066 prev.reset_on_path_challenges(); -5067 } -5068 } -5069 OnPath { .. } => { -5070 trace!( -5071 %response, -5072 "ignoring PATH_RESPONSE received after path is abandoned" -5073 ); -5074 } -5075 Ignored { -5076 sent_on, -5077 current_path, -5078 } => { -5079 debug!(%sent_on, %current_path, %response, "ignoring valid PATH_RESPONSE") -5080 } -5081 Unknown => debug!(%response, "ignoring invalid PATH_RESPONSE"), -5082 } -5083 } -5084 } -5085 Frame::MaxData(frame::MaxData(bytes)) => { -5086 self.streams.received_max_data(bytes); -5087 } -5088 Frame::MaxStreamData(frame::MaxStreamData { id, offset }) => { -5089 self.streams.received_max_stream_data(id, offset)?; -5090 } -5091 Frame::MaxStreams(frame::MaxStreams { dir, count }) => { -5092 self.streams.received_max_streams(dir, count)?; -5093 } -5094 Frame::ResetStream(frame) => { -5095 if self.streams.received_reset(frame)?.should_transmit() { -5096 self.spaces[SpaceId::Data].pending.max_data = true; -5097 } -5098 } -5099 Frame::DataBlocked(DataBlocked(offset)) => { -5100 debug!(offset, "peer claims to be blocked at connection level"); -5101 } -5102 Frame::StreamDataBlocked(StreamDataBlocked { id, offset }) => { -5103 if id.initiator() == self.side.side() && id.dir() == Dir::Uni { -5104 debug!("got STREAM_DATA_BLOCKED on send-only {}", id); -5105 return Err(TransportError::STREAM_STATE_ERROR( -5106 "STREAM_DATA_BLOCKED on send-only stream", -5107 )); +4961 } +4962 +4963 match frame { +4964 Frame::Crypto(frame) => { +4965 self.read_crypto(SpaceId::Data, &frame, payload_len)?; +4966 } +4967 Frame::Stream(frame) => { +4968 if self.streams.received(frame, payload_len)?.should_transmit() { +4969 self.spaces[SpaceId::Data].pending.max_data = true; +4970 } +4971 } +4972 Frame::Ack(ack) => { +4973 self.on_ack_received(now, SpaceId::Data, ack)?; +4974 } +4975 Frame::PathAck(ack) => { +4976 if !self.is_multipath_negotiated() { +4977 return Err(TransportError::PROTOCOL_VIOLATION( +4978 "received PATH_ACK frame when multipath was not negotiated", +4979 )); +4980 } +4981 span.record("path", tracing::field::display(&ack.path_id)); +4982 self.on_path_ack_received(now, SpaceId::Data, ack)?; +4983 } +4984 Frame::Padding | Frame::Ping => {} +4985 Frame::Close(reason) => { +4986 close = Some(reason); +4987 } +4988 Frame::PathChallenge(challenge) => { +4989 self.spaces[SpaceKind::Data] +4990 .for_path(path_id) +4991 .pending_path_responses +4992 .push(number, challenge.0, network_path); +4993 // If we were passively migrated (e.g. NAT rebinding), our local_ip will +4994 // not match. Once we processed a non-probing packet the local_ip will +4995 // finally be updated. +4996 let path = &mut self +4997 .path_mut(path_id) +4998 .expect("payload is processed only after the path becomes known"); +4999 if network_path.remote == path.network_path.remote { +5000 // PATH_CHALLENGE on active path, possible off-path packet +5001 // forwarding attack. Send a non-probing packet to recover the +5002 // active path. See +5003 // https://www.rfc-editor.org/rfc/rfc9000.html#section-9.3.3-3. In +5004 // rare cases NAT probes might also appear on-path and would also +5005 // get a non-probing packet as response. There is little harm in +5006 // this. +5007 match self.peer_supports_ack_frequency() { +5008 true => self.immediate_ack(path_id), +5009 false => { +5010 self.ping_path(path_id).ok(); +5011 } +5012 } +5013 } +5014 } +5015 Frame::PathResponse(response) => { +5016 // First try to see if this is a NAT probe response. +5017 if self +5018 .n0_nat_traversal +5019 .handle_path_response(network_path, response.0) +5020 { +5021 self.open_nat_traversed_paths(now); +5022 } else { +5023 // Try to see if this is a response to an on-path PATH_CHALLENGE. +5024 self.handle_path_response_on_path(now, response, path_id, network_path); +5025 } +5026 } +5027 Frame::MaxData(frame::MaxData(bytes)) => { +5028 self.streams.received_max_data(bytes); +5029 } +5030 Frame::MaxStreamData(frame::MaxStreamData { id, offset }) => { +5031 self.streams.received_max_stream_data(id, offset)?; +5032 } +5033 Frame::MaxStreams(frame::MaxStreams { dir, count }) => { +5034 self.streams.received_max_streams(dir, count)?; +5035 } +5036 Frame::ResetStream(frame) => { +5037 if self.streams.received_reset(frame)?.should_transmit() { +5038 self.spaces[SpaceId::Data].pending.max_data = true; +5039 } +5040 } +5041 Frame::DataBlocked(DataBlocked(offset)) => { +5042 debug!(offset, "peer claims to be blocked at connection level"); +5043 } +5044 Frame::StreamDataBlocked(StreamDataBlocked { id, offset }) => { +5045 if id.initiator() == self.side.side() && id.dir() == Dir::Uni { +5046 debug!("got STREAM_DATA_BLOCKED on send-only {}", id); +5047 return Err(TransportError::STREAM_STATE_ERROR( +5048 "STREAM_DATA_BLOCKED on send-only stream", +5049 )); +5050 } +5051 debug!( +5052 stream = %id, +5053 offset, "peer claims to be blocked at stream level" +5054 ); +5055 } +5056 Frame::StreamsBlocked(StreamsBlocked { dir, limit }) => { +5057 if limit > MAX_STREAM_COUNT { +5058 return Err(TransportError::FRAME_ENCODING_ERROR( +5059 "unrepresentable stream limit", +5060 )); +5061 } +5062 debug!( +5063 "peer claims to be blocked opening more than {} {} streams", +5064 limit, dir +5065 ); +5066 } +5067 Frame::StopSending(frame::StopSending { id, error_code }) => { +5068 if id.initiator() != self.side.side() { +5069 if id.dir() == Dir::Uni { +5070 debug!("got STOP_SENDING on recv-only {}", id); +5071 return Err(TransportError::STREAM_STATE_ERROR( +5072 "STOP_SENDING on recv-only stream", +5073 )); +5074 } +5075 } else if self.streams.is_local_unopened(id) { +5076 return Err(TransportError::STREAM_STATE_ERROR( +5077 "STOP_SENDING on unopened stream", +5078 )); +5079 } +5080 self.streams.received_stop_sending(id, error_code); +5081 } +5082 Frame::RetireConnectionId(frame::RetireConnectionId { path_id, sequence }) => { +5083 if let Some(ref path_id) = path_id { +5084 span.record("path", tracing::field::display(&path_id)); +5085 } +5086 let path_id = path_id.unwrap_or_default(); +5087 match self.local_cid_state.get_mut(&path_id) { +5088 None => debug!(?path_id, "RETIRE_CONNECTION_ID for unknown path"), +5089 Some(cid_state) => { +5090 let allow_more_cids = cid_state +5091 .on_cid_retirement(sequence, self.peer_params.issue_cids_limit())?; +5092 +5093 // If the path has closed, we do not issue more CIDs for this path +5094 // For details see https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.2.2 +5095 // > an endpoint SHOULD provide new connection IDs for that path, if still open, using PATH_NEW_CONNECTION_ID frames. +5096 let has_path = !self.abandoned_paths.contains(&path_id); +5097 let allow_more_cids = allow_more_cids && has_path; +5098 +5099 debug_assert!(!self.state.is_drained()); // required for adding endpoint events, process_payload is never called for drained connections +5100 self.endpoint_events +5101 .push_back(EndpointEventInner::RetireConnectionId( +5102 now, +5103 path_id, +5104 sequence, +5105 allow_more_cids, +5106 )); +5107 } 5108 } -5109 debug!( -5110 stream = %id, -5111 offset, "peer claims to be blocked at stream level" -5112 ); -5113 } -5114 Frame::StreamsBlocked(StreamsBlocked { dir, limit }) => { -5115 if limit > MAX_STREAM_COUNT { -5116 return Err(TransportError::FRAME_ENCODING_ERROR( -5117 "unrepresentable stream limit", -5118 )); -5119 } -5120 debug!( -5121 "peer claims to be blocked opening more than {} {} streams", -5122 limit, dir -5123 ); -5124 } -5125 Frame::StopSending(frame::StopSending { id, error_code }) => { -5126 if id.initiator() != self.side.side() { -5127 if id.dir() == Dir::Uni { -5128 debug!("got STOP_SENDING on recv-only {}", id); -5129 return Err(TransportError::STREAM_STATE_ERROR( -5130 "STOP_SENDING on recv-only stream", -5131 )); -5132 } -5133 } else if self.streams.is_local_unopened(id) { -5134 return Err(TransportError::STREAM_STATE_ERROR( -5135 "STOP_SENDING on unopened stream", -5136 )); -5137 } -5138 self.streams.received_stop_sending(id, error_code); -5139 } -5140 Frame::RetireConnectionId(frame::RetireConnectionId { path_id, sequence }) => { -5141 if let Some(ref path_id) = path_id { -5142 span.record("path", tracing::field::display(&path_id)); +5109 } +5110 Frame::NewConnectionId(frame) => { +5111 let path_id = if let Some(path_id) = frame.path_id { +5112 if !self.is_multipath_negotiated() { +5113 return Err(TransportError::PROTOCOL_VIOLATION( +5114 "received PATH_NEW_CONNECTION_ID frame when multipath was not negotiated", +5115 )); +5116 } +5117 if path_id > self.local_max_path_id { +5118 return Err(TransportError::PROTOCOL_VIOLATION( +5119 "PATH_NEW_CONNECTION_ID contains path_id exceeding current max", +5120 )); +5121 } +5122 path_id +5123 } else { +5124 PathId::ZERO +5125 }; +5126 +5127 if let Some(ref path_id) = frame.path_id { +5128 span.record("path", tracing::field::display(&path_id)); +5129 } +5130 +5131 if self.abandoned_paths.contains(&path_id) { +5132 trace!("ignoring issued CID for abandoned path"); +5133 continue; +5134 } +5135 let remote_cids = self +5136 .remote_cids +5137 .entry(path_id) +5138 .or_insert_with(|| CidQueue::new(frame.id)); +5139 if remote_cids.active().is_empty() { +5140 return Err(TransportError::PROTOCOL_VIOLATION( +5141 "NEW_CONNECTION_ID when CIDs aren't in use", +5142 )); 5143 } -5144 let path_id = path_id.unwrap_or_default(); -5145 match self.local_cid_state.get_mut(&path_id) { -5146 None => debug!(?path_id, "RETIRE_CONNECTION_ID for unknown path"), -5147 Some(cid_state) => { -5148 let allow_more_cids = cid_state -5149 .on_cid_retirement(sequence, self.peer_params.issue_cids_limit())?; -5150 -5151 // If the path has closed, we do not issue more CIDs for this path -5152 // For details see https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.2.2 -5153 // > an endpoint SHOULD provide new connection IDs for that path, if still open, using PATH_NEW_CONNECTION_ID frames. -5154 let has_path = !self.abandoned_paths.contains(&path_id); -5155 let allow_more_cids = allow_more_cids && has_path; -5156 -5157 debug_assert!(!self.state.is_drained()); // required for adding endpoint events, process_payload is never called for drained connections -5158 self.endpoint_events -5159 .push_back(EndpointEventInner::RetireConnectionId( -5160 now, -5161 path_id, -5162 sequence, -5163 allow_more_cids, -5164 )); -5165 } -5166 } -5167 } -5168 Frame::NewConnectionId(frame) => { -5169 let path_id = if let Some(path_id) = frame.path_id { -5170 if !self.is_multipath_negotiated() { -5171 return Err(TransportError::PROTOCOL_VIOLATION( -5172 "received PATH_NEW_CONNECTION_ID frame when multipath was not negotiated", -5173 )); +5144 if frame.retire_prior_to > frame.sequence { +5145 return Err(TransportError::PROTOCOL_VIOLATION( +5146 "NEW_CONNECTION_ID retiring unissued CIDs", +5147 )); +5148 } +5149 +5150 use crate::cid_queue::InsertError; +5151 match remote_cids.insert(frame) { +5152 Ok(None) => { +5153 self.open_nat_traversed_paths(now); +5154 } +5155 Ok(Some((retired, reset_token))) => { +5156 let pending_retired = +5157 &mut self.spaces[SpaceId::Data].pending.retire_cids; +5158 /// Ensure `pending_retired` cannot grow without bound. Limit is +5159 /// somewhat arbitrary but very permissive. +5160 const MAX_PENDING_RETIRED_CIDS: u64 = CidQueue::LEN as u64 * 10; +5161 // We don't bother counting in-flight frames because those are bounded +5162 // by congestion control. +5163 if (pending_retired.len() as u64) +5164 .saturating_add(retired.end.saturating_sub(retired.start)) +5165 > MAX_PENDING_RETIRED_CIDS +5166 { +5167 return Err(TransportError::CONNECTION_ID_LIMIT_ERROR( +5168 "queued too many retired CIDs", +5169 )); +5170 } +5171 pending_retired.extend(retired.map(|seq| (path_id, seq))); +5172 self.set_reset_token(path_id, network_path.remote, reset_token); +5173 self.open_nat_traversed_paths(now); 5174 } -5175 if path_id > self.local_max_path_id { -5176 return Err(TransportError::PROTOCOL_VIOLATION( -5177 "PATH_NEW_CONNECTION_ID contains path_id exceeding current max", -5178 )); -5179 } -5180 path_id -5181 } else { -5182 PathId::ZERO -5183 }; -5184 -5185 if let Some(ref path_id) = frame.path_id { -5186 span.record("path", tracing::field::display(&path_id)); -5187 } -5188 -5189 if self.abandoned_paths.contains(&path_id) { -5190 trace!("ignoring issued CID for abandoned path"); -5191 continue; -5192 } -5193 let remote_cids = self -5194 .remote_cids -5195 .entry(path_id) -5196 .or_insert_with(|| CidQueue::new(frame.id)); -5197 if remote_cids.active().is_empty() { -5198 return Err(TransportError::PROTOCOL_VIOLATION( -5199 "NEW_CONNECTION_ID when CIDs aren't in use", -5200 )); -5201 } -5202 if frame.retire_prior_to > frame.sequence { -5203 return Err(TransportError::PROTOCOL_VIOLATION( -5204 "NEW_CONNECTION_ID retiring unissued CIDs", -5205 )); -5206 } -5207 -5208 use crate::cid_queue::InsertError; -5209 match remote_cids.insert(frame) { -5210 Ok(None) => { -5211 self.open_nat_traversed_paths(now); -5212 } -5213 Ok(Some((retired, reset_token))) => { -5214 let pending_retired = -5215 &mut self.spaces[SpaceId::Data].pending.retire_cids; -5216 /// Ensure `pending_retired` cannot grow without bound. Limit is -5217 /// somewhat arbitrary but very permissive. -5218 const MAX_PENDING_RETIRED_CIDS: u64 = CidQueue::LEN as u64 * 10; -5219 // We don't bother counting in-flight frames because those are bounded -5220 // by congestion control. -5221 if (pending_retired.len() as u64) -5222 .saturating_add(retired.end.saturating_sub(retired.start)) -5223 > MAX_PENDING_RETIRED_CIDS -5224 { -5225 return Err(TransportError::CONNECTION_ID_LIMIT_ERROR( -5226 "queued too many retired CIDs", -5227 )); -5228 } -5229 pending_retired.extend(retired.map(|seq| (path_id, seq))); -5230 self.set_reset_token(path_id, network_path.remote, reset_token); -5231 self.open_nat_traversed_paths(now); -5232 } -5233 Err(InsertError::ExceedsLimit) => { -5234 return Err(TransportError::CONNECTION_ID_LIMIT_ERROR("")); -5235 } -5236 Err(InsertError::Retired) => { -5237 trace!("discarding already-retired"); -5238 // RETIRE_CONNECTION_ID might not have been previously sent if e.g. a -5239 // range of connection IDs larger than the active connection ID limit -5240 // was retired all at once via retire_prior_to. -5241 self.spaces[SpaceId::Data] -5242 .pending -5243 .retire_cids -5244 .push((path_id, frame.sequence)); -5245 continue; -5246 } -5247 }; -5248 -5249 if self.side.is_server() -5250 && path_id == PathId::ZERO -5251 && self -5252 .remote_cids -5253 .get(&PathId::ZERO) -5254 .map(|cids| cids.active_seq() == 0) -5255 .unwrap_or_default() -5256 { -5257 // We're a server still using the initial remote CID for the client, so -5258 // let's switch immediately to enable clientside stateless resets. -5259 self.update_remote_cid(PathId::ZERO); +5175 Err(InsertError::ExceedsLimit) => { +5176 return Err(TransportError::CONNECTION_ID_LIMIT_ERROR("")); +5177 } +5178 Err(InsertError::Retired) => { +5179 trace!("discarding already-retired"); +5180 // RETIRE_CONNECTION_ID might not have been previously sent if e.g. a +5181 // range of connection IDs larger than the active connection ID limit +5182 // was retired all at once via retire_prior_to. +5183 self.spaces[SpaceId::Data] +5184 .pending +5185 .retire_cids +5186 .push((path_id, frame.sequence)); +5187 continue; +5188 } +5189 }; +5190 +5191 if self.side.is_server() +5192 && path_id == PathId::ZERO +5193 && self +5194 .remote_cids +5195 .get(&PathId::ZERO) +5196 .map(|cids| cids.active_seq() == 0) +5197 .unwrap_or_default() +5198 { +5199 // We're a server still using the initial remote CID for the client, so +5200 // let's switch immediately to enable clientside stateless resets. +5201 self.update_remote_cid(PathId::ZERO); +5202 } +5203 } +5204 Frame::NewToken(NewToken { token }) => { +5205 let ConnectionSide::Client { +5206 token_store, +5207 server_name, +5208 .. +5209 } = &self.side +5210 else { +5211 return Err(TransportError::PROTOCOL_VIOLATION("client sent NEW_TOKEN")); +5212 }; +5213 if token.is_empty() { +5214 return Err(TransportError::FRAME_ENCODING_ERROR("empty token")); +5215 } +5216 trace!("got new token"); +5217 token_store.insert(server_name, token); +5218 } +5219 Frame::Datagram(datagram) => { +5220 if self +5221 .datagrams +5222 .received(datagram, &self.config.datagram_receive_buffer_size)? +5223 { +5224 self.events.push_back(Event::DatagramReceived); +5225 } +5226 } +5227 Frame::AckFrequency(ack_frequency) => { +5228 // This frame can only be sent in the Data space +5229 +5230 if !self.ack_frequency.ack_frequency_received(&ack_frequency)? { +5231 // The AckFrequency frame is stale (we have already received a more +5232 // recent one) +5233 continue; +5234 } +5235 +5236 // Update the params for all of our paths +5237 for (path_id, space) in self.spaces[SpaceId::Data].number_spaces.iter_mut() { +5238 space.pending_acks.set_ack_frequency_params(&ack_frequency); +5239 +5240 // Our `max_ack_delay` has been updated, so we may need to adjust +5241 // its associated timeout. +5242 // Packets received on abandoned paths are always acknowledged immediately. +5243 if !self.abandoned_paths.contains(path_id) +5244 && let Some(timeout) = space +5245 .pending_acks +5246 .max_ack_delay_timeout(self.ack_frequency.max_ack_delay) +5247 { +5248 self.timers.set( +5249 Timer::PerPath(*path_id, PathTimer::MaxAckDelay), +5250 timeout, +5251 self.qlog.with_time(now), +5252 ); +5253 } +5254 } +5255 } +5256 Frame::ImmediateAck => { +5257 // This frame can only be sent in the Data space +5258 for pns in self.spaces[SpaceId::Data].iter_paths_mut() { +5259 pns.pending_acks.set_immediate_ack_required(); 5260 } 5261 } -5262 Frame::NewToken(NewToken { token }) => { -5263 let ConnectionSide::Client { -5264 token_store, -5265 server_name, -5266 .. -5267 } = &self.side -5268 else { -5269 return Err(TransportError::PROTOCOL_VIOLATION("client sent NEW_TOKEN")); -5270 }; -5271 if token.is_empty() { -5272 return Err(TransportError::FRAME_ENCODING_ERROR("empty token")); -5273 } -5274 trace!("got new token"); -5275 token_store.insert(server_name, token); -5276 } -5277 Frame::Datagram(datagram) => { -5278 if self -5279 .datagrams -5280 .received(datagram, &self.config.datagram_receive_buffer_size)? -5281 { -5282 self.events.push_back(Event::DatagramReceived); -5283 } -5284 } -5285 Frame::AckFrequency(ack_frequency) => { -5286 // This frame can only be sent in the Data space -5287 -5288 if !self.ack_frequency.ack_frequency_received(&ack_frequency)? { -5289 // The AckFrequency frame is stale (we have already received a more -5290 // recent one) -5291 continue; -5292 } -5293 -5294 // Update the params for all of our paths -5295 for (path_id, space) in self.spaces[SpaceId::Data].number_spaces.iter_mut() { -5296 space.pending_acks.set_ack_frequency_params(&ack_frequency); -5297 -5298 // Our `max_ack_delay` has been updated, so we may need to adjust -5299 // its associated timeout. -5300 // Packets received on abandoned paths are always acknowledged immediately. -5301 if !self.abandoned_paths.contains(path_id) -5302 && let Some(timeout) = space -5303 .pending_acks -5304 .max_ack_delay_timeout(self.ack_frequency.max_ack_delay) -5305 { -5306 self.timers.set( -5307 Timer::PerPath(*path_id, PathTimer::MaxAckDelay), -5308 timeout, -5309 self.qlog.with_time(now), -5310 ); -5311 } -5312 } -5313 } -5314 Frame::ImmediateAck => { -5315 // This frame can only be sent in the Data space -5316 for pns in self.spaces[SpaceId::Data].iter_paths_mut() { -5317 pns.pending_acks.set_immediate_ack_required(); -5318 } -5319 } -5320 Frame::HandshakeDone => { -5321 if self.side.is_server() { -5322 return Err(TransportError::PROTOCOL_VIOLATION( -5323 "client sent HANDSHAKE_DONE", -5324 )); -5325 } -5326 if self.crypto_state.has_keys(EncryptionLevel::Handshake) { -5327 self.discard_space(now, SpaceKind::Handshake); -5328 self.events.push_back(Event::HandshakeConfirmed); -5329 trace!("handshake confirmed"); -5330 } -5331 } -5332 Frame::ObservedAddr(observed) => { -5333 // check if params allows the peer to send report and this node to receive it -5334 trace!(seq_no = %observed.seq_no, ip = %observed.ip, port = observed.port); -5335 if !self -5336 .peer_params -5337 .address_discovery_role -5338 .should_report(&self.config.address_discovery_role) -5339 { -5340 return Err(TransportError::PROTOCOL_VIOLATION( -5341 "received OBSERVED_ADDRESS frame when not negotiated", -5342 )); -5343 } -5344 // must only be sent in data space -5345 if packet.header.space() != SpaceKind::Data { -5346 return Err(TransportError::PROTOCOL_VIOLATION( -5347 "OBSERVED_ADDRESS frame outside data space", -5348 )); -5349 } -5350 -5351 let path = self.path_data_mut(path_id); -5352 if path.network_path.remote == network_path.remote { -5353 if let Some(updated) = path.update_observed_addr_report(observed) -5354 && path.open_status == paths::OpenStatus::Informed -5355 { -5356 self.events.push_back(Event::Path(PathEvent::ObservedAddr { -5357 id: path_id, -5358 addr: updated, -5359 })); -5360 // otherwise the event is reported when the path is deemed open -5361 } -5362 } else { -5363 // include in migration -5364 migration_observed_addr = Some(observed) -5365 } -5366 } -5367 Frame::PathAbandon(frame::PathAbandon { -5368 path_id, -5369 error_code, -5370 }) => { -5371 span.record("path", tracing::field::display(&path_id)); -5372 match self.close_path_inner( -5373 now, -5374 path_id, -5375 PathAbandonReason::RemoteAbandoned { -5376 error_code: error_code.into(), -5377 }, -5378 ) { -5379 Ok(()) => { -5380 trace!("peer abandoned path"); -5381 } -5382 Err(ClosePathError::ClosedPath) => { -5383 trace!("peer abandoned already closed path"); -5384 } -5385 Err(ClosePathError::MultipathNotNegotiated) => { -5386 return Err(TransportError::PROTOCOL_VIOLATION( -5387 "received PATH_ABANDON frame when multipath was not negotiated", -5388 )); -5389 } -5390 Err(ClosePathError::LastOpenPath) => { -5391 // Not reachable: close_path_inner allows remote abandons -5392 // for the last path. But handle gracefully just in case. -5393 error!( -5394 "peer abandoned last path but close_path_inner returned LastOpenPath" -5395 ); -5396 } -5397 }; -5398 -5399 // Start draining the path if it still exists and hasn't started draining yet. -5400 if let Some(path) = self.paths.get_mut(&path_id) -5401 && !mem::replace(&mut path.data.draining, true) -5402 { -5403 let ack_delay = self.ack_frequency.max_ack_delay_for_pto(); -5404 let pto = path.data.rtt.pto_base() + ack_delay; -5405 self.timers.set( -5406 Timer::PerPath(path_id, PathTimer::PathDrained), -5407 now + 3 * pto, -5408 self.qlog.with_time(now), -5409 ); -5410 -5411 self.set_max_path_id(now, self.local_max_path_id.saturating_add(1u8)); +5262 Frame::HandshakeDone => { +5263 if self.side.is_server() { +5264 return Err(TransportError::PROTOCOL_VIOLATION( +5265 "client sent HANDSHAKE_DONE", +5266 )); +5267 } +5268 if self.crypto_state.has_keys(EncryptionLevel::Handshake) { +5269 self.discard_space(now, SpaceKind::Handshake); +5270 self.events.push_back(Event::HandshakeConfirmed); +5271 trace!("handshake confirmed"); +5272 } +5273 } +5274 Frame::ObservedAddr(observed) => { +5275 // check if params allows the peer to send report and this node to receive it +5276 trace!(seq_no = %observed.seq_no, ip = %observed.ip, port = observed.port); +5277 if !self +5278 .peer_params +5279 .address_discovery_role +5280 .should_report(&self.config.address_discovery_role) +5281 { +5282 return Err(TransportError::PROTOCOL_VIOLATION( +5283 "received OBSERVED_ADDRESS frame when not negotiated", +5284 )); +5285 } +5286 // must only be sent in data space +5287 if packet.header.space() != SpaceKind::Data { +5288 return Err(TransportError::PROTOCOL_VIOLATION( +5289 "OBSERVED_ADDRESS frame outside data space", +5290 )); +5291 } +5292 +5293 let space_open_status = +5294 self.spaces[SpaceKind::Data].for_path(path_id).open_status; +5295 let path = self.path_data_mut(path_id); +5296 if path.network_path.remote == network_path.remote { +5297 if let Some(updated) = path.update_observed_addr_report(observed) +5298 && space_open_status == OpenStatus::Informed +5299 { +5300 self.events.push_back(Event::Path(PathEvent::ObservedAddr { +5301 id: path_id, +5302 addr: updated, +5303 })); +5304 // otherwise the event is reported when the path is deemed open +5305 } +5306 } else { +5307 // include in migration +5308 migration_observed_addr = Some(observed) +5309 } +5310 } +5311 Frame::PathAbandon(frame::PathAbandon { +5312 path_id, +5313 error_code, +5314 }) => { +5315 span.record("path", tracing::field::display(&path_id)); +5316 match self.close_path_inner( +5317 now, +5318 path_id, +5319 PathAbandonReason::RemoteAbandoned { +5320 error_code: error_code.into(), +5321 }, +5322 ) { +5323 Ok(()) => { +5324 trace!("peer abandoned path"); +5325 } +5326 Err(ClosePathError::ClosedPath) => { +5327 trace!("peer abandoned already closed path"); +5328 } +5329 Err(ClosePathError::MultipathNotNegotiated) => { +5330 return Err(TransportError::PROTOCOL_VIOLATION( +5331 "received PATH_ABANDON frame when multipath was not negotiated", +5332 )); +5333 } +5334 Err(ClosePathError::LastOpenPath) => { +5335 // Not reachable: close_path_inner allows remote abandons +5336 // for the last path. But handle gracefully just in case. +5337 error!( +5338 "peer abandoned last path but close_path_inner returned LastOpenPath" +5339 ); +5340 } +5341 }; +5342 +5343 // Start draining the path if it still exists and hasn't started draining yet. +5344 if let Some(path) = self.paths.get_mut(&path_id) +5345 && !mem::replace(&mut path.data.draining, true) +5346 { +5347 let ack_delay = self.ack_frequency.max_ack_delay_for_pto(); +5348 let pto = path.data.rtt.pto_base() + ack_delay; +5349 self.timers.set( +5350 Timer::PerPath(path_id, PathTimer::PathDrained), +5351 now + 3 * pto, +5352 self.qlog.with_time(now), +5353 ); +5354 +5355 self.set_max_path_id(now, self.local_max_path_id.saturating_add(1u8)); +5356 } +5357 } +5358 Frame::PathStatusAvailable(info) => { +5359 span.record("path", tracing::field::display(&info.path_id)); +5360 if self.is_multipath_negotiated() { +5361 self.on_path_status( +5362 info.path_id, +5363 PathStatus::Available, +5364 info.status_seq_no, +5365 ); +5366 } else { +5367 return Err(TransportError::PROTOCOL_VIOLATION( +5368 "received PATH_STATUS_AVAILABLE frame when multipath was not negotiated", +5369 )); +5370 } +5371 } +5372 Frame::PathStatusBackup(info) => { +5373 span.record("path", tracing::field::display(&info.path_id)); +5374 if self.is_multipath_negotiated() { +5375 self.on_path_status(info.path_id, PathStatus::Backup, info.status_seq_no); +5376 } else { +5377 return Err(TransportError::PROTOCOL_VIOLATION( +5378 "received PATH_STATUS_BACKUP frame when multipath was not negotiated", +5379 )); +5380 } +5381 } +5382 Frame::MaxPathId(frame::MaxPathId(path_id)) => { +5383 span.record("path", tracing::field::display(&path_id)); +5384 if !self.is_multipath_negotiated() { +5385 return Err(TransportError::PROTOCOL_VIOLATION( +5386 "received MAX_PATH_ID frame when multipath was not negotiated", +5387 )); +5388 } +5389 // frames that do not increase the path id are ignored +5390 if path_id > self.remote_max_path_id { +5391 self.remote_max_path_id = path_id; +5392 self.issue_first_path_cids(now); +5393 self.open_nat_traversed_paths(now); +5394 } +5395 } +5396 Frame::PathsBlocked(frame::PathsBlocked(max_path_id)) => { +5397 // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST +5398 // be treated as a connection error of type PROTOCOL_VIOLATION. +5399 // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids> +5400 if self.is_multipath_negotiated() { +5401 if max_path_id > self.local_max_path_id { +5402 return Err(TransportError::PROTOCOL_VIOLATION( +5403 "PATHS_BLOCKED maximum path identifier was larger than local maximum", +5404 )); +5405 } +5406 debug!("received PATHS_BLOCKED({:?})", max_path_id); +5407 // TODO(@divma): ensure max concurrent paths +5408 } else { +5409 return Err(TransportError::PROTOCOL_VIOLATION( +5410 "received PATHS_BLOCKED frame when not multipath was not negotiated", +5411 )); 5412 } 5413 } -5414 Frame::PathStatusAvailable(info) => { -5415 span.record("path", tracing::field::display(&info.path_id)); -5416 if self.is_multipath_negotiated() { -5417 self.on_path_status( -5418 info.path_id, -5419 PathStatus::Available, -5420 info.status_seq_no, -5421 ); -5422 } else { -5423 return Err(TransportError::PROTOCOL_VIOLATION( -5424 "received PATH_STATUS_AVAILABLE frame when multipath was not negotiated", -5425 )); -5426 } -5427 } -5428 Frame::PathStatusBackup(info) => { -5429 span.record("path", tracing::field::display(&info.path_id)); -5430 if self.is_multipath_negotiated() { -5431 self.on_path_status(info.path_id, PathStatus::Backup, info.status_seq_no); -5432 } else { -5433 return Err(TransportError::PROTOCOL_VIOLATION( -5434 "received PATH_STATUS_BACKUP frame when multipath was not negotiated", -5435 )); -5436 } -5437 } -5438 Frame::MaxPathId(frame::MaxPathId(path_id)) => { -5439 span.record("path", tracing::field::display(&path_id)); -5440 if !self.is_multipath_negotiated() { +5414 Frame::PathCidsBlocked(frame::PathCidsBlocked { path_id, next_seq }) => { +5415 // Nothing to do. This is recorded in the frame stats, but otherwise we +5416 // always issue all CIDs we're allowed to issue, so either this is an +5417 // impatient peer or a bug on our side. +5418 +5419 // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST +5420 // be treated as a connection error of type PROTOCOL_VIOLATION. +5421 // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids> +5422 if self.is_multipath_negotiated() { +5423 if path_id > self.local_max_path_id { +5424 return Err(TransportError::PROTOCOL_VIOLATION( +5425 "PATH_CIDS_BLOCKED path identifier was larger than local maximum", +5426 )); +5427 } +5428 if next_seq.0 +5429 > self +5430 .local_cid_state +5431 .get(&path_id) +5432 .map(|cid_state| cid_state.active_seq().1 + 1) +5433 .unwrap_or_default() +5434 { +5435 return Err(TransportError::PROTOCOL_VIOLATION( +5436 "PATH_CIDS_BLOCKED next sequence number larger than in local state", +5437 )); +5438 } +5439 debug!(%path_id, %next_seq, "received PATH_CIDS_BLOCKED"); +5440 } else { 5441 return Err(TransportError::PROTOCOL_VIOLATION( -5442 "received MAX_PATH_ID frame when multipath was not negotiated", +5442 "received PATH_CIDS_BLOCKED frame when not multipath was not negotiated", 5443 )); 5444 } -5445 // frames that do not increase the path id are ignored -5446 if path_id > self.remote_max_path_id { -5447 self.remote_max_path_id = path_id; -5448 self.issue_first_path_cids(now); -5449 self.open_nat_traversed_paths(now); -5450 } -5451 } -5452 Frame::PathsBlocked(frame::PathsBlocked(max_path_id)) => { -5453 // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST -5454 // be treated as a connection error of type PROTOCOL_VIOLATION. -5455 // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids> -5456 if self.is_multipath_negotiated() { -5457 if max_path_id > self.local_max_path_id { -5458 return Err(TransportError::PROTOCOL_VIOLATION( -5459 "PATHS_BLOCKED maximum path identifier was larger than local maximum", -5460 )); -5461 } -5462 debug!("received PATHS_BLOCKED({:?})", max_path_id); -5463 // TODO(@divma): ensure max concurrent paths -5464 } else { -5465 return Err(TransportError::PROTOCOL_VIOLATION( -5466 "received PATHS_BLOCKED frame when not multipath was not negotiated", -5467 )); -5468 } -5469 } -5470 Frame::PathCidsBlocked(frame::PathCidsBlocked { path_id, next_seq }) => { -5471 // Nothing to do. This is recorded in the frame stats, but otherwise we -5472 // always issue all CIDs we're allowed to issue, so either this is an -5473 // impatient peer or a bug on our side. -5474 -5475 // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST -5476 // be treated as a connection error of type PROTOCOL_VIOLATION. -5477 // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids> -5478 if self.is_multipath_negotiated() { -5479 if path_id > self.local_max_path_id { -5480 return Err(TransportError::PROTOCOL_VIOLATION( -5481 "PATH_CIDS_BLOCKED path identifier was larger than local maximum", -5482 )); -5483 } -5484 if next_seq.0 -5485 > self -5486 .local_cid_state -5487 .get(&path_id) -5488 .map(|cid_state| cid_state.active_seq().1 + 1) -5489 .unwrap_or_default() -5490 { -5491 return Err(TransportError::PROTOCOL_VIOLATION( -5492 "PATH_CIDS_BLOCKED next sequence number larger than in local state", -5493 )); -5494 } -5495 debug!(%path_id, %next_seq, "received PATH_CIDS_BLOCKED"); -5496 } else { -5497 return Err(TransportError::PROTOCOL_VIOLATION( -5498 "received PATH_CIDS_BLOCKED frame when not multipath was not negotiated", -5499 )); -5500 } -5501 } -5502 Frame::AddAddress(addr) => { -5503 let client_state = match self.n0_nat_traversal.client_side_mut() { -5504 Ok(state) => state, -5505 Err(err) => { -5506 return Err(TransportError::PROTOCOL_VIOLATION(format!( -5507 "Nat traversal(ADD_ADDRESS): {err}" -5508 ))); -5509 } -5510 }; -5511 -5512 if !client_state.check_remote_address(&addr) { -5513 // if the address is not valid we flag it, but update anyway -5514 warn!(?addr, "server sent illegal ADD_ADDRESS frame"); -5515 } -5516 -5517 match client_state.add_remote_address(addr) { -5518 Ok(maybe_added) => { -5519 if let Some(added) = maybe_added { -5520 self.events.push_back(Event::NatTraversal( -5521 n0_nat_traversal::Event::AddressAdded(added), -5522 )); -5523 } -5524 } -5525 Err(e) => { -5526 warn!(%e, "failed to add remote address") -5527 } -5528 } -5529 } -5530 Frame::RemoveAddress(addr) => { -5531 let client_state = match self.n0_nat_traversal.client_side_mut() { -5532 Ok(state) => state, -5533 Err(err) => { -5534 return Err(TransportError::PROTOCOL_VIOLATION(format!( -5535 "Nat traversal(REMOVE_ADDRESS): {err}" -5536 ))); -5537 } -5538 }; -5539 if let Some(removed_addr) = client_state.remove_remote_address(addr) { -5540 self.events.push_back(Event::NatTraversal( -5541 n0_nat_traversal::Event::AddressRemoved(removed_addr), -5542 )); -5543 } -5544 } -5545 Frame::ReachOut(reach_out) => { -5546 let ipv6 = self.is_ipv6(); -5547 let server_state = match self.n0_nat_traversal.server_side_mut() { -5548 Ok(state) => state, -5549 Err(err) => { -5550 return Err(TransportError::PROTOCOL_VIOLATION(format!( -5551 "Nat traversal(REACH_OUT): {err}" -5552 ))); -5553 } -5554 }; -5555 -5556 let round_before = server_state.current_round(); -5557 -5558 if let Err(err) = server_state.handle_reach_out(reach_out, ipv6) { -5559 return Err(TransportError::PROTOCOL_VIOLATION(format!( -5560 "Nat traversal(REACH_OUT): {err}" -5561 ))); -5562 } -5563 -5564 if server_state.current_round() > round_before { -5565 // A new round was started, reset the NAT probe retry timer. -5566 if let Some(delay) = -5567 self.n0_nat_traversal.retry_delay(self.config.initial_rtt) -5568 { -5569 self.timers.set( -5570 Timer::Conn(ConnTimer::NatTraversalProbeRetry), -5571 now + delay, -5572 self.qlog.with_time(now), -5573 ); -5574 } -5575 } -5576 } -5577 } -5578 } -5579 -5580 let space = self.spaces[SpaceId::Data].for_path(path_id); -5581 if space -5582 .pending_acks -5583 .packet_received(now, number, ack_eliciting, &space.dedup) -5584 { -5585 if self.abandoned_paths.contains(&path_id) { -5586 // § 3.4.3 QUIC-MULTIPATH: promptly send ACKs for packets received from -5587 // abandoned paths. -5588 space.pending_acks.set_immediate_ack_required(); -5589 } else { -5590 self.timers.set( -5591 Timer::PerPath(path_id, PathTimer::MaxAckDelay), -5592 now + self.ack_frequency.max_ack_delay, -5593 self.qlog.with_time(now), -5594 ); -5595 } -5596 } -5597 -5598 // Issue stream ID credit due to ACKs of outgoing finish/resets and incoming finish/resets -5599 // on stopped streams. Incoming finishes/resets on open streams are not handled here as they -5600 // are only freed, and hence only issue credit, once the application has been notified -5601 // during a read on the stream. -5602 let pending = &mut self.spaces[SpaceId::Data].pending; -5603 self.streams.queue_max_stream_id(pending); -5604 -5605 if let Some(reason) = close { -5606 self.state.move_to_draining(Some(reason.into())); -5607 self.endpoint_events.push_back(EndpointEventInner::Draining); -5608 self.connection_close_pending = true; -5609 } -5610 -5611 // For Multipath any packet triggers migration. For RFC9000 or QNT (+ Multipath) -5612 // only non-probing packets trigger migration. -5613 let migrate_on_any_packet = -5614 self.is_multipath_negotiated() && !self.n0_nat_traversal.is_negotiated(); -5615 -5616 // Only migrate if this is the largest packet number seen. -5617 let is_largest_received_pn = Some(number) -5618 == self.spaces[SpaceId::Data] -5619 .for_path(path_id) -5620 .largest_received_packet_number; -5621 -5622 // If we receive a non-probing packet on a new local IP that means we had a NAT -5623 // rebinding-like migration. We update our local address but do not otherwise -5624 // validate the new path, we only need to validate the path if the peer migrates per -5625 // RFC9000 §9: https://www.rfc-editor.org/rfc/rfc9000.html#section-9-4 -5626 if (migrate_on_any_packet || !is_probing_packet) -5627 && is_largest_received_pn -5628 && self.local_ip_may_migrate() -5629 && let Some(new_local_ip) = network_path.local_ip -5630 { -5631 let path_data = self.path_data_mut(path_id); -5632 if path_data -5633 .network_path -5634 .local_ip -5635 .is_some_and(|ip| ip != new_local_ip) -5636 { -5637 debug!( -5638 %path_id, -5639 new_4tuple = %network_path, -5640 prev_4tuple = %path_data.network_path, -5641 "local address passive migration" -5642 ); -5643 } -5644 path_data.network_path.local_ip = Some(new_local_ip) -5645 } -5646 -5647 // If the peer migrated to a new address, trigger migration. -5648 if self.peer_may_migrate() -5649 && (migrate_on_any_packet || !is_probing_packet) -5650 && is_largest_received_pn -5651 && network_path.remote != self.path_data(path_id).network_path.remote -5652 { -5653 self.migrate(path_id, now, network_path, migration_observed_addr); -5654 // Break linkability, if possible -5655 self.update_remote_cid(path_id); -5656 self.spin = false; -5657 } -5658 -5659 Ok(()) -5660 } -5661 -5662 /// Opens any paths that have been successfully NAT traversed. -5663 fn open_nat_traversed_paths(&mut self, now: Instant) { -5664 while let Some(network_path) = self -5665 .n0_nat_traversal -5666 .client_side_mut() -5667 .ok() -5668 .and_then(|s| s.pop_pending_path_open()) -5669 { -5670 match self.open_path_ensure(network_path, PathStatus::Backup, now) { -5671 Ok((path_id, already_existed)) => { -5672 debug!( -5673 %path_id, -5674 ?network_path, -5675 new_path = !already_existed, -5676 "Opened NAT traversal path", -5677 ); -5678 } -5679 Err(err) => match err { -5680 PathError::MultipathNotNegotiated -5681 | PathError::ServerSideNotAllowed -5682 | PathError::ValidationFailed -5683 | PathError::InvalidRemoteAddress(_) => { -5684 error!( -5685 ?err, -5686 ?network_path, -5687 "Failed to open path for successful NAT traversal" -5688 ); -5689 } -5690 PathError::MaxPathIdReached | PathError::RemoteCidsExhausted => { -5691 // Temporary error, put back. -5692 self.n0_nat_traversal -5693 .client_side_mut() -5694 .map(|s| s.push_pending_path_open(network_path)) -5695 .ok(); -5696 debug!( -5697 ?err, -5698 ?network_path, -5699 "Blocked opening NAT traversal path, enqueued" -5700 ); -5701 return; -5702 } -5703 }, -5704 } -5705 } -5706 } -5707 -5708 /// Migrates the 4-tuple of the path. -5709 /// -5710 /// This creates a new [`PathData`] for the migrated path and stores the previous -5711 /// [`PathData`] in [`PathState::prev`]. -5712 fn migrate( -5713 &mut self, -5714 path_id: PathId, -5715 now: Instant, -5716 network_path: FourTuple, -5717 observed_addr: Option<ObservedAddr>, -5718 ) { -5719 trace!( -5720 new_4tuple = %network_path, -5721 prev_4tuple = %self.path_data(path_id).network_path, -5722 %path_id, -5723 "migration initiated", -5724 ); -5725 self.path_generation_counter = self.path_generation_counter.wrapping_add(1); -5726 // TODO(@divma): conditions for path migration in multipath are very specific, check them -5727 // again to prevent path migrations that should actually create a new path -5728 -5729 // Reset rtt/congestion state for new path unless it looks like a NAT rebinding. -5730 // Note that the congestion window will not grow until validation terminates. Helps mitigate -5731 // amplification attacks performed by spoofing source addresses. -5732 let prev_pto = self.pto(SpaceKind::Data, path_id); -5733 let path = self.paths.get_mut(&path_id).expect("known path"); -5734 let mut new_path_data = if network_path.remote.is_ipv4() -5735 && network_path.remote.ip() == path.data.network_path.remote.ip() -5736 { -5737 PathData::from_previous(network_path, &path.data, self.path_generation_counter, now) -5738 } else { -5739 let peer_max_udp_payload_size = -5740 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()) -5741 .unwrap_or(u16::MAX); -5742 PathData::new( -5743 network_path, -5744 self.allow_mtud, -5745 Some(peer_max_udp_payload_size), -5746 self.path_generation_counter, -5747 now, -5748 &self.config, -5749 ) -5750 }; -5751 new_path_data.last_observed_addr_report = path.data.last_observed_addr_report.clone(); -5752 if let Some(report) = observed_addr -5753 && let Some(updated) = new_path_data.update_observed_addr_report(report) -5754 { -5755 tracing::info!("adding observed addr event from migration"); -5756 self.events.push_back(Event::Path(PathEvent::ObservedAddr { -5757 id: path_id, -5758 addr: updated, -5759 })); -5760 } -5761 new_path_data.pending_on_path_challenge = true; -5762 -5763 let mut prev_path_data = mem::replace(&mut path.data, new_path_data); -5764 -5765 // Only store this as previous path if it was validated. For all we know there could -5766 // already be a previous path stored which might have been validated in the past, -5767 // which is more valuable than one that's not yet validated. -5768 // -5769 // With multipath it is possible that there are no remote CIDs for the path ID -5770 // yet. In this case we would never have sent on this path yet and would not be able -5771 // to send a PATH_CHALLENGE either, which is currently a fire-and-forget affair -5772 // anyway. So don't store such a path either. -5773 if !prev_path_data.validated -5774 && let Some(cid) = self.remote_cids.get(&path_id).map(CidQueue::active) -5775 { -5776 prev_path_data.pending_on_path_challenge = true; -5777 // We haven't updated the remote CID yet, this captures the remote CID we were using on -5778 // the previous path. -5779 path.prev = Some((cid, prev_path_data)); -5780 } -5781 -5782 // We need to re-assign the correct remote to this path in qlog -5783 self.qlog.emit_tuple_assigned(path_id, network_path, now); +5445 } +5446 Frame::AddAddress(addr) => { +5447 let client_state = match self.n0_nat_traversal.client_side_mut() { +5448 Ok(state) => state, +5449 Err(err) => { +5450 return Err(TransportError::PROTOCOL_VIOLATION(format!( +5451 "Nat traversal(ADD_ADDRESS): {err}" +5452 ))); +5453 } +5454 }; +5455 +5456 if !client_state.check_remote_address(&addr) { +5457 // if the address is not valid we flag it, but update anyway +5458 warn!(?addr, "server sent illegal ADD_ADDRESS frame"); +5459 } +5460 +5461 match client_state.add_remote_address(addr) { +5462 Ok(maybe_added) => { +5463 if let Some(added) = maybe_added { +5464 self.events.push_back(Event::NatTraversal( +5465 n0_nat_traversal::Event::AddressAdded(added), +5466 )); +5467 } +5468 } +5469 Err(e) => { +5470 warn!(%e, "failed to add remote address") +5471 } +5472 } +5473 } +5474 Frame::RemoveAddress(addr) => { +5475 let client_state = match self.n0_nat_traversal.client_side_mut() { +5476 Ok(state) => state, +5477 Err(err) => { +5478 return Err(TransportError::PROTOCOL_VIOLATION(format!( +5479 "Nat traversal(REMOVE_ADDRESS): {err}" +5480 ))); +5481 } +5482 }; +5483 if let Some(removed_addr) = client_state.remove_remote_address(addr) { +5484 self.events.push_back(Event::NatTraversal( +5485 n0_nat_traversal::Event::AddressRemoved(removed_addr), +5486 )); +5487 } +5488 } +5489 Frame::ReachOut(reach_out) => { +5490 let ipv6 = self.is_ipv6(); +5491 let server_state = match self.n0_nat_traversal.server_side_mut() { +5492 Ok(state) => state, +5493 Err(err) => { +5494 return Err(TransportError::PROTOCOL_VIOLATION(format!( +5495 "Nat traversal(REACH_OUT): {err}" +5496 ))); +5497 } +5498 }; +5499 +5500 let round_before = server_state.current_round(); +5501 +5502 if let Err(err) = server_state.handle_reach_out(reach_out, ipv6) { +5503 return Err(TransportError::PROTOCOL_VIOLATION(format!( +5504 "Nat traversal(REACH_OUT): {err}" +5505 ))); +5506 } +5507 +5508 if server_state.current_round() > round_before { +5509 // A new round was started, reset the NAT probe retry timer. +5510 if let Some(delay) = +5511 self.n0_nat_traversal.retry_delay(self.config.initial_rtt) +5512 { +5513 self.timers.set( +5514 Timer::Conn(ConnTimer::NatTraversalProbeRetry), +5515 now + delay, +5516 self.qlog.with_time(now), +5517 ); +5518 } +5519 } +5520 } +5521 } +5522 } +5523 +5524 let space = self.spaces[SpaceId::Data].for_path(path_id); +5525 if space +5526 .pending_acks +5527 .packet_received(now, number, ack_eliciting, &space.dedup) +5528 { +5529 if self.abandoned_paths.contains(&path_id) { +5530 // § 3.4.3 QUIC-MULTIPATH: promptly send ACKs for packets received from +5531 // abandoned paths. +5532 space.pending_acks.set_immediate_ack_required(); +5533 } else { +5534 self.timers.set( +5535 Timer::PerPath(path_id, PathTimer::MaxAckDelay), +5536 now + self.ack_frequency.max_ack_delay, +5537 self.qlog.with_time(now), +5538 ); +5539 } +5540 } +5541 +5542 // Issue stream ID credit due to ACKs of outgoing finish/resets and incoming finish/resets +5543 // on stopped streams. Incoming finishes/resets on open streams are not handled here as they +5544 // are only freed, and hence only issue credit, once the application has been notified +5545 // during a read on the stream. +5546 let pending = &mut self.spaces[SpaceId::Data].pending; +5547 self.streams.queue_max_stream_id(pending); +5548 +5549 if let Some(reason) = close { +5550 self.state.move_to_draining(Some(reason.into())); +5551 self.endpoint_events.push_back(EndpointEventInner::Draining); +5552 self.connection_close_pending = true; +5553 } +5554 +5555 // For Multipath any packet triggers migration. For RFC9000 or QNT (+ Multipath) +5556 // only non-probing packets trigger migration. +5557 let migrate_on_any_packet = +5558 self.is_multipath_negotiated() && !self.n0_nat_traversal.is_negotiated(); +5559 +5560 // Only migrate if this is the largest packet number seen. +5561 let is_largest_received_pn = Some(number) +5562 == self.spaces[SpaceId::Data] +5563 .for_path(path_id) +5564 .largest_received_packet_number; +5565 +5566 // If we receive a non-probing packet on a new local IP that means we had a NAT +5567 // rebinding-like migration. We update our local address but do not otherwise +5568 // validate the new path, we only need to validate the path if the peer migrates per +5569 // RFC9000 §9: https://www.rfc-editor.org/rfc/rfc9000.html#section-9-4 +5570 if (migrate_on_any_packet || !is_probing_packet) +5571 && is_largest_received_pn +5572 && self.local_ip_may_migrate() +5573 && let Some(new_local_ip) = network_path.local_ip +5574 { +5575 let path_data = self.path_data_mut(path_id); +5576 if path_data +5577 .network_path +5578 .local_ip +5579 .is_some_and(|ip| ip != new_local_ip) +5580 { +5581 debug!( +5582 %path_id, +5583 new_4tuple = %network_path, +5584 prev_4tuple = %path_data.network_path, +5585 "local address passive migration" +5586 ); +5587 } +5588 path_data.network_path.local_ip = Some(new_local_ip) +5589 } +5590 +5591 // If the peer migrated to a new address, trigger migration. +5592 if self.peer_may_migrate() +5593 && (migrate_on_any_packet || !is_probing_packet) +5594 && is_largest_received_pn +5595 && network_path.remote != self.path_data(path_id).network_path.remote +5596 { +5597 self.migrate(path_id, now, network_path, migration_observed_addr); +5598 // Break linkability, if possible +5599 self.update_remote_cid(path_id); +5600 self.spin = false; +5601 } +5602 +5603 Ok(()) +5604 } +5605 +5606 /// Handles any on-path PATH_RESPONSE frames. +5607 /// +5608 /// *path_id* and *network_path* are those on which the PATH_RESPONSE was received. +5609 fn handle_path_response_on_path( +5610 &mut self, +5611 now: Instant, +5612 response: frame::PathResponse, +5613 path_id: PathId, +5614 network_path: FourTuple, +5615 ) { +5616 let is_multipath_negotiated = self.is_multipath_negotiated(); +5617 let path = self +5618 .paths +5619 .get_mut(&path_id) +5620 .expect("payload is processed only after the path becomes known"); +5621 match path +5622 .data +5623 .on_path_response_received(now, response.0, network_path) +5624 { +5625 paths::OnPathResponseReceived::OnPath if !self.abandoned_paths.contains(&path_id) => { +5626 let qlog = self.qlog.with_time(now); +5627 self.timers.stop( +5628 Timer::PerPath(path_id, PathTimer::PathValidationFailed), +5629 qlog.clone(), +5630 ); +5631 self.timers.stop( +5632 Timer::PerPath(path_id, PathTimer::AbandonFromValidation), +5633 qlog.clone(), +5634 ); +5635 let next_challenge = path +5636 .data +5637 .earliest_on_path_expiring_challenge() +5638 .map(|time| time + self.ack_frequency.max_ack_delay_for_pto()); +5639 self.timers.set_or_stop( +5640 Timer::PerPath(path_id, PathTimer::PathChallengeLost), +5641 next_challenge, +5642 qlog, +5643 ); +5644 let pns = self.spaces[SpaceKind::Data].for_path(path_id); +5645 if !matches!(pns.open_status, OpenStatus::Informed) { +5646 if is_multipath_negotiated { +5647 self.events +5648 .push_back(Event::Path(PathEvent::Established { id: path_id })); +5649 } +5650 pns.open_status = OpenStatus::Informed; +5651 if let Some(observed) = path.data.last_observed_addr_report.as_ref() { +5652 self.events.push_back(Event::Path(PathEvent::ObservedAddr { +5653 id: path_id, +5654 addr: observed.socket_addr(), +5655 })); +5656 } +5657 } +5658 if let Some((_, ref mut prev)) = path.prev { +5659 // If an on-path response was received while there is a +5660 // previous path from a migration, then the new path is +5661 // validated and we can stop sending challenges that try to +5662 // re-validate the previous path. +5663 prev.reset_on_path_challenges(); +5664 } +5665 } +5666 paths::OnPathResponseReceived::OnPath => { +5667 trace!( +5668 %response, +5669 "ignoring PATH_RESPONSE received after path is abandoned" +5670 ); +5671 } +5672 paths::OnPathResponseReceived::Unknown => { +5673 debug!(%response, "ignoring invalid PATH_RESPONSE"); +5674 } +5675 paths::OnPathResponseReceived::Ignored { +5676 sent_on, +5677 current_path, +5678 } => { +5679 debug!(%sent_on, %current_path, %response, "ignoring valid PATH_RESPONSE"); +5680 } +5681 } +5682 } +5683 +5684 /// Opens any paths that have been successfully NAT traversed. +5685 fn open_nat_traversed_paths(&mut self, now: Instant) { +5686 while let Some(network_path) = self +5687 .n0_nat_traversal +5688 .client_side_mut() +5689 .ok() +5690 .and_then(|s| s.pop_pending_path_open()) +5691 { +5692 match self.open_path_ensure(network_path, PathStatus::Backup, now) { +5693 Ok((path_id, already_existed)) => { +5694 debug!( +5695 %path_id, +5696 ?network_path, +5697 new_path = !already_existed, +5698 "Opened NAT traversal path", +5699 ); +5700 } +5701 Err(err) => match err { +5702 PathError::MultipathNotNegotiated +5703 | PathError::ServerSideNotAllowed +5704 | PathError::ValidationFailed +5705 | PathError::InvalidRemoteAddress(_) => { +5706 error!( +5707 ?err, +5708 ?network_path, +5709 "Failed to open path for successful NAT traversal" +5710 ); +5711 } +5712 PathError::MaxPathIdReached | PathError::RemoteCidsExhausted => { +5713 // Temporary error, put back. +5714 self.n0_nat_traversal +5715 .client_side_mut() +5716 .map(|s| s.push_pending_path_open(network_path)) +5717 .ok(); +5718 debug!( +5719 ?err, +5720 ?network_path, +5721 "Blocked opening NAT traversal path, enqueued" +5722 ); +5723 return; +5724 } +5725 }, +5726 } +5727 } +5728 } +5729 +5730 /// Migrates the 4-tuple of the path. +5731 /// +5732 /// This creates a new [`PathData`] for the migrated path and stores the previous +5733 /// [`PathData`] in [`PathState::prev`]. +5734 fn migrate( +5735 &mut self, +5736 path_id: PathId, +5737 now: Instant, +5738 network_path: FourTuple, +5739 observed_addr: Option<ObservedAddr>, +5740 ) { +5741 trace!( +5742 new_4tuple = %network_path, +5743 prev_4tuple = %self.path_data(path_id).network_path, +5744 %path_id, +5745 "migration initiated", +5746 ); +5747 self.path_generation_counter = self.path_generation_counter.wrapping_add(1); +5748 // TODO(@divma): conditions for path migration in multipath are very specific, check them +5749 // again to prevent path migrations that should actually create a new path +5750 +5751 // Reset rtt/congestion state for new path unless it looks like a NAT rebinding. +5752 // Note that the congestion window will not grow until validation terminates. Helps mitigate +5753 // amplification attacks performed by spoofing source addresses. +5754 let prev_pto = self.pto(SpaceKind::Data, path_id); +5755 let path = self.paths.get_mut(&path_id).expect("known path"); +5756 let mut new_path_data = if network_path.remote.is_ipv4() +5757 && network_path.remote.ip() == path.data.network_path.remote.ip() +5758 { +5759 PathData::from_previous(network_path, &path.data, self.path_generation_counter, now) +5760 } else { +5761 let peer_max_udp_payload_size = +5762 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()) +5763 .unwrap_or(u16::MAX); +5764 PathData::new( +5765 network_path, +5766 self.allow_mtud, +5767 Some(peer_max_udp_payload_size), +5768 self.path_generation_counter, +5769 now, +5770 &self.config, +5771 ) +5772 }; +5773 new_path_data.last_observed_addr_report = path.data.last_observed_addr_report.clone(); +5774 if let Some(report) = observed_addr +5775 && let Some(updated) = new_path_data.update_observed_addr_report(report) +5776 { +5777 tracing::info!("adding observed addr event from migration"); +5778 self.events.push_back(Event::Path(PathEvent::ObservedAddr { +5779 id: path_id, +5780 addr: updated, +5781 })); +5782 } +5783 new_path_data.pending_challenge = true; 5784 -5785 self.timers.set( -5786 Timer::PerPath(path_id, PathTimer::PathValidationFailed), -5787 now + 3 * cmp::max(self.pto(SpaceKind::Data, path_id), prev_pto), -5788 self.qlog.with_time(now), -5789 ); -5790 } -5791 -5792 /// Handle a change in the local address, i.e. an active migration -5793 /// -5794 /// In the general (non-multipath) case, paths will perform a RFC9000 migration and be pinged -5795 /// for a liveness check. This is the behaviour of a path assumed to be recoverable, even if -5796 /// this is not the case. -5797 /// -5798 /// Clients in a connection in which multipath has been negotiated should migrate paths to new -5799 /// [`PathId`]s. For paths that are known to be non-recoverable can be migrated to a new -5800 /// [`PathId`] by closing the current path, and opening a new one to the same remote. Treating -5801 /// paths as non recoverable when necessary accelerates connectivity re-establishment, or might -5802 /// allow it altogether. -5803 /// -5804 /// The optional `hint` allows callers to indicate when paths are non-recoverable and should be -5805 /// migrated to new a [`PathId`]. -5806 // NOTE: only clients are allowed to migrate, but generally dealing with RFC9000 migrations is -5807 // lacking <https://github.com/n0-computer/noq/issues/364> -5808 pub fn handle_network_change(&mut self, hint: Option<&dyn NetworkChangeHint>, now: Instant) { -5809 debug!("network changed"); -5810 if self.state.is_drained() { -5811 return; -5812 } -5813 if self.highest_space < SpaceKind::Data { -5814 for path in self.paths.values_mut() { -5815 // Clear the local address for it to be obtained from the socket again. -5816 path.data.network_path.local_ip = None; -5817 } -5818 -5819 self.update_remote_cid(PathId::ZERO); -5820 self.ping(); -5821 -5822 return; -5823 } -5824 -5825 // Paths that can't recover so a new path should be open instead. If multipath is not -5826 // negotiated, this will be empty. -5827 let mut non_recoverable_paths = Vec::default(); -5828 let mut recoverable_paths = Vec::default(); -5829 let mut open_paths = 0; -5830 -5831 let is_multipath_negotiated = self.is_multipath_negotiated(); -5832 let is_client = self.side().is_client(); -5833 let immediate_ack_allowed = self.peer_supports_ack_frequency(); -5834 -5835 for (path_id, path) in self.paths.iter_mut() { -5836 if self.abandoned_paths.contains(path_id) { -5837 continue; -5838 } -5839 open_paths += 1; +5785 let mut prev_path_data = mem::replace(&mut path.data, new_path_data); +5786 +5787 // Only store this as previous path if it was validated. For all we know there could +5788 // already be a previous path stored which might have been validated in the past, +5789 // which is more valuable than one that's not yet validated. +5790 // +5791 // With multipath it is possible that there are no remote CIDs for the path ID +5792 // yet. In this case we would never have sent on this path yet and would not be able +5793 // to send a PATH_CHALLENGE either, which is currently a fire-and-forget affair +5794 // anyway. So don't store such a path either. +5795 if !prev_path_data.validated +5796 && let Some(cid) = self.remote_cids.get(&path_id).map(CidQueue::active) +5797 { +5798 prev_path_data.pending_challenge = true; +5799 // We haven't updated the remote CID yet, this captures the remote CID we were using on +5800 // the previous path. +5801 path.prev = Some((cid, prev_path_data)); +5802 } +5803 +5804 // We need to re-assign the correct remote to this path in qlog +5805 self.qlog.emit_tuple_assigned(path_id, network_path, now); +5806 +5807 self.timers.set( +5808 Timer::PerPath(path_id, PathTimer::PathValidationFailed), +5809 now + 3 * cmp::max(self.pto(SpaceKind::Data, path_id), prev_pto), +5810 self.qlog.with_time(now), +5811 ); +5812 } +5813 +5814 /// Handle a change in the local address, i.e. an active migration +5815 /// +5816 /// In the general (non-multipath) case, paths will perform a RFC9000 migration and be pinged +5817 /// for a liveness check. This is the behaviour of a path assumed to be recoverable, even if +5818 /// this is not the case. +5819 /// +5820 /// Clients in a connection in which multipath has been negotiated should migrate paths to new +5821 /// [`PathId`]s. For paths that are known to be non-recoverable can be migrated to a new +5822 /// [`PathId`] by closing the current path, and opening a new one to the same remote. Treating +5823 /// paths as non recoverable when necessary accelerates connectivity re-establishment, or might +5824 /// allow it altogether. +5825 /// +5826 /// The optional `hint` allows callers to indicate when paths are non-recoverable and should be +5827 /// migrated to new a [`PathId`]. +5828 // NOTE: only clients are allowed to migrate, but generally dealing with RFC9000 migrations is +5829 // lacking <https://github.com/n0-computer/noq/issues/364> +5830 pub fn handle_network_change(&mut self, hint: Option<&dyn NetworkChangeHint>, now: Instant) { +5831 debug!("network changed"); +5832 if self.state.is_drained() { +5833 return; +5834 } +5835 if self.highest_space < SpaceKind::Data { +5836 for path in self.paths.values_mut() { +5837 // Clear the local address for it to be obtained from the socket again. +5838 path.data.network_path.local_ip = None; +5839 } 5840 -5841 // Read the network path BEFORE clearing local_ip, so the hint can -5842 // check which interface the path was using. -5843 let network_path = path.data.network_path; -5844 -5845 // Clear the local address for it to be obtained from the socket again. This applies to -5846 // all paths, regardless of being considered recoverable or not -5847 path.data.network_path.local_ip = None; -5848 let remote = network_path.remote; -5849 -5850 // Without multipath, the connection tries to recover the single path, whereas with -5851 // multipath, even in a single-path scenario, we attempt to migrate the path to a new -5852 // PathId. -5853 let attempt_to_recover = if is_multipath_negotiated { -5854 // Use the hint to determine if the path can recover. When no hint is -5855 // provided, clients default to non-recoverable (abandon and re-open) -5856 // while servers default to recoverable (attempt in-place recovery). -5857 hint.map(|h| h.is_path_recoverable(*path_id, network_path)) -5858 .unwrap_or(!is_client) -5859 } else { -5860 // In the non multipath case, we try to recover the single active path -5861 true -5862 }; -5863 -5864 if attempt_to_recover { -5865 recoverable_paths.push((*path_id, remote)); -5866 } else { -5867 non_recoverable_paths.push((*path_id, remote, path.data.local_status())) -5868 } -5869 } -5870 -5871 /* NON RECOVERABLE PATHS */ -5872 // This are handled first, so that in case the treatment intended for these fails, we can -5873 // go the recoverable route instead. -5874 -5875 // Decide if we need to close first or open first in the multipath case. -5876 // - Opening first has a higher risk of getting limited by the negotiated MAX_PATH_ID. -5877 // - Closing first risks this being the only open path. -5878 // We prefer closing paths first unless we identify this is the last open path. -5879 let open_first = open_paths == non_recoverable_paths.len(); -5880 -5881 for (path_id, remote, status) in non_recoverable_paths.into_iter() { -5882 let network_path = FourTuple { -5883 remote, -5884 local_ip: None, /* allow the local ip to be discovered */ -5885 }; -5886 -5887 if open_first && let Err(e) = self.open_path(network_path, status, now) { -5888 if self.side().is_client() { -5889 debug!(%e, "Failed to open new path for network change"); -5890 } -5891 // if this fails, let the path try to recover itself -5892 recoverable_paths.push((path_id, remote)); -5893 continue; -5894 } -5895 -5896 if let Err(e) = -5897 self.close_path_inner(now, path_id, PathAbandonReason::UnusableAfterNetworkChange) -5898 { -5899 debug!(%e,"Failed to close unrecoverable path after network change"); -5900 recoverable_paths.push((path_id, remote)); -5901 continue; -5902 } -5903 -5904 if !open_first && let Err(e) = self.open_path(network_path, status, now) { -5905 // Path has already been closed if we got here. Since the path was not recoverable, -5906 // this might be desirable in any case, because other paths exist (!open_first) and -5907 // this was is considered non recoverable -5908 debug!(%e,"Failed to open new path for network change"); -5909 } -5910 } -5911 -5912 /* RECOVERABLE PATHS */ -5913 -5914 for (path_id, remote) in recoverable_paths.into_iter() { -5915 // Schedule a Ping for a liveness check. -5916 if let Some(path_space) = self.spaces[SpaceId::Data].number_spaces.get_mut(&path_id) { -5917 path_space.ping_pending = true; -5918 -5919 if immediate_ack_allowed { -5920 path_space.immediate_ack_pending = true; -5921 } -5922 } -5923 -5924 // Reset PTO backoff so retransmits resume promptly. Congestion controller and -5925 // RTT are intentionally preserved for recoverable paths. We explicitly allow -5926 // this reset also during the handshake, so do not check -5927 // Self::peer_competed_handshake_address_validation. -5928 if let Some(path) = self.paths.get_mut(&path_id) { -5929 path.data.pto_count = 0; -5930 } -5931 self.set_loss_detection_timer(now, path_id); -5932 -5933 let Some((reset_token, retired)) = -5934 self.remote_cids.get_mut(&path_id).and_then(CidQueue::next) -5935 else { -5936 continue; -5937 }; -5938 -5939 // Retire the current remote CID and any CIDs we had to skip. -5940 self.spaces[SpaceId::Data] -5941 .pending -5942 .retire_cids -5943 .extend(retired.map(|seq| (path_id, seq))); -5944 -5945 debug_assert!(!self.state.is_drained()); // required for endpoint_events, checked above -5946 self.endpoint_events -5947 .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token)); -5948 } -5949 } -5950 -5951 /// Switch to a previously unused remote connection ID, if possible -5952 fn update_remote_cid(&mut self, path_id: PathId) { -5953 let Some((reset_token, retired)) = self -5954 .remote_cids -5955 .get_mut(&path_id) -5956 .and_then(|cids| cids.next()) -5957 else { -5958 return; -5959 }; +5841 self.update_remote_cid(PathId::ZERO); +5842 self.ping(); +5843 +5844 return; +5845 } +5846 +5847 // Paths that can't recover so a new path should be open instead. If multipath is not +5848 // negotiated, this will be empty. +5849 let mut non_recoverable_paths = Vec::default(); +5850 let mut recoverable_paths = Vec::default(); +5851 let mut open_paths = 0; +5852 +5853 let is_multipath_negotiated = self.is_multipath_negotiated(); +5854 let is_client = self.side().is_client(); +5855 let immediate_ack_allowed = self.peer_supports_ack_frequency(); +5856 +5857 for (path_id, path) in self.paths.iter_mut() { +5858 if self.abandoned_paths.contains(path_id) { +5859 continue; +5860 } +5861 open_paths += 1; +5862 +5863 // Read the network path BEFORE clearing local_ip, so the hint can +5864 // check which interface the path was using. +5865 let network_path = path.data.network_path; +5866 +5867 // Clear the local address for it to be obtained from the socket again. This applies to +5868 // all paths, regardless of being considered recoverable or not +5869 path.data.network_path.local_ip = None; +5870 let remote = network_path.remote; +5871 +5872 // Without multipath, the connection tries to recover the single path, whereas with +5873 // multipath, even in a single-path scenario, we attempt to migrate the path to a new +5874 // PathId. +5875 let attempt_to_recover = if is_multipath_negotiated { +5876 // Use the hint to determine if the path can recover. When no hint is +5877 // provided, clients default to non-recoverable (abandon and re-open) +5878 // while servers default to recoverable (attempt in-place recovery). +5879 hint.map(|h| h.is_path_recoverable(*path_id, network_path)) +5880 .unwrap_or(!is_client) +5881 } else { +5882 // In the non multipath case, we try to recover the single active path +5883 true +5884 }; +5885 +5886 if attempt_to_recover { +5887 recoverable_paths.push((*path_id, remote)); +5888 } else { +5889 non_recoverable_paths.push((*path_id, remote, path.data.local_status())) +5890 } +5891 } +5892 +5893 /* NON RECOVERABLE PATHS */ +5894 // This are handled first, so that in case the treatment intended for these fails, we can +5895 // go the recoverable route instead. +5896 +5897 // Decide if we need to close first or open first in the multipath case. +5898 // - Opening first has a higher risk of getting limited by the negotiated MAX_PATH_ID. +5899 // - Closing first risks this being the only open path. +5900 // We prefer closing paths first unless we identify this is the last open path. +5901 let open_first = open_paths == non_recoverable_paths.len(); +5902 +5903 for (path_id, remote, status) in non_recoverable_paths.into_iter() { +5904 let network_path = FourTuple { +5905 remote, +5906 local_ip: None, /* allow the local ip to be discovered */ +5907 }; +5908 +5909 if open_first && let Err(e) = self.open_path(network_path, status, now) { +5910 if self.side().is_client() { +5911 debug!(%e, "Failed to open new path for network change"); +5912 } +5913 // if this fails, let the path try to recover itself +5914 recoverable_paths.push((path_id, remote)); +5915 continue; +5916 } +5917 +5918 if let Err(e) = +5919 self.close_path_inner(now, path_id, PathAbandonReason::UnusableAfterNetworkChange) +5920 { +5921 debug!(%e,"Failed to close unrecoverable path after network change"); +5922 recoverable_paths.push((path_id, remote)); +5923 continue; +5924 } +5925 +5926 if !open_first && let Err(e) = self.open_path(network_path, status, now) { +5927 // Path has already been closed if we got here. Since the path was not recoverable, +5928 // this might be desirable in any case, because other paths exist (!open_first) and +5929 // this was is considered non recoverable +5930 debug!(%e,"Failed to open new path for network change"); +5931 } +5932 } +5933 +5934 /* RECOVERABLE PATHS */ +5935 +5936 for (path_id, remote) in recoverable_paths.into_iter() { +5937 // Schedule a Ping for a liveness check. +5938 if let Some(path_space) = self.spaces[SpaceId::Data].number_spaces.get_mut(&path_id) { +5939 path_space.pending_ping = true; +5940 +5941 if immediate_ack_allowed { +5942 path_space.pending_immediate_ack = true; +5943 } +5944 } +5945 +5946 // Reset PTO backoff so retransmits resume promptly. Congestion controller and +5947 // RTT are intentionally preserved for recoverable paths. We explicitly allow +5948 // this reset also during the handshake, so do not check +5949 // Self::peer_competed_handshake_address_validation. +5950 if let Some(path) = self.paths.get_mut(&path_id) { +5951 path.data.pto_count = 0; +5952 } +5953 self.set_loss_detection_timer(now, path_id); +5954 +5955 let Some((reset_token, retired)) = +5956 self.remote_cids.get_mut(&path_id).and_then(CidQueue::next) +5957 else { +5958 continue; +5959 }; 5960 -5961 // Retire the current remote CID and any CIDs we had to skip. -5962 self.spaces[SpaceId::Data] -5963 .pending -5964 .retire_cids -5965 .extend(retired.map(|seq| (path_id, seq))); -5966 let remote = self.path_data(path_id).network_path.remote; -5967 self.set_reset_token(path_id, remote, reset_token); -5968 } -5969 -5970 /// Sends this reset token to the endpoint -5971 /// -5972 /// The endpoint needs to know the reset tokens issued by the peer, so that if the peer -5973 /// sends a reset token it knows to route it to this connection. See RFC 9000 section -5974 /// 10.3. Stateless Reset. -5975 /// -5976 /// Reset tokens are different for each path, the endpoint identifies paths by peer -5977 /// socket address however, not by path ID. -5978 fn set_reset_token(&mut self, path_id: PathId, remote: SocketAddr, reset_token: ResetToken) { -5979 debug_assert!(!self.state.is_drained()); // required for endpoint events, set_reset_token is never called for drained connections -5980 self.endpoint_events -5981 .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token)); +5961 // Retire the current remote CID and any CIDs we had to skip. +5962 self.spaces[SpaceId::Data] +5963 .pending +5964 .retire_cids +5965 .extend(retired.map(|seq| (path_id, seq))); +5966 +5967 debug_assert!(!self.state.is_drained()); // required for endpoint_events, checked above +5968 self.endpoint_events +5969 .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token)); +5970 } +5971 } +5972 +5973 /// Switch to a previously unused remote connection ID, if possible +5974 fn update_remote_cid(&mut self, path_id: PathId) { +5975 let Some((reset_token, retired)) = self +5976 .remote_cids +5977 .get_mut(&path_id) +5978 .and_then(|cids| cids.next()) +5979 else { +5980 return; +5981 }; 5982 -5983 // During the handshake the server sends a reset token in the transport -5984 // parameters. When we are the client and we receive the reset token during the -5985 // handshake we want this to affect our peer transport parameters. -5986 // TODO(flub): Pretty sure this is pointless, the entire params is overwritten -5987 // shortly after this was called. And then the params don't have this anymore. -5988 if path_id == PathId::ZERO { -5989 self.peer_params.stateless_reset_token = Some(reset_token); -5990 } -5991 } -5992 -5993 /// Issue an initial set of connection IDs to the peer upon connection -5994 fn issue_first_cids(&mut self, now: Instant) { -5995 if self -5996 .local_cid_state -5997 .get(&PathId::ZERO) -5998 .expect("PathId::ZERO exists when the connection is created") -5999 .cid_len() -6000 == 0 -6001 { -6002 return; -6003 } +5983 // Retire the current remote CID and any CIDs we had to skip. +5984 self.spaces[SpaceId::Data] +5985 .pending +5986 .retire_cids +5987 .extend(retired.map(|seq| (path_id, seq))); +5988 let remote = self.path_data(path_id).network_path.remote; +5989 self.set_reset_token(path_id, remote, reset_token); +5990 } +5991 +5992 /// Sends this reset token to the endpoint +5993 /// +5994 /// The endpoint needs to know the reset tokens issued by the peer, so that if the peer +5995 /// sends a reset token it knows to route it to this connection. See RFC 9000 section +5996 /// 10.3. Stateless Reset. +5997 /// +5998 /// Reset tokens are different for each path, the endpoint identifies paths by peer +5999 /// socket address however, not by path ID. +6000 fn set_reset_token(&mut self, path_id: PathId, remote: SocketAddr, reset_token: ResetToken) { +6001 debug_assert!(!self.state.is_drained()); // required for endpoint events, set_reset_token is never called for drained connections +6002 self.endpoint_events +6003 .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token)); 6004 -6005 // Subtract 1 to account for the CID we supplied while handshaking -6006 let mut n = self.peer_params.issue_cids_limit() - 1; -6007 if let ConnectionSide::Server { server_config } = &self.side -6008 && server_config.has_preferred_address() -6009 { -6010 // We also sent a CID in the transport parameters -6011 n -= 1; +6005 // During the handshake the server sends a reset token in the transport +6006 // parameters. When we are the client and we receive the reset token during the +6007 // handshake we want this to affect our peer transport parameters. +6008 // TODO(flub): Pretty sure this is pointless, the entire params is overwritten +6009 // shortly after this was called. And then the params don't have this anymore. +6010 if path_id == PathId::ZERO { +6011 self.peer_params.stateless_reset_token = Some(reset_token); 6012 } -6013 debug_assert!(!self.state.is_drained()); // requirement for endpoint_events -6014 self.endpoint_events -6015 .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n)); -6016 } -6017 -6018 /// Issues an initial set of CIDs for paths that have not yet had any CIDs issued -6019 /// -6020 /// Later CIDs are issued when CIDs expire or are retired by the peer. -6021 fn issue_first_path_cids(&mut self, now: Instant) { -6022 if let Some(max_path_id) = self.max_path_id() { -6023 let mut path_id = self.max_path_id_with_cids.next(); -6024 while path_id <= max_path_id { -6025 self.endpoint_events -6026 .push_back(EndpointEventInner::NeedIdentifiers( -6027 path_id, -6028 now, -6029 self.peer_params.issue_cids_limit(), -6030 )); -6031 path_id = path_id.next(); -6032 } -6033 self.max_path_id_with_cids = max_path_id; +6013 } +6014 +6015 /// Issue an initial set of connection IDs to the peer upon connection +6016 fn issue_first_cids(&mut self, now: Instant) { +6017 if self +6018 .local_cid_state +6019 .get(&PathId::ZERO) +6020 .expect("PathId::ZERO exists when the connection is created") +6021 .cid_len() +6022 == 0 +6023 { +6024 return; +6025 } +6026 +6027 // Subtract 1 to account for the CID we supplied while handshaking +6028 let mut n = self.peer_params.issue_cids_limit() - 1; +6029 if let ConnectionSide::Server { server_config } = &self.side +6030 && server_config.has_preferred_address() +6031 { +6032 // We also sent a CID in the transport parameters +6033 n -= 1; 6034 } -6035 } -6036 -6037 /// Populates a packet with frames -6038 /// -6039 /// This tries to fit as many frames as possible into the packet. -6040 /// -6041 /// *path_exclusive_only* means to only build frames which can only be sent on this -6042 /// *path. This is used in multipath for backup paths while there is still an active -6043 /// *path. -6044 fn populate_packet<'a, 'b>( -6045 &mut self, -6046 now: Instant, -6047 space_id: SpaceId, -6048 path_id: PathId, -6049 scheduling_info: &PathSchedulingInfo, -6050 builder: &mut PacketBuilder<'a, 'b>, -6051 ) { -6052 let is_multipath_negotiated = self.is_multipath_negotiated(); -6053 let space_has_keys = self.crypto_state.has_keys(space_id.encryption_level()); -6054 let is_0rtt = space_id == SpaceId::Data && !space_has_keys; -6055 let stats = &mut self.path_stats.for_path(path_id).frame_tx; -6056 let space = &mut self.spaces[space_id]; -6057 let path = &mut self.paths.get_mut(&path_id).expect("known path").data; -6058 space -6059 .for_path(path_id) -6060 .pending_acks -6061 .maybe_ack_non_eliciting(); -6062 -6063 // HANDSHAKE_DONE -6064 if !is_0rtt -6065 && !scheduling_info.is_abandoned -6066 && scheduling_info.may_send_data -6067 && mem::replace(&mut space.pending.handshake_done, false) -6068 { -6069 builder.write_frame(frame::HandshakeDone, stats); -6070 } -6071 -6072 // PING -6073 if !scheduling_info.is_abandoned -6074 && mem::replace(&mut space.for_path(path_id).ping_pending, false) -6075 { -6076 builder.write_frame(frame::Ping, stats); -6077 } -6078 -6079 // IMMEDIATE_ACK -6080 if !scheduling_info.is_abandoned -6081 && mem::replace(&mut space.for_path(path_id).immediate_ack_pending, false) -6082 { -6083 debug_assert_eq!( -6084 space_id, -6085 SpaceId::Data, -6086 "immediate acks must be sent in the data space" -6087 ); -6088 builder.write_frame(frame::ImmediateAck, stats); -6089 } -6090 -6091 // ACK -6092 if !scheduling_info.is_abandoned && scheduling_info.may_send_data { -6093 for path_id in space -6094 .number_spaces -6095 .iter_mut() -6096 .filter(|(_, pns)| pns.pending_acks.can_send()) -6097 .map(|(&path_id, _)| path_id) -6098 .collect::<Vec<_>>() -6099 { -6100 Self::populate_acks( -6101 now, -6102 self.receiving_ecn, -6103 path_id, -6104 space_id, -6105 space, -6106 is_multipath_negotiated, -6107 builder, -6108 stats, -6109 space_has_keys, -6110 ); -6111 } -6112 } -6113 -6114 // ACK_FREQUENCY -6115 if !scheduling_info.is_abandoned -6116 && scheduling_info.may_send_data -6117 && mem::replace(&mut space.pending.ack_frequency, false) -6118 { -6119 let sequence_number = self.ack_frequency.next_sequence_number(); -6120 -6121 // Safe to unwrap because this is always provided when ACK frequency is enabled -6122 let config = self.config.ack_frequency_config.as_ref().unwrap(); -6123 -6124 // Ensure the delay is within bounds to avoid a PROTOCOL_VIOLATION error -6125 let max_ack_delay = self.ack_frequency.candidate_max_ack_delay( -6126 path.rtt.get(), -6127 config, -6128 &self.peer_params, -6129 ); -6130 -6131 let frame = frame::AckFrequency { -6132 sequence: sequence_number, -6133 ack_eliciting_threshold: config.ack_eliciting_threshold, -6134 request_max_ack_delay: max_ack_delay.as_micros().try_into().unwrap_or(VarInt::MAX), -6135 reordering_threshold: config.reordering_threshold, -6136 }; -6137 builder.write_frame(frame, stats); -6138 -6139 self.ack_frequency -6140 .ack_frequency_sent(path_id, builder.packet_number, max_ack_delay); -6141 } +6035 debug_assert!(!self.state.is_drained()); // requirement for endpoint_events +6036 self.endpoint_events +6037 .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n)); +6038 } +6039 +6040 /// Issues an initial set of CIDs for paths that have not yet had any CIDs issued +6041 /// +6042 /// Later CIDs are issued when CIDs expire or are retired by the peer. +6043 fn issue_first_path_cids(&mut self, now: Instant) { +6044 if let Some(max_path_id) = self.max_path_id() { +6045 let mut path_id = self.max_path_id_with_cids.next(); +6046 while path_id <= max_path_id { +6047 self.endpoint_events +6048 .push_back(EndpointEventInner::NeedIdentifiers( +6049 path_id, +6050 now, +6051 self.peer_params.issue_cids_limit(), +6052 )); +6053 path_id = path_id.next(); +6054 } +6055 self.max_path_id_with_cids = max_path_id; +6056 } +6057 } +6058 +6059 /// Populates a packet with frames +6060 /// +6061 /// This tries to fit as many frames as possible into the packet. +6062 /// +6063 /// *path_exclusive_only* means to only build frames which can only be sent on this +6064 /// *path. This is used in multipath for backup paths while there is still an active +6065 /// *path. +6066 fn populate_packet<'a, 'b>( +6067 &mut self, +6068 now: Instant, +6069 space_id: SpaceId, +6070 path_id: PathId, +6071 scheduling_info: &PathSchedulingInfo, +6072 builder: &mut PacketBuilder<'a, 'b>, +6073 ) { +6074 let is_multipath_negotiated = self.is_multipath_negotiated(); +6075 let space_has_keys = self.crypto_state.has_keys(space_id.encryption_level()); +6076 let is_0rtt = space_id == SpaceId::Data && !space_has_keys; +6077 let stats = &mut self.path_stats.for_path(path_id).frame_tx; +6078 let space = &mut self.spaces[space_id]; +6079 let path = &mut self.paths.get_mut(&path_id).expect("known path").data; +6080 space +6081 .for_path(path_id) +6082 .pending_acks +6083 .maybe_ack_non_eliciting(); +6084 +6085 // HANDSHAKE_DONE +6086 if !is_0rtt +6087 && !scheduling_info.is_abandoned +6088 && scheduling_info.may_send_data +6089 && mem::replace(&mut space.pending.handshake_done, false) +6090 { +6091 builder.write_frame(frame::HandshakeDone, stats); +6092 } +6093 +6094 // PING +6095 if !scheduling_info.is_abandoned +6096 && mem::replace(&mut space.for_path(path_id).pending_ping, false) +6097 { +6098 builder.write_frame(frame::Ping, stats); +6099 } +6100 +6101 // IMMEDIATE_ACK +6102 if !scheduling_info.is_abandoned +6103 && mem::replace(&mut space.for_path(path_id).pending_immediate_ack, false) +6104 { +6105 debug_assert_eq!( +6106 space_id, +6107 SpaceId::Data, +6108 "immediate acks must be sent in the data space" +6109 ); +6110 builder.write_frame(frame::ImmediateAck, stats); +6111 } +6112 +6113 // ACK +6114 if !scheduling_info.is_abandoned && scheduling_info.may_send_data { +6115 for path_id in space +6116 .number_spaces +6117 .iter_mut() +6118 .filter(|(_, pns)| pns.pending_acks.can_send()) +6119 .map(|(&path_id, _)| path_id) +6120 .collect::<Vec<_>>() +6121 { +6122 Self::populate_acks( +6123 now, +6124 self.receiving_ecn, +6125 path_id, +6126 space_id, +6127 space, +6128 is_multipath_negotiated, +6129 builder, +6130 stats, +6131 space_has_keys, +6132 ); +6133 } +6134 } +6135 +6136 // ACK_FREQUENCY +6137 if !scheduling_info.is_abandoned +6138 && scheduling_info.may_send_data +6139 && mem::replace(&mut space.pending.ack_frequency, false) +6140 { +6141 let sequence_number = self.ack_frequency.next_sequence_number(); 6142 -6143 // PATH_CHALLENGE -6144 if !scheduling_info.is_abandoned -6145 && space_id == SpaceId::Data -6146 && path.pending_on_path_challenge -6147 // we don't want to send new challenges if we are already closing -6148 && !self.state.is_closed() -6149 && builder.frame_space_remaining() > frame::PathChallenge::SIZE_BOUND -6150 // PATH_CHALLENGE must be part of datagrams expanded to the MIN_INITIAL_SIZE (1200 -6151 // bytes). A datagram can be expanded to this size if it's the first, as it defines the -6152 // GSO segment size, or if the first datagram is larger than this. -6153 && !(builder.buf.num_datagrams() > 1 && builder.buf.segment_size() < usize::from(MIN_INITIAL_SIZE)) -6154 { -6155 path.pending_on_path_challenge = false; -6156 -6157 let token = self.rng.random(); -6158 path.record_path_challenge_sent(now, token, path.network_path); -6159 // Generate a new challenge every time we send a new PATH_CHALLENGE -6160 let challenge = frame::PathChallenge(token); -6161 builder.write_frame(challenge, stats); -6162 builder.require_padding(); -6163 let pto = self.ack_frequency.max_ack_delay_for_pto() + path.rtt.pto_base(); -6164 match path.open_status { -6165 paths::OpenStatus::Sent | paths::OpenStatus::Informed => {} -6166 paths::OpenStatus::Pending => { -6167 path.open_status = paths::OpenStatus::Sent; -6168 self.timers.set( -6169 Timer::PerPath(path_id, PathTimer::AbandonFromValidation), -6170 now + 3 * pto, -6171 self.qlog.with_time(now), -6172 ); -6173 } -6174 } -6175 -6176 self.timers.set( -6177 Timer::PerPath(path_id, PathTimer::PathChallengeLost), -6178 now + path.on_path_challenge_expiry(), -6179 self.qlog.with_time(now), -6180 ); -6181 -6182 if is_multipath_negotiated && !path.validated && path.pending_on_path_challenge { -6183 // queue informing the path status along with the challenge -6184 space.pending.path_status.insert(path_id); -6185 } -6186 -6187 // Always include an OBSERVED_ADDR frame with a PATH_CHALLENGE, regardless -6188 // of whether one has already been sent on this path. -6189 if space_id == SpaceId::Data -6190 && self -6191 .config -6192 .address_discovery_role -6193 .should_report(&self.peer_params.address_discovery_role) -6194 { -6195 let frame = frame::ObservedAddr::new( -6196 path.network_path.remote, -6197 self.next_observed_addr_seq_no, -6198 ); -6199 if builder.frame_space_remaining() > frame.size() { -6200 builder.write_frame(frame, stats); -6201 -6202 self.next_observed_addr_seq_no = -6203 self.next_observed_addr_seq_no.saturating_add(1u8); -6204 path.observed_addr_sent = true; -6205 -6206 space.pending.observed_addr = false; -6207 } -6208 } -6209 } -6210 -6211 // PATH_RESPONSE -6212 if !scheduling_info.is_abandoned -6213 && space_id == SpaceId::Data -6214 && builder.frame_space_remaining() > frame::PathResponse::SIZE_BOUND -6215 && let Some(token) = path.path_responses.pop_on_path(path.network_path) -6216 // PATH_RESPONSE must be part of datagrams expanded to the MIN_INITIAL_SIZE (1200 -6217 // bytes). A datagram can be expanded to this size if it's the first, as it defines the -6218 // GSO segment size, or if the first datagram is larger than this. -6219 && !(builder.buf.num_datagrams() > 1 && builder.buf.segment_size() < usize::from(MIN_INITIAL_SIZE)) -6220 { -6221 let response = frame::PathResponse(token); -6222 builder.write_frame(response, stats); -6223 builder.require_padding(); -6224 -6225 // NOTE: this is technically not required but might be useful to ride the -6226 // request/response nature of path challenges to refresh an observation -6227 // Since PATH_RESPONSE is a probing frame, this is allowed by the spec. -6228 if space_id == SpaceId::Data -6229 && self -6230 .config -6231 .address_discovery_role -6232 .should_report(&self.peer_params.address_discovery_role) -6233 { -6234 let frame = frame::ObservedAddr::new( -6235 path.network_path.remote, -6236 self.next_observed_addr_seq_no, -6237 ); -6238 if builder.frame_space_remaining() > frame.size() { -6239 builder.write_frame(frame, stats); -6240 -6241 self.next_observed_addr_seq_no = -6242 self.next_observed_addr_seq_no.saturating_add(1u8); -6243 path.observed_addr_sent = true; -6244 -6245 space.pending.observed_addr = false; -6246 } -6247 } -6248 } -6249 -6250 // REACH_OUT -6251 while !scheduling_info.is_abandoned -6252 && scheduling_info.may_send_data -6253 && let Some(reach_out) = space -6254 .pending -6255 .reach_out -6256 .pop_if(|frame| builder.frame_space_remaining() >= frame.size()) -6257 { -6258 builder.write_frame(reach_out, stats); -6259 } -6260 -6261 // PATH_ABANDON -6262 if space_id == SpaceId::Data -6263 && scheduling_info.is_abandoned -6264 && scheduling_info.may_self_abandon -6265 && frame::PathAbandon::SIZE_BOUND <= builder.frame_space_remaining() -6266 && let Some(error_code) = space.pending.path_abandon.remove(&path_id) -6267 { -6268 let frame = frame::PathAbandon { -6269 path_id, -6270 error_code, -6271 }; -6272 builder.write_frame(frame, stats); -6273 -6274 // Consider remotely issued CIDs as retired now that we have sent this frame at -6275 // least once. -6276 self.remote_cids.remove(&path_id); -6277 } -6278 while space_id == SpaceId::Data -6279 && scheduling_info.may_send_data -6280 && frame::PathAbandon::SIZE_BOUND <= builder.frame_space_remaining() -6281 && let Some((abandoned_path_id, error_code)) = space.pending.path_abandon.pop_first() -6282 { -6283 let frame = frame::PathAbandon { -6284 path_id: abandoned_path_id, -6285 error_code, -6286 }; -6287 builder.write_frame(frame, stats); -6288 -6289 // Consider remotely issued CIDs as retired now that we have sent this frame at -6290 // least once. -6291 self.remote_cids.remove(&abandoned_path_id); -6292 } -6293 -6294 // OBSERVED_ADDR -6295 if !scheduling_info.is_abandoned -6296 && scheduling_info.may_send_data -6297 && space_id == SpaceId::Data -6298 && self -6299 .config -6300 .address_discovery_role -6301 .should_report(&self.peer_params.address_discovery_role) -6302 && (!path.observed_addr_sent || space.pending.observed_addr) -6303 { -6304 let frame = -6305 frame::ObservedAddr::new(path.network_path.remote, self.next_observed_addr_seq_no); -6306 if builder.frame_space_remaining() > frame.size() { -6307 builder.write_frame(frame, stats); -6308 -6309 self.next_observed_addr_seq_no = self.next_observed_addr_seq_no.saturating_add(1u8); -6310 path.observed_addr_sent = true; -6311 -6312 space.pending.observed_addr = false; -6313 } -6314 } -6315 -6316 // CRYPTO -6317 while !is_0rtt -6318 && !scheduling_info.is_abandoned -6319 && scheduling_info.may_send_data -6320 && builder.frame_space_remaining() > frame::Crypto::SIZE_BOUND -6321 { -6322 let Some(mut frame) = space.pending.crypto.pop_front() else { -6323 break; -6324 }; -6325 -6326 // Calculate the maximum amount of crypto data we can store in the buffer. -6327 // Since the offset is known, we can reserve the exact size required to encode it. -6328 // For length we reserve 2bytes which allows to encode up to 2^14, -6329 // which is more than what fits into normally sized QUIC frames. -6330 let max_crypto_data_size = builder.frame_space_remaining() -6331 - 1 // Frame Type -6332 - VarInt::size(unsafe { VarInt::from_u64_unchecked(frame.offset) }) -6333 - 2; // Maximum encoded length for frame size, given we send less than 2^14 bytes -6334 -6335 let len = frame -6336 .data -6337 .len() -6338 .min(2usize.pow(14) - 1) -6339 .min(max_crypto_data_size); +6143 // Safe to unwrap because this is always provided when ACK frequency is enabled +6144 let config = self.config.ack_frequency_config.as_ref().unwrap(); +6145 +6146 // Ensure the delay is within bounds to avoid a PROTOCOL_VIOLATION error +6147 let max_ack_delay = self.ack_frequency.candidate_max_ack_delay( +6148 path.rtt.get(), +6149 config, +6150 &self.peer_params, +6151 ); +6152 +6153 let frame = frame::AckFrequency { +6154 sequence: sequence_number, +6155 ack_eliciting_threshold: config.ack_eliciting_threshold, +6156 request_max_ack_delay: max_ack_delay.as_micros().try_into().unwrap_or(VarInt::MAX), +6157 reordering_threshold: config.reordering_threshold, +6158 }; +6159 builder.write_frame(frame, stats); +6160 +6161 self.ack_frequency +6162 .ack_frequency_sent(path_id, builder.packet_number, max_ack_delay); +6163 } +6164 +6165 // PATH_CHALLENGE (on-path) +6166 if !scheduling_info.is_abandoned +6167 && space_id == SpaceId::Data +6168 && path.pending_challenge +6169 // we don't want to send new challenges if we are already closing +6170 && !self.state.is_closed() +6171 && builder.frame_space_remaining() > frame::PathChallenge::SIZE_BOUND +6172 // An on-path PATH_CHALLENGE must be part of datagrams expanded to the +6173 // MIN_INITIAL_SIZE (1200 bytes). +6174 && builder.buf.segment_size() >= usize::from(MIN_INITIAL_SIZE) +6175 { +6176 path.pending_challenge = false; +6177 +6178 let token = self.rng.random(); +6179 path.record_path_challenge_sent(now, token, path.network_path); +6180 // Generate a new challenge every time we send a new PATH_CHALLENGE +6181 let challenge = frame::PathChallenge(token); +6182 builder.write_frame(challenge, stats); +6183 builder.require_padding(); +6184 let pto = self.ack_frequency.max_ack_delay_for_pto() + path.rtt.pto_base(); +6185 let pns = space.for_path(path_id); +6186 match pns.open_status { +6187 OpenStatus::Sent | OpenStatus::Informed => {} +6188 OpenStatus::Pending => { +6189 pns.open_status = OpenStatus::Sent; +6190 self.timers.set( +6191 Timer::PerPath(path_id, PathTimer::AbandonFromValidation), +6192 now + 3 * pto, +6193 self.qlog.with_time(now), +6194 ); +6195 } +6196 } +6197 +6198 self.timers.set( +6199 Timer::PerPath(path_id, PathTimer::PathChallengeLost), +6200 now + path.on_path_challenge_expiry(), +6201 self.qlog.with_time(now), +6202 ); +6203 +6204 if is_multipath_negotiated && !path.validated && path.pending_challenge { +6205 // queue informing the path status along with the challenge +6206 space.pending.path_status.insert(path_id); +6207 } +6208 +6209 // Always include an OBSERVED_ADDR frame with a PATH_CHALLENGE, regardless +6210 // of whether one has already been sent on this path. +6211 if space_id == SpaceId::Data +6212 && self +6213 .config +6214 .address_discovery_role +6215 .should_report(&self.peer_params.address_discovery_role) +6216 { +6217 let frame = frame::ObservedAddr::new( +6218 path.network_path.remote, +6219 self.next_observed_addr_seq_no, +6220 ); +6221 if builder.frame_space_remaining() > frame.size() { +6222 builder.write_frame(frame, stats); +6223 +6224 self.next_observed_addr_seq_no = +6225 self.next_observed_addr_seq_no.saturating_add(1u8); +6226 path.observed_addr_sent = true; +6227 +6228 space.pending.observed_addr = false; +6229 } +6230 } +6231 } +6232 +6233 // PATH_RESPONSE (on-path) +6234 if !scheduling_info.is_abandoned +6235 && space_id == SpaceId::Data +6236 && builder.frame_space_remaining() > frame::PathResponse::SIZE_BOUND +6237 // An on-path PATH_RESPONSE must be part of datagrams expanded to the +6238 // MIN_INITIAL_SIZE (1200 bytes). +6239 && builder.buf.segment_size() >= usize::from(MIN_INITIAL_SIZE) +6240 && let Some(token) = space.for_path(path_id).pending_path_responses.pop_on_path(path.network_path) +6241 { +6242 let response = frame::PathResponse(token); +6243 builder.write_frame(response, stats); +6244 builder.require_padding(); +6245 +6246 // NOTE: this is technically not required but might be useful to ride the +6247 // request/response nature of path challenges to refresh an observation +6248 // Since PATH_RESPONSE is a probing frame, this is allowed by the spec. +6249 if space_id == SpaceId::Data +6250 && self +6251 .config +6252 .address_discovery_role +6253 .should_report(&self.peer_params.address_discovery_role) +6254 { +6255 let frame = frame::ObservedAddr::new( +6256 path.network_path.remote, +6257 self.next_observed_addr_seq_no, +6258 ); +6259 if builder.frame_space_remaining() > frame.size() { +6260 builder.write_frame(frame, stats); +6261 +6262 self.next_observed_addr_seq_no = +6263 self.next_observed_addr_seq_no.saturating_add(1u8); +6264 path.observed_addr_sent = true; +6265 +6266 space.pending.observed_addr = false; +6267 } +6268 } +6269 } +6270 +6271 // ADD_ADDRESS +6272 while space_id == SpaceId::Data +6273 && !scheduling_info.is_abandoned +6274 && scheduling_info.may_send_data +6275 && frame::AddAddress::SIZE_BOUND <= builder.frame_space_remaining() +6276 { +6277 if let Some(added_address) = space.pending.add_address.pop_last() { +6278 builder.write_frame(added_address, stats); +6279 } else { +6280 break; +6281 } +6282 } +6283 +6284 // REMOVE_ADDRESS +6285 while space_id == SpaceId::Data +6286 && !scheduling_info.is_abandoned +6287 && scheduling_info.may_send_data +6288 && frame::RemoveAddress::SIZE_BOUND <= builder.frame_space_remaining() +6289 { +6290 if let Some(removed_address) = space.pending.remove_address.pop_last() { +6291 builder.write_frame(removed_address, stats); +6292 } else { +6293 break; +6294 } +6295 } +6296 +6297 // REACH_OUT +6298 while !scheduling_info.is_abandoned +6299 && scheduling_info.may_send_data +6300 && let Some(reach_out) = space +6301 .pending +6302 .reach_out +6303 .pop_if(|frame| builder.frame_space_remaining() >= frame.size()) +6304 { +6305 builder.write_frame(reach_out, stats); +6306 } +6307 +6308 // PATH_ABANDON +6309 if space_id == SpaceId::Data +6310 && scheduling_info.is_abandoned +6311 && scheduling_info.may_self_abandon +6312 && frame::PathAbandon::SIZE_BOUND <= builder.frame_space_remaining() +6313 && let Some(error_code) = space.pending.path_abandon.remove(&path_id) +6314 { +6315 let frame = frame::PathAbandon { +6316 path_id, +6317 error_code, +6318 }; +6319 builder.write_frame(frame, stats); +6320 +6321 // Consider remotely issued CIDs as retired now that we have sent this frame at +6322 // least once. +6323 self.remote_cids.remove(&path_id); +6324 } +6325 while space_id == SpaceId::Data +6326 && scheduling_info.may_send_data +6327 && frame::PathAbandon::SIZE_BOUND <= builder.frame_space_remaining() +6328 && let Some((abandoned_path_id, error_code)) = space.pending.path_abandon.pop_first() +6329 { +6330 let frame = frame::PathAbandon { +6331 path_id: abandoned_path_id, +6332 error_code, +6333 }; +6334 builder.write_frame(frame, stats); +6335 +6336 // Consider remotely issued CIDs as retired now that we have sent this frame at +6337 // least once. +6338 self.remote_cids.remove(&abandoned_path_id); +6339 } 6340 -6341 let data = frame.data.split_to(len); -6342 let offset = frame.offset; -6343 let truncated = frame::Crypto { offset, data }; -6344 builder.write_frame(truncated, stats); -6345 -6346 if !frame.data.is_empty() { -6347 frame.offset += len as u64; -6348 space.pending.crypto.push_front(frame); -6349 } -6350 } -6351 -6352 // PATH_STATUS_AVAILABLE & PATH_STATUS_BACKUP -6353 while space_id == SpaceId::Data -6354 && !scheduling_info.is_abandoned -6355 && scheduling_info.may_send_data -6356 && frame::PathStatusAvailable::SIZE_BOUND <= builder.frame_space_remaining() -6357 { -6358 let Some(path_id) = space.pending.path_status.pop_first() else { -6359 break; -6360 }; -6361 let Some(path) = self.paths.get(&path_id).map(|path_state| &path_state.data) else { -6362 trace!(%path_id, "discarding queued path status for unknown path"); -6363 continue; -6364 }; -6365 -6366 let seq = path.status.seq(); -6367 match path.local_status() { -6368 PathStatus::Available => { -6369 let frame = frame::PathStatusAvailable { -6370 path_id, -6371 status_seq_no: seq, -6372 }; -6373 builder.write_frame(frame, stats); -6374 } -6375 PathStatus::Backup => { -6376 let frame = frame::PathStatusBackup { -6377 path_id, -6378 status_seq_no: seq, -6379 }; -6380 builder.write_frame(frame, stats); -6381 } -6382 } -6383 } -6384 -6385 // MAX_PATH_ID -6386 if space_id == SpaceId::Data -6387 && !scheduling_info.is_abandoned -6388 && scheduling_info.may_send_data -6389 && space.pending.max_path_id -6390 && frame::MaxPathId::SIZE_BOUND <= builder.frame_space_remaining() -6391 { -6392 let frame = frame::MaxPathId(self.local_max_path_id); -6393 builder.write_frame(frame, stats); -6394 space.pending.max_path_id = false; -6395 } -6396 -6397 // PATHS_BLOCKED -6398 if space_id == SpaceId::Data -6399 && !scheduling_info.is_abandoned -6400 && scheduling_info.may_send_data -6401 && space.pending.paths_blocked -6402 && frame::PathsBlocked::SIZE_BOUND <= builder.frame_space_remaining() -6403 { -6404 let frame = frame::PathsBlocked(self.remote_max_path_id); -6405 builder.write_frame(frame, stats); -6406 space.pending.paths_blocked = false; -6407 } -6408 -6409 // PATH_CIDS_BLOCKED -6410 while space_id == SpaceId::Data -6411 && !scheduling_info.is_abandoned -6412 && scheduling_info.may_send_data -6413 && frame::PathCidsBlocked::SIZE_BOUND <= builder.frame_space_remaining() -6414 { -6415 let Some(path_id) = space.pending.path_cids_blocked.pop_first() else { -6416 break; -6417 }; -6418 let next_seq = match self.remote_cids.get(&path_id) { -6419 Some(cid_queue) => VarInt(cid_queue.active_seq() + 1), -6420 None => VarInt(0), -6421 }; -6422 let frame = frame::PathCidsBlocked { path_id, next_seq }; -6423 builder.write_frame(frame, stats); -6424 } -6425 -6426 // RESET_STREAM, STOP_SENDING, MAX_DATA, MAX_STREAM_DATA, MAX_STREAMS -6427 if space_id == SpaceId::Data -6428 && !scheduling_info.is_abandoned -6429 && scheduling_info.may_send_data -6430 { -6431 self.streams -6432 .write_control_frames(builder, &mut space.pending, stats); -6433 } -6434 -6435 // NEW_CONNECTION_ID -6436 let cid_len = self -6437 .local_cid_state -6438 .values() -6439 .map(|cid_state| cid_state.cid_len()) -6440 .max() -6441 .expect("some local CID state must exist"); -6442 let new_cid_size_bound = -6443 frame::NewConnectionId::size_bound(is_multipath_negotiated, cid_len); -6444 while !scheduling_info.is_abandoned -6445 && scheduling_info.may_send_data -6446 && builder.frame_space_remaining() > new_cid_size_bound -6447 { -6448 let Some(issued) = space.pending.new_cids.pop() else { -6449 break; -6450 }; -6451 // Path was discarded after this CID was queued, drop. -6452 let Some(cid_state) = self.local_cid_state.get(&issued.path_id) else { -6453 debug!( -6454 path = %issued.path_id, seq = issued.sequence, -6455 "dropping queued NEW_CONNECTION_ID for discarded path", -6456 ); -6457 continue; -6458 }; -6459 let retire_prior_to = cid_state.retire_prior_to(); -6460 -6461 let cid_path_id = match is_multipath_negotiated { -6462 true => Some(issued.path_id), -6463 false => { -6464 debug_assert_eq!(issued.path_id, PathId::ZERO); -6465 None -6466 } -6467 }; -6468 let frame = frame::NewConnectionId { -6469 path_id: cid_path_id, -6470 sequence: issued.sequence, -6471 retire_prior_to, -6472 id: issued.id, -6473 reset_token: issued.reset_token, -6474 }; -6475 builder.write_frame(frame, stats); -6476 } -6477 -6478 // RETIRE_CONNECTION_ID -6479 let retire_cid_bound = frame::RetireConnectionId::size_bound(is_multipath_negotiated); -6480 while !scheduling_info.is_abandoned -6481 && scheduling_info.may_send_data -6482 && builder.frame_space_remaining() > retire_cid_bound -6483 { -6484 let (path_id, sequence) = match space.pending.retire_cids.pop() { -6485 Some((PathId::ZERO, seq)) if !is_multipath_negotiated => (None, seq), -6486 Some((path_id, seq)) => (Some(path_id), seq), -6487 None => break, -6488 }; -6489 let frame = frame::RetireConnectionId { path_id, sequence }; -6490 builder.write_frame(frame, stats); -6491 } -6492 -6493 // DATAGRAM -6494 let mut sent_datagrams = false; -6495 while !scheduling_info.is_abandoned -6496 && scheduling_info.may_send_data -6497 && builder.frame_space_remaining() > Datagram::SIZE_BOUND -6498 && space_id == SpaceId::Data -6499 { -6500 match self.datagrams.write(builder, stats) { -6501 true => { -6502 sent_datagrams = true; -6503 } -6504 false => break, -6505 } -6506 } -6507 if self.datagrams.send_blocked && sent_datagrams { -6508 self.events.push_back(Event::DatagramsUnblocked); -6509 self.datagrams.send_blocked = false; -6510 } -6511 -6512 let path = &mut self.paths.get_mut(&path_id).expect("known path").data; -6513 -6514 // NEW_TOKEN -6515 if !scheduling_info.is_abandoned && scheduling_info.may_send_data { -6516 while let Some(network_path) = space.pending.new_tokens.pop() { -6517 debug_assert_eq!(space_id, SpaceId::Data); -6518 let ConnectionSide::Server { server_config } = &self.side else { -6519 panic!("NEW_TOKEN frames should not be enqueued by clients"); -6520 }; -6521 -6522 if !network_path.is_probably_same_path(&path.network_path) { -6523 // NEW_TOKEN frames contain tokens bound to a client's IP address, and are only -6524 // useful if used from the same IP address. Thus, we abandon enqueued NEW_TOKEN -6525 // frames upon an path change. Instead, when the new path becomes validated, -6526 // NEW_TOKEN frames may be enqueued for the new path instead. -6527 continue; -6528 } -6529 -6530 let token = Token::new( -6531 TokenPayload::Validation { -6532 ip: network_path.remote.ip(), -6533 issued: server_config.time_source.now(), -6534 }, -6535 &mut self.rng, -6536 ); -6537 let new_token = NewToken { -6538 token: token.encode(&*server_config.token_key).into(), -6539 }; -6540 -6541 if builder.frame_space_remaining() < new_token.size() { -6542 space.pending.new_tokens.push(network_path); -6543 break; -6544 } -6545 -6546 builder.write_frame(new_token, stats); -6547 builder.retransmits_mut().new_tokens.push(network_path); -6548 } -6549 } -6550 -6551 // ADD_ADDRESS -6552 while space_id == SpaceId::Data -6553 && !scheduling_info.is_abandoned -6554 && scheduling_info.may_send_data -6555 && frame::AddAddress::SIZE_BOUND <= builder.frame_space_remaining() -6556 { -6557 if let Some(added_address) = space.pending.add_address.pop_last() { -6558 builder.write_frame(added_address, stats); -6559 } else { -6560 break; -6561 } -6562 } -6563 -6564 // REMOVE_ADDRESS -6565 while space_id == SpaceId::Data -6566 && !scheduling_info.is_abandoned -6567 && scheduling_info.may_send_data -6568 && frame::RemoveAddress::SIZE_BOUND <= builder.frame_space_remaining() -6569 { -6570 if let Some(removed_address) = space.pending.remove_address.pop_last() { -6571 builder.write_frame(removed_address, stats); -6572 } else { -6573 break; -6574 } -6575 } +6341 // OBSERVED_ADDR +6342 if !scheduling_info.is_abandoned +6343 && scheduling_info.may_send_data +6344 && space_id == SpaceId::Data +6345 && self +6346 .config +6347 .address_discovery_role +6348 .should_report(&self.peer_params.address_discovery_role) +6349 && (!path.observed_addr_sent || space.pending.observed_addr) +6350 { +6351 let frame = +6352 frame::ObservedAddr::new(path.network_path.remote, self.next_observed_addr_seq_no); +6353 if builder.frame_space_remaining() > frame.size() { +6354 builder.write_frame(frame, stats); +6355 +6356 self.next_observed_addr_seq_no = self.next_observed_addr_seq_no.saturating_add(1u8); +6357 path.observed_addr_sent = true; +6358 +6359 space.pending.observed_addr = false; +6360 } +6361 } +6362 +6363 // CRYPTO +6364 while !is_0rtt +6365 && !scheduling_info.is_abandoned +6366 && scheduling_info.may_send_data +6367 && builder.frame_space_remaining() > frame::Crypto::SIZE_BOUND +6368 { +6369 let Some(mut frame) = space.pending.crypto.pop_front() else { +6370 break; +6371 }; +6372 +6373 // Calculate the maximum amount of crypto data we can store in the buffer. +6374 // Since the offset is known, we can reserve the exact size required to encode it. +6375 // For length we reserve 2bytes which allows to encode up to 2^14, +6376 // which is more than what fits into normally sized QUIC frames. +6377 let max_crypto_data_size = builder.frame_space_remaining() +6378 - 1 // Frame Type +6379 - VarInt::size(unsafe { VarInt::from_u64_unchecked(frame.offset) }) +6380 - 2; // Maximum encoded length for frame size, given we send less than 2^14 bytes +6381 +6382 let len = frame +6383 .data +6384 .len() +6385 .min(2usize.pow(14) - 1) +6386 .min(max_crypto_data_size); +6387 +6388 let data = frame.data.split_to(len); +6389 let offset = frame.offset; +6390 let truncated = frame::Crypto { offset, data }; +6391 builder.write_frame(truncated, stats); +6392 +6393 if !frame.data.is_empty() { +6394 frame.offset += len as u64; +6395 space.pending.crypto.push_front(frame); +6396 } +6397 } +6398 +6399 // PATH_STATUS_AVAILABLE & PATH_STATUS_BACKUP +6400 while space_id == SpaceId::Data +6401 && !scheduling_info.is_abandoned +6402 && scheduling_info.may_send_data +6403 && frame::PathStatusAvailable::SIZE_BOUND <= builder.frame_space_remaining() +6404 { +6405 let Some(path_id) = space.pending.path_status.pop_first() else { +6406 break; +6407 }; +6408 let Some(path) = self.paths.get(&path_id).map(|path_state| &path_state.data) else { +6409 trace!(%path_id, "discarding queued path status for unknown path"); +6410 continue; +6411 }; +6412 +6413 let seq = path.status.seq(); +6414 match path.local_status() { +6415 PathStatus::Available => { +6416 let frame = frame::PathStatusAvailable { +6417 path_id, +6418 status_seq_no: seq, +6419 }; +6420 builder.write_frame(frame, stats); +6421 } +6422 PathStatus::Backup => { +6423 let frame = frame::PathStatusBackup { +6424 path_id, +6425 status_seq_no: seq, +6426 }; +6427 builder.write_frame(frame, stats); +6428 } +6429 } +6430 } +6431 +6432 // MAX_PATH_ID +6433 if space_id == SpaceId::Data +6434 && !scheduling_info.is_abandoned +6435 && scheduling_info.may_send_data +6436 && space.pending.max_path_id +6437 && frame::MaxPathId::SIZE_BOUND <= builder.frame_space_remaining() +6438 { +6439 let frame = frame::MaxPathId(self.local_max_path_id); +6440 builder.write_frame(frame, stats); +6441 space.pending.max_path_id = false; +6442 } +6443 +6444 // PATHS_BLOCKED +6445 if space_id == SpaceId::Data +6446 && !scheduling_info.is_abandoned +6447 && scheduling_info.may_send_data +6448 && space.pending.paths_blocked +6449 && frame::PathsBlocked::SIZE_BOUND <= builder.frame_space_remaining() +6450 { +6451 let frame = frame::PathsBlocked(self.remote_max_path_id); +6452 builder.write_frame(frame, stats); +6453 space.pending.paths_blocked = false; +6454 } +6455 +6456 // PATH_CIDS_BLOCKED +6457 while space_id == SpaceId::Data +6458 && !scheduling_info.is_abandoned +6459 && scheduling_info.may_send_data +6460 && frame::PathCidsBlocked::SIZE_BOUND <= builder.frame_space_remaining() +6461 { +6462 let Some(path_id) = space.pending.path_cids_blocked.pop_first() else { +6463 break; +6464 }; +6465 let next_seq = match self.remote_cids.get(&path_id) { +6466 Some(cid_queue) => VarInt(cid_queue.active_seq() + 1), +6467 None => VarInt(0), +6468 }; +6469 let frame = frame::PathCidsBlocked { path_id, next_seq }; +6470 builder.write_frame(frame, stats); +6471 } +6472 +6473 // RESET_STREAM, STOP_SENDING, MAX_DATA, MAX_STREAM_DATA, MAX_STREAMS +6474 if space_id == SpaceId::Data +6475 && !scheduling_info.is_abandoned +6476 && scheduling_info.may_send_data +6477 { +6478 self.streams +6479 .write_control_frames(builder, &mut space.pending, stats); +6480 } +6481 +6482 // NEW_CONNECTION_ID +6483 let cid_len = self +6484 .local_cid_state +6485 .values() +6486 .map(|cid_state| cid_state.cid_len()) +6487 .max() +6488 .expect("some local CID state must exist"); +6489 let new_cid_size_bound = +6490 frame::NewConnectionId::size_bound(is_multipath_negotiated, cid_len); +6491 while !scheduling_info.is_abandoned +6492 && scheduling_info.may_send_data +6493 && builder.frame_space_remaining() > new_cid_size_bound +6494 { +6495 let Some(issued) = space.pending.new_cids.pop() else { +6496 break; +6497 }; +6498 // Path was discarded after this CID was queued, drop. +6499 let Some(cid_state) = self.local_cid_state.get(&issued.path_id) else { +6500 debug!( +6501 path = %issued.path_id, seq = issued.sequence, +6502 "dropping queued NEW_CONNECTION_ID for discarded path", +6503 ); +6504 continue; +6505 }; +6506 let retire_prior_to = cid_state.retire_prior_to(); +6507 +6508 let cid_path_id = match is_multipath_negotiated { +6509 true => Some(issued.path_id), +6510 false => { +6511 debug_assert_eq!(issued.path_id, PathId::ZERO); +6512 None +6513 } +6514 }; +6515 let frame = frame::NewConnectionId { +6516 path_id: cid_path_id, +6517 sequence: issued.sequence, +6518 retire_prior_to, +6519 id: issued.id, +6520 reset_token: issued.reset_token, +6521 }; +6522 builder.write_frame(frame, stats); +6523 } +6524 +6525 // RETIRE_CONNECTION_ID +6526 let retire_cid_bound = frame::RetireConnectionId::size_bound(is_multipath_negotiated); +6527 while !scheduling_info.is_abandoned +6528 && scheduling_info.may_send_data +6529 && builder.frame_space_remaining() > retire_cid_bound +6530 { +6531 let (path_id, sequence) = match space.pending.retire_cids.pop() { +6532 Some((PathId::ZERO, seq)) if !is_multipath_negotiated => (None, seq), +6533 Some((path_id, seq)) => (Some(path_id), seq), +6534 None => break, +6535 }; +6536 let frame = frame::RetireConnectionId { path_id, sequence }; +6537 builder.write_frame(frame, stats); +6538 } +6539 +6540 // DATAGRAM +6541 let mut sent_datagrams = false; +6542 while !scheduling_info.is_abandoned +6543 && scheduling_info.may_send_data +6544 && builder.frame_space_remaining() > Datagram::SIZE_BOUND +6545 && space_id == SpaceId::Data +6546 { +6547 match self.datagrams.write(builder, stats) { +6548 true => { +6549 sent_datagrams = true; +6550 } +6551 false => break, +6552 } +6553 } +6554 if self.datagrams.send_blocked && sent_datagrams { +6555 self.events.push_back(Event::DatagramsUnblocked); +6556 self.datagrams.send_blocked = false; +6557 } +6558 +6559 let path = &mut self.paths.get_mut(&path_id).expect("known path").data; +6560 +6561 // NEW_TOKEN +6562 if !scheduling_info.is_abandoned && scheduling_info.may_send_data { +6563 while let Some(network_path) = space.pending.new_tokens.pop() { +6564 debug_assert_eq!(space_id, SpaceId::Data); +6565 let ConnectionSide::Server { server_config } = &self.side else { +6566 panic!("NEW_TOKEN frames should not be enqueued by clients"); +6567 }; +6568 +6569 if !network_path.is_probably_same_path(&path.network_path) { +6570 // NEW_TOKEN frames contain tokens bound to a client's IP address, and are only +6571 // useful if used from the same IP address. Thus, we abandon enqueued NEW_TOKEN +6572 // frames upon an path change. Instead, when the new path becomes validated, +6573 // NEW_TOKEN frames may be enqueued for the new path instead. +6574 continue; +6575 } 6576 -6577 // STREAM -6578 if !scheduling_info.is_abandoned -6579 && scheduling_info.may_send_data -6580 && space_id == SpaceId::Data -6581 { -6582 self.streams -6583 .write_stream_frames(builder, self.config.send_fairness, stats); -6584 } -6585 } -6586 -6587 /// Write pending ACKs into a buffer -6588 fn populate_acks<'a, 'b>( -6589 now: Instant, -6590 receiving_ecn: bool, -6591 path_id: PathId, -6592 space_id: SpaceId, -6593 space: &mut PacketSpace, -6594 is_multipath_negotiated: bool, -6595 builder: &mut PacketBuilder<'a, 'b>, -6596 stats: &mut FrameStats, -6597 space_has_keys: bool, -6598 ) { -6599 // 0-RTT packets must never carry acks (which would have to be of handshake packets) -6600 debug_assert!(space_has_keys, "tried to send ACK in 0-RTT"); -6601 -6602 debug_assert!( -6603 is_multipath_negotiated || path_id == PathId::ZERO, -6604 "Only PathId::ZERO allowed without multipath (have {path_id:?})" -6605 ); -6606 if is_multipath_negotiated { -6607 debug_assert!( -6608 space_id == SpaceId::Data || path_id == PathId::ZERO, -6609 "path acks must be sent in 1RTT space (have {space_id:?})" -6610 ); -6611 } -6612 -6613 let pns = space.for_path(path_id); -6614 let ranges = pns.pending_acks.ranges(); -6615 debug_assert!(!ranges.is_empty(), "can not send empty ACK range"); -6616 let ecn = if receiving_ecn { -6617 Some(&pns.ecn_counters) -6618 } else { -6619 None -6620 }; -6621 -6622 let delay_micros = pns.pending_acks.ack_delay(now).as_micros() as u64; -6623 // TODO: This should come from `TransportConfig` if that gets configurable. -6624 let ack_delay_exp = TransportParameters::default().ack_delay_exponent; -6625 let delay = delay_micros >> ack_delay_exp.into_inner(); -6626 -6627 if is_multipath_negotiated && space_id == SpaceId::Data { -6628 if !ranges.is_empty() { -6629 let frame = frame::PathAck::encoder(path_id, delay, ranges, ecn); -6630 builder.write_frame(frame, stats); -6631 } -6632 } else { -6633 builder.write_frame(frame::Ack::encoder(delay, ranges, ecn), stats); -6634 } -6635 } -6636 -6637 fn close_common(&mut self) { -6638 trace!("connection closed"); -6639 self.timers.reset(); -6640 } -6641 -6642 fn set_close_timer(&mut self, now: Instant) { -6643 // QUIC-MULTIPATH § 2.6 Connection Closure: draining for 3*PTO using the max PTO of -6644 // all paths. -6645 let pto_max = self.max_pto_for_space(self.highest_space); -6646 self.timers.set( -6647 Timer::Conn(ConnTimer::Close), -6648 now + 3 * pto_max, -6649 self.qlog.with_time(now), -6650 ); -6651 } -6652 -6653 /// Handle transport parameters received from the peer -6654 /// -6655 /// *remote_cid* and *local_cid* are the source and destination CIDs respectively of the -6656 /// *packet into which the transport parameters arrived. -6657 fn handle_peer_params( -6658 &mut self, -6659 params: TransportParameters, -6660 local_cid: ConnectionId, -6661 remote_cid: ConnectionId, -6662 now: Instant, -6663 ) -> Result<(), TransportError> { -6664 if Some(self.original_remote_cid) != params.initial_src_cid -6665 || (self.side.is_client() -6666 && (Some(self.initial_dst_cid) != params.original_dst_cid -6667 || self.retry_src_cid != params.retry_src_cid)) -6668 { -6669 return Err(TransportError::TRANSPORT_PARAMETER_ERROR( -6670 "CID authentication failure", -6671 )); -6672 } -6673 if params.initial_max_path_id.is_some() && (local_cid.is_empty() || remote_cid.is_empty()) { -6674 return Err(TransportError::PROTOCOL_VIOLATION( -6675 "multipath must not use zero-length CIDs", -6676 )); -6677 } -6678 -6679 self.set_peer_params(params); -6680 self.qlog.emit_peer_transport_params_received(self, now); -6681 -6682 Ok(()) -6683 } -6684 -6685 fn set_peer_params(&mut self, params: TransportParameters) { -6686 self.streams.set_params(&params); -6687 self.idle_timeout = -6688 negotiate_max_idle_timeout(self.config.max_idle_timeout, Some(params.max_idle_timeout)); -6689 trace!("negotiated max idle timeout {:?}", self.idle_timeout); -6690 -6691 if let Some(ref info) = params.preferred_address { -6692 // During the handshake PathId::ZERO exists. -6693 self.remote_cids.get_mut(&PathId::ZERO).expect("not yet abandoned").insert(frame::NewConnectionId { -6694 path_id: None, -6695 sequence: 1, -6696 id: info.connection_id, -6697 reset_token: info.stateless_reset_token, -6698 retire_prior_to: 0, -6699 }) -6700 .expect( -6701 "preferred address CID is the first received, and hence is guaranteed to be legal", -6702 ); -6703 let remote = self.path_data(PathId::ZERO).network_path.remote; -6704 self.set_reset_token(PathId::ZERO, remote, info.stateless_reset_token); -6705 } -6706 self.ack_frequency.peer_max_ack_delay = get_max_ack_delay(&params); -6707 -6708 let mut multipath_enabled = false; -6709 if let (Some(local_max_path_id), Some(remote_max_path_id)) = ( -6710 self.config.get_initial_max_path_id(), -6711 params.initial_max_path_id, -6712 ) { -6713 // multipath is enabled, register the local and remote maximums -6714 self.local_max_path_id = local_max_path_id; -6715 self.remote_max_path_id = remote_max_path_id; -6716 let initial_max_path_id = local_max_path_id.min(remote_max_path_id); -6717 debug!(%initial_max_path_id, "multipath negotiated"); -6718 multipath_enabled = true; -6719 } -6720 -6721 if let Some((max_locally_allowed_remote_addresses, max_remotely_allowed_remote_addresses)) = -6722 self.config -6723 .max_remote_nat_traversal_addresses -6724 .zip(params.max_remote_nat_traversal_addresses) -6725 { -6726 if multipath_enabled { -6727 let max_local_addresses = max_remotely_allowed_remote_addresses.get(); -6728 let max_remote_addresses = max_locally_allowed_remote_addresses.get(); -6729 self.n0_nat_traversal = n0_nat_traversal::State::new( -6730 max_remote_addresses, -6731 max_local_addresses, -6732 self.side(), -6733 ); -6734 debug!( -6735 %max_remote_addresses, %max_local_addresses, -6736 "n0's nat traversal negotiated" -6737 ); -6738 } else { -6739 debug!("n0 nat traversal enabled for both endpoints, but multipath is missing") -6740 } -6741 } -6742 -6743 self.peer_params = params; -6744 let peer_max_udp_payload_size = -6745 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX); -6746 self.path_data_mut(PathId::ZERO) -6747 .mtud -6748 .on_peer_max_udp_payload_size_received(peer_max_udp_payload_size); -6749 } -6750 -6751 /// Decrypts a packet, returning the packet number on success -6752 fn decrypt_packet( -6753 &mut self, -6754 now: Instant, -6755 path_id: PathId, -6756 packet: &mut Packet, -6757 ) -> Result<Option<u64>, Option<TransportError>> { -6758 let result = self -6759 .crypto_state -6760 .decrypt_packet_body(packet, path_id, &self.spaces)?; -6761 -6762 let Some(result) = result else { -6763 return Ok(None); -6764 }; -6765 -6766 if result.outgoing_key_update_acked -6767 && let Some(prev) = self.crypto_state.prev_crypto.as_mut() -6768 { -6769 prev.end_packet = Some((result.packet_number, now)); -6770 self.set_key_discard_timer(now, packet.header.space()); -6771 } -6772 -6773 if result.incoming_key_update { -6774 trace!("key update authenticated"); -6775 self.crypto_state -6776 .update_keys(Some((result.packet_number, now)), true); -6777 self.set_key_discard_timer(now, packet.header.space()); -6778 } -6779 -6780 Ok(Some(result.packet_number)) -6781 } +6577 let token = Token::new( +6578 TokenPayload::Validation { +6579 ip: network_path.remote.ip(), +6580 issued: server_config.time_source.now(), +6581 }, +6582 &mut self.rng, +6583 ); +6584 let new_token = NewToken { +6585 token: token.encode(&*server_config.token_key).into(), +6586 }; +6587 +6588 if builder.frame_space_remaining() < new_token.size() { +6589 space.pending.new_tokens.push(network_path); +6590 break; +6591 } +6592 +6593 builder.write_frame(new_token, stats); +6594 builder.retransmits_mut().new_tokens.push(network_path); +6595 } +6596 } +6597 +6598 // STREAM +6599 if !scheduling_info.is_abandoned +6600 && scheduling_info.may_send_data +6601 && space_id == SpaceId::Data +6602 { +6603 self.streams +6604 .write_stream_frames(builder, self.config.send_fairness, stats); +6605 } +6606 } +6607 +6608 /// Write pending ACKs into a buffer +6609 fn populate_acks<'a, 'b>( +6610 now: Instant, +6611 receiving_ecn: bool, +6612 path_id: PathId, +6613 space_id: SpaceId, +6614 space: &mut PacketSpace, +6615 is_multipath_negotiated: bool, +6616 builder: &mut PacketBuilder<'a, 'b>, +6617 stats: &mut FrameStats, +6618 space_has_keys: bool, +6619 ) { +6620 // 0-RTT packets must never carry acks (which would have to be of handshake packets) +6621 debug_assert!(space_has_keys, "tried to send ACK in 0-RTT"); +6622 +6623 debug_assert!( +6624 is_multipath_negotiated || path_id == PathId::ZERO, +6625 "Only PathId::ZERO allowed without multipath (have {path_id:?})" +6626 ); +6627 if is_multipath_negotiated { +6628 debug_assert!( +6629 space_id == SpaceId::Data || path_id == PathId::ZERO, +6630 "path acks must be sent in 1RTT space (have {space_id:?})" +6631 ); +6632 } +6633 +6634 let pns = space.for_path(path_id); +6635 let ranges = pns.pending_acks.ranges(); +6636 debug_assert!(!ranges.is_empty(), "can not send empty ACK range"); +6637 let ecn = if receiving_ecn { +6638 Some(&pns.ecn_counters) +6639 } else { +6640 None +6641 }; +6642 +6643 let delay_micros = pns.pending_acks.ack_delay(now).as_micros() as u64; +6644 // TODO: This should come from `TransportConfig` if that gets configurable. +6645 let ack_delay_exp = TransportParameters::default().ack_delay_exponent; +6646 let delay = delay_micros >> ack_delay_exp.into_inner(); +6647 +6648 if is_multipath_negotiated && space_id == SpaceId::Data { +6649 if !ranges.is_empty() { +6650 let frame = frame::PathAck::encoder(path_id, delay, ranges, ecn); +6651 builder.write_frame(frame, stats); +6652 } +6653 } else { +6654 builder.write_frame(frame::Ack::encoder(delay, ranges, ecn), stats); +6655 } +6656 } +6657 +6658 fn close_common(&mut self) { +6659 trace!("connection closed"); +6660 self.timers.reset(); +6661 } +6662 +6663 fn set_close_timer(&mut self, now: Instant) { +6664 // QUIC-MULTIPATH § 2.6 Connection Closure: draining for 3*PTO using the max PTO of +6665 // all paths. +6666 let pto_max = self.max_pto_for_space(self.highest_space); +6667 self.timers.set( +6668 Timer::Conn(ConnTimer::Close), +6669 now + 3 * pto_max, +6670 self.qlog.with_time(now), +6671 ); +6672 } +6673 +6674 /// Handle transport parameters received from the peer +6675 /// +6676 /// *remote_cid* and *local_cid* are the source and destination CIDs respectively of the +6677 /// *packet into which the transport parameters arrived. +6678 fn handle_peer_params( +6679 &mut self, +6680 params: TransportParameters, +6681 local_cid: ConnectionId, +6682 remote_cid: ConnectionId, +6683 now: Instant, +6684 ) -> Result<(), TransportError> { +6685 if Some(self.original_remote_cid) != params.initial_src_cid +6686 || (self.side.is_client() +6687 && (Some(self.initial_dst_cid) != params.original_dst_cid +6688 || self.retry_src_cid != params.retry_src_cid)) +6689 { +6690 return Err(TransportError::TRANSPORT_PARAMETER_ERROR( +6691 "CID authentication failure", +6692 )); +6693 } +6694 if params.initial_max_path_id.is_some() && (local_cid.is_empty() || remote_cid.is_empty()) { +6695 return Err(TransportError::PROTOCOL_VIOLATION( +6696 "multipath must not use zero-length CIDs", +6697 )); +6698 } +6699 +6700 self.set_peer_params(params); +6701 self.qlog.emit_peer_transport_params_received(self, now); +6702 +6703 Ok(()) +6704 } +6705 +6706 fn set_peer_params(&mut self, params: TransportParameters) { +6707 self.streams.set_params(&params); +6708 self.idle_timeout = +6709 negotiate_max_idle_timeout(self.config.max_idle_timeout, Some(params.max_idle_timeout)); +6710 trace!("negotiated max idle timeout {:?}", self.idle_timeout); +6711 +6712 if let Some(ref info) = params.preferred_address { +6713 // During the handshake PathId::ZERO exists. +6714 self.remote_cids.get_mut(&PathId::ZERO).expect("not yet abandoned").insert(frame::NewConnectionId { +6715 path_id: None, +6716 sequence: 1, +6717 id: info.connection_id, +6718 reset_token: info.stateless_reset_token, +6719 retire_prior_to: 0, +6720 }) +6721 .expect( +6722 "preferred address CID is the first received, and hence is guaranteed to be legal", +6723 ); +6724 let remote = self.path_data(PathId::ZERO).network_path.remote; +6725 self.set_reset_token(PathId::ZERO, remote, info.stateless_reset_token); +6726 } +6727 self.ack_frequency.peer_max_ack_delay = get_max_ack_delay(&params); +6728 +6729 let mut multipath_enabled = false; +6730 if let (Some(local_max_path_id), Some(remote_max_path_id)) = ( +6731 self.config.get_initial_max_path_id(), +6732 params.initial_max_path_id, +6733 ) { +6734 // multipath is enabled, register the local and remote maximums +6735 self.local_max_path_id = local_max_path_id; +6736 self.remote_max_path_id = remote_max_path_id; +6737 let initial_max_path_id = local_max_path_id.min(remote_max_path_id); +6738 debug!(%initial_max_path_id, "multipath negotiated"); +6739 multipath_enabled = true; +6740 } +6741 +6742 if let Some((max_locally_allowed_remote_addresses, max_remotely_allowed_remote_addresses)) = +6743 self.config +6744 .max_remote_nat_traversal_addresses +6745 .zip(params.max_remote_nat_traversal_addresses) +6746 { +6747 if multipath_enabled { +6748 let max_local_addresses = max_remotely_allowed_remote_addresses.get(); +6749 let max_remote_addresses = max_locally_allowed_remote_addresses.get(); +6750 self.n0_nat_traversal = n0_nat_traversal::State::new( +6751 max_remote_addresses, +6752 max_local_addresses, +6753 self.side(), +6754 ); +6755 debug!( +6756 %max_remote_addresses, %max_local_addresses, +6757 "n0's nat traversal negotiated" +6758 ); +6759 } else { +6760 debug!("n0 nat traversal enabled for both endpoints, but multipath is missing") +6761 } +6762 } +6763 +6764 self.peer_params = params; +6765 let peer_max_udp_payload_size = +6766 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX); +6767 self.path_data_mut(PathId::ZERO) +6768 .mtud +6769 .on_peer_max_udp_payload_size_received(peer_max_udp_payload_size); +6770 } +6771 +6772 /// Decrypts a packet, returning the packet number on success +6773 fn decrypt_packet( +6774 &mut self, +6775 now: Instant, +6776 path_id: PathId, +6777 packet: &mut Packet, +6778 ) -> Result<Option<u64>, Option<TransportError>> { +6779 let result = self +6780 .crypto_state +6781 .decrypt_packet_body(packet, path_id, &self.spaces)?; 6782 -6783 fn peer_supports_ack_frequency(&self) -> bool { -6784 self.peer_params.min_ack_delay.is_some() -6785 } +6783 let Some(result) = result else { +6784 return Ok(None); +6785 }; 6786 -6787 /// Send an IMMEDIATE_ACK frame to the remote endpoint -6788 /// -6789 /// According to the spec, this will result in an error if the remote endpoint does not support -6790 /// the Acknowledgement Frequency extension -6791 pub(crate) fn immediate_ack(&mut self, path_id: PathId) { -6792 debug_assert_eq!( -6793 self.highest_space, -6794 SpaceKind::Data, -6795 "immediate ack must be written in the data space" -6796 ); -6797 self.spaces[SpaceId::Data] -6798 .for_path(path_id) -6799 .immediate_ack_pending = true; -6800 } -6801 -6802 /// Decodes a packet, returning its decrypted payload, so it can be inspected in tests -6803 #[cfg(test)] -6804 pub(crate) fn decode_packet(&self, event: &ConnectionEvent) -> Option<Vec<u8>> { -6805 let ConnectionEventInner::Datagram(DatagramConnectionEvent { -6806 path_id, -6807 first_decode, -6808 remaining, -6809 .. -6810 }) = &event.0 -6811 else { -6812 return None; -6813 }; -6814 -6815 if remaining.is_some() { -6816 panic!("Packets should never be coalesced in tests"); -6817 } -6818 -6819 let decrypted_header = self -6820 .crypto_state -6821 .unprotect_header(first_decode.clone(), self.peer_params.stateless_reset_token)?; +6787 if result.outgoing_key_update_acked +6788 && let Some(prev) = self.crypto_state.prev_crypto.as_mut() +6789 { +6790 prev.end_packet = Some((result.packet_number, now)); +6791 self.set_key_discard_timer(now, packet.header.space()); +6792 } +6793 +6794 if result.incoming_key_update { +6795 trace!("key update authenticated"); +6796 self.crypto_state +6797 .update_keys(Some((result.packet_number, now)), true); +6798 self.set_key_discard_timer(now, packet.header.space()); +6799 } +6800 +6801 Ok(Some(result.packet_number)) +6802 } +6803 +6804 fn peer_supports_ack_frequency(&self) -> bool { +6805 self.peer_params.min_ack_delay.is_some() +6806 } +6807 +6808 /// Send an IMMEDIATE_ACK frame to the remote endpoint +6809 /// +6810 /// According to the spec, this will result in an error if the remote endpoint does not support +6811 /// the Acknowledgement Frequency extension +6812 pub(crate) fn immediate_ack(&mut self, path_id: PathId) { +6813 debug_assert_eq!( +6814 self.highest_space, +6815 SpaceKind::Data, +6816 "immediate ack must be written in the data space" +6817 ); +6818 self.spaces[SpaceId::Data] +6819 .for_path(path_id) +6820 .pending_immediate_ack = true; +6821 } 6822 -6823 let mut packet = decrypted_header.packet?; -6824 self.crypto_state -6825 .decrypt_packet_body(&mut packet, *path_id, &self.spaces) -6826 .ok()?; -6827 -6828 Some(packet.payload.to_vec()) -6829 } -6830 -6831 /// The number of bytes of packets containing retransmittable frames that have not been -6832 /// acknowledged or declared lost. -6833 #[cfg(test)] -6834 pub(crate) fn bytes_in_flight(&self) -> u64 { -6835 // TODO(@divma): consider including for multipath? -6836 self.path_data(PathId::ZERO).in_flight.bytes -6837 } -6838 -6839 /// Number of bytes worth of non-ack-only packets that may be sent -6840 #[cfg(test)] -6841 pub(crate) fn congestion_window(&self) -> u64 { -6842 let path = self.path_data(PathId::ZERO); -6843 path.congestion -6844 .window() -6845 .saturating_sub(path.in_flight.bytes) -6846 } -6847 -6848 /// Whether no timers but keepalive, idle, rtt, pushnewcid, and key discard are running -6849 #[cfg(test)] -6850 pub(crate) fn is_idle(&self) -> bool { -6851 let current_timers = self.timers.values(); -6852 current_timers -6853 .into_iter() -6854 .filter(|(timer, _)| { -6855 !matches!( -6856 timer, -6857 Timer::Conn(ConnTimer::KeepAlive) -6858 | Timer::PerPath(_, PathTimer::PathKeepAlive) -6859 | Timer::Conn(ConnTimer::PushNewCid) -6860 | Timer::Conn(ConnTimer::KeyDiscard) -6861 ) -6862 }) -6863 .min_by_key(|(_, time)| *time) -6864 .is_none_or(|(timer, _)| { -6865 matches!( -6866 timer, -6867 Timer::Conn(ConnTimer::Idle) | Timer::PerPath(_, PathTimer::PathIdle) -6868 ) -6869 }) -6870 } -6871 -6872 /// Whether explicit congestion notification is in use on outgoing packets. -6873 #[cfg(test)] -6874 pub(crate) fn using_ecn(&self) -> bool { -6875 self.path_data(PathId::ZERO).sending_ecn -6876 } -6877 -6878 /// The number of received bytes in the current path -6879 #[cfg(test)] -6880 pub(crate) fn total_recvd(&self) -> u64 { -6881 self.path_data(PathId::ZERO).total_recvd -6882 } -6883 -6884 #[cfg(test)] -6885 pub(crate) fn active_local_cid_seq(&self) -> (u64, u64) { -6886 self.local_cid_state -6887 .get(&PathId::ZERO) -6888 .unwrap() -6889 .active_seq() -6890 } -6891 -6892 #[cfg(test)] -6893 #[track_caller] -6894 pub(crate) fn active_local_path_cid_seq(&self, path_id: u32) -> (u64, u64) { -6895 self.local_cid_state -6896 .get(&PathId(path_id)) -6897 .unwrap() -6898 .active_seq() -6899 } -6900 -6901 /// Instruct the peer to replace previously issued CIDs by sending a NEW_CONNECTION_ID frame -6902 /// with updated `retire_prior_to` field set to `v` -6903 #[cfg(test)] -6904 pub(crate) fn rotate_local_cid(&mut self, v: u64, now: Instant) { -6905 let n = self -6906 .local_cid_state -6907 .get_mut(&PathId::ZERO) -6908 .unwrap() -6909 .assign_retire_seq(v); -6910 debug_assert!(!self.state.is_drained()); // requirement for endpoint_events -6911 self.endpoint_events -6912 .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n)); -6913 } -6914 -6915 /// Check the current active remote CID sequence for `PathId::ZERO` -6916 #[cfg(test)] -6917 pub(crate) fn active_remote_cid_seq(&self) -> u64 { -6918 self.remote_cids.get(&PathId::ZERO).unwrap().active_seq() -6919 } -6920 -6921 /// Returns the detected maximum udp payload size for the current path -6922 #[cfg(test)] -6923 pub(crate) fn path_mtu(&self, path_id: PathId) -> u16 { -6924 self.path_data(path_id).current_mtu() -6925 } -6926 -6927 /// Triggers path validation on all paths -6928 #[cfg(test)] -6929 pub(crate) fn trigger_path_validation(&mut self) { -6930 for path in self.paths.values_mut() { -6931 path.data.pending_on_path_challenge = true; -6932 } -6933 } -6934 -6935 /// Simulates a protocol violation error for test purposes. -6936 #[cfg(test)] -6937 pub fn simulate_protocol_violation(&mut self, now: Instant) { -6938 if !self.state.is_closed() { -6939 self.state -6940 .move_to_closed(TransportError::PROTOCOL_VIOLATION("simulated violation")); -6941 self.close_common(); -6942 if !self.state.is_drained() { -6943 self.set_close_timer(now); -6944 } -6945 self.connection_close_pending = true; -6946 } -6947 } -6948 -6949 /// Whether we have on-path 1-RTT data to send. -6950 /// -6951 /// This checks for frames that can only be sent in the data space (1-RTT): -6952 /// - Pending PATH_CHALLENGE frames on the active and previous path if just migrated. -6953 /// - Pending PATH_RESPONSE frames. -6954 /// - Pending data to send in STREAM frames. -6955 /// - Pending DATAGRAM frames to send. -6956 /// -6957 /// See also [`PacketSpace::can_send`] which keeps track of all other frame types that -6958 /// may need to be sent. -6959 fn can_send_1rtt(&self, path_id: PathId, max_size: usize) -> SendableFrames { -6960 let space_specific = self.paths.get(&path_id).is_some_and(|path| { -6961 path.data.pending_on_path_challenge || !path.data.path_responses.is_empty() -6962 }); -6963 -6964 // Stream control frames are checked in PacketSpace::can_send, only check data here. -6965 let other = self.streams.can_send_stream_data() -6966 || self -6967 .datagrams -6968 .outgoing -6969 .front() -6970 .is_some_and(|x| x.size(true) <= max_size); -6971 -6972 // All `false` fields are set in PacketSpace::can_send. -6973 SendableFrames { -6974 acks: false, -6975 close: false, -6976 space_specific, -6977 other, -6978 } -6979 } -6980 -6981 /// Terminate the connection instantly, without sending a close packet -6982 fn kill(&mut self, reason: ConnectionError) { -6983 self.close_common(); -6984 let was_draining = self.state.move_to_drained(Some(reason)); -6985 if !was_draining { -6986 self.endpoint_events.push_back(EndpointEventInner::Draining); -6987 } -6988 // move_to_drained checks that we were never in drained before, so we -6989 // never sent a `Drained` event before (it's illegal to send more events after drained). -6990 self.endpoint_events.push_back(EndpointEventInner::Drained); -6991 } -6992 -6993 /// Storage size required for the largest packet that can be transmitted on all currently -6994 /// available paths -6995 /// -6996 /// Buffers passed to [`Connection::poll_transmit`] should be at least this large. -6997 /// -6998 /// When multipath is enabled, this value is the minimum MTU across all available paths. -6999 pub fn current_mtu(&self) -> u16 { -7000 self.paths -7001 .iter() -7002 .filter(|&(path_id, _path_state)| !self.abandoned_paths.contains(path_id)) -7003 .map(|(_path_id, path_state)| path_state.data.current_mtu()) -7004 .min() -7005 .unwrap_or(INITIAL_MTU) +6823 /// Decodes a packet, returning its decrypted payload, so it can be inspected in tests +6824 #[cfg(test)] +6825 pub(crate) fn decode_packet(&self, event: &ConnectionEvent) -> Option<Vec<u8>> { +6826 let ConnectionEventInner::Datagram(DatagramConnectionEvent { +6827 path_id, +6828 first_decode, +6829 remaining, +6830 .. +6831 }) = &event.0 +6832 else { +6833 return None; +6834 }; +6835 +6836 if remaining.is_some() { +6837 panic!("Packets should never be coalesced in tests"); +6838 } +6839 +6840 let decrypted_header = self +6841 .crypto_state +6842 .unprotect_header(first_decode.clone(), self.peer_params.stateless_reset_token)?; +6843 +6844 let mut packet = decrypted_header.packet?; +6845 self.crypto_state +6846 .decrypt_packet_body(&mut packet, *path_id, &self.spaces) +6847 .ok()?; +6848 +6849 Some(packet.payload.to_vec()) +6850 } +6851 +6852 /// The number of bytes of packets containing retransmittable frames that have not been +6853 /// acknowledged or declared lost. +6854 #[cfg(test)] +6855 pub(crate) fn bytes_in_flight(&self) -> u64 { +6856 // TODO(@divma): consider including for multipath? +6857 self.path_data(PathId::ZERO).in_flight.bytes +6858 } +6859 +6860 /// Number of bytes worth of non-ack-only packets that may be sent +6861 #[cfg(test)] +6862 pub(crate) fn congestion_window(&self) -> u64 { +6863 let path = self.path_data(PathId::ZERO); +6864 path.congestion +6865 .window() +6866 .saturating_sub(path.in_flight.bytes) +6867 } +6868 +6869 /// Whether no timers but keepalive, idle, rtt, pushnewcid, and key discard are running +6870 #[cfg(test)] +6871 pub(crate) fn is_idle(&self) -> bool { +6872 let current_timers = self.timers.values(); +6873 current_timers +6874 .into_iter() +6875 .filter(|(timer, _)| { +6876 !matches!( +6877 timer, +6878 Timer::Conn(ConnTimer::KeepAlive) +6879 | Timer::PerPath(_, PathTimer::PathKeepAlive) +6880 | Timer::Conn(ConnTimer::PushNewCid) +6881 | Timer::Conn(ConnTimer::KeyDiscard) +6882 ) +6883 }) +6884 .min_by_key(|(_, time)| *time) +6885 .is_none_or(|(timer, _)| { +6886 matches!( +6887 timer, +6888 Timer::Conn(ConnTimer::Idle) | Timer::PerPath(_, PathTimer::PathIdle) +6889 ) +6890 }) +6891 } +6892 +6893 /// Whether explicit congestion notification is in use on outgoing packets. +6894 #[cfg(test)] +6895 pub(crate) fn using_ecn(&self) -> bool { +6896 self.path_data(PathId::ZERO).sending_ecn +6897 } +6898 +6899 /// The number of received bytes in the current path +6900 #[cfg(test)] +6901 pub(crate) fn total_recvd(&self) -> u64 { +6902 self.path_data(PathId::ZERO).total_recvd +6903 } +6904 +6905 #[cfg(test)] +6906 pub(crate) fn active_local_cid_seq(&self) -> (u64, u64) { +6907 self.local_cid_state +6908 .get(&PathId::ZERO) +6909 .unwrap() +6910 .active_seq() +6911 } +6912 +6913 #[cfg(test)] +6914 #[track_caller] +6915 pub(crate) fn active_local_path_cid_seq(&self, path_id: u32) -> (u64, u64) { +6916 self.local_cid_state +6917 .get(&PathId(path_id)) +6918 .unwrap() +6919 .active_seq() +6920 } +6921 +6922 /// Instruct the peer to replace previously issued CIDs by sending a NEW_CONNECTION_ID frame +6923 /// with updated `retire_prior_to` field set to `v` +6924 #[cfg(test)] +6925 pub(crate) fn rotate_local_cid(&mut self, v: u64, now: Instant) { +6926 let n = self +6927 .local_cid_state +6928 .get_mut(&PathId::ZERO) +6929 .unwrap() +6930 .assign_retire_seq(v); +6931 debug_assert!(!self.state.is_drained()); // requirement for endpoint_events +6932 self.endpoint_events +6933 .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n)); +6934 } +6935 +6936 /// Check the current active remote CID sequence for `PathId::ZERO` +6937 #[cfg(test)] +6938 pub(crate) fn active_remote_cid_seq(&self) -> u64 { +6939 self.remote_cids.get(&PathId::ZERO).unwrap().active_seq() +6940 } +6941 +6942 /// Returns the detected maximum udp payload size for the current path +6943 #[cfg(test)] +6944 pub(crate) fn path_mtu(&self, path_id: PathId) -> u16 { +6945 self.path_data(path_id).current_mtu() +6946 } +6947 +6948 /// Triggers path validation on all paths +6949 #[cfg(test)] +6950 pub(crate) fn trigger_path_validation(&mut self) { +6951 for path in self.paths.values_mut() { +6952 path.data.pending_challenge = true; +6953 } +6954 } +6955 +6956 /// Simulates a protocol violation error for test purposes. +6957 #[cfg(test)] +6958 pub fn simulate_protocol_violation(&mut self, now: Instant) { +6959 if !self.state.is_closed() { +6960 self.state +6961 .move_to_closed(TransportError::PROTOCOL_VIOLATION("simulated violation")); +6962 self.close_common(); +6963 if !self.state.is_drained() { +6964 self.set_close_timer(now); +6965 } +6966 self.connection_close_pending = true; +6967 } +6968 } +6969 +6970 /// Whether we have **on-path** 1-RTT data to send. +6971 /// +6972 /// This checks for frames that can only be sent in the data space (1-RTT): +6973 /// - Pending PATH_CHALLENGE frames on the active and previous path if just migrated. +6974 /// - Pending PATH_RESPONSE frames. +6975 /// - Pending data to send in STREAM frames. +6976 /// - Pending DATAGRAM frames to send. +6977 /// +6978 /// See also [`PacketSpace::can_send`] which keeps track of all other frame types that +6979 /// may need to be sent. +6980 fn can_send_1rtt(&self, path_id: PathId, max_size: usize) -> SendableFrames { +6981 let network_path = self.path_data(path_id).network_path; +6982 let space_specific = self +6983 .paths +6984 .get(&path_id) +6985 .is_some_and(|path| path.data.pending_challenge) +6986 || self.spaces[SpaceKind::Data] +6987 .number_spaces +6988 .get(&path_id) +6989 .is_some_and(|pns| pns.pending_path_responses.has_pending_on_path(network_path)); +6990 +6991 // Stream control frames are checked in PacketSpace::can_send, only check data here. +6992 let other = self.streams.can_send_stream_data() +6993 || self +6994 .datagrams +6995 .outgoing +6996 .front() +6997 .is_some_and(|x| x.size(true) <= max_size); +6998 +6999 // All `false` fields are set in PacketSpace::can_send. +7000 SendableFrames { +7001 acks: false, +7002 close: false, +7003 space_specific, +7004 other, +7005 } 7006 } 7007 -7008 /// Size of non-frame data for a 1-RTT packet -7009 /// -7010 /// Quantifies space consumed by the QUIC header and AEAD tag. All other bytes in a packet are -7011 /// frames. Changes if the length of the remote connection ID changes, which is expected to be -7012 /// rare. If `pn` is specified, may additionally change unpredictably due to variations in -7013 /// latency and packet loss. -7014 fn predict_1rtt_overhead(&mut self, pn: u64, path: PathId) -> usize { -7015 let pn_len = PacketNumber::new( -7016 pn, -7017 self.spaces[SpaceId::Data] -7018 .for_path(path) -7019 .largest_acked_packet_pn -7020 .unwrap_or(0), -7021 ) -7022 .len(); -7023 -7024 // 1 byte for flags -7025 1 + self -7026 .remote_cids -7027 .get(&path) -7028 .map(|cids| cids.active().len()) -7029 .unwrap_or(20) // Max CID len in QUIC v1 -7030 + pn_len -7031 + self.tag_len_1rtt() -7032 } -7033 -7034 fn predict_1rtt_overhead_no_pn(&self) -> usize { -7035 let pn_len = 4; -7036 -7037 let cid_len = self -7038 .remote_cids -7039 .values() -7040 .map(|cids| cids.active().len()) -7041 .max() -7042 .unwrap_or(20); // Max CID len in QUIC v1 -7043 -7044 // 1 byte for flags -7045 1 + cid_len + pn_len + self.tag_len_1rtt() -7046 } -7047 -7048 fn tag_len_1rtt(&self) -> usize { -7049 // encryption_keys for Data space returns 1-RTT keys if available, otherwise 0-RTT keys -7050 let packet_crypto = self -7051 .crypto_state -7052 .encryption_keys(SpaceKind::Data, self.side.side()) -7053 .map(|(_header, packet, _level)| packet); -7054 // If neither Data nor 0-RTT keys are available, make a reasonable tag length guess. As of -7055 // this writing, all QUIC cipher suites use 16-byte tags. We could return `None` instead, -7056 // but that would needlessly prevent sending datagrams during 0-RTT. -7057 packet_crypto.map_or(16, |x| x.tag_len()) -7058 } -7059 -7060 /// Mark the path as validated, and enqueue NEW_TOKEN frames to be sent as appropriate -7061 fn on_path_validated(&mut self, path_id: PathId) { -7062 self.path_data_mut(path_id).validated = true; -7063 let ConnectionSide::Server { server_config } = &self.side else { -7064 return; -7065 }; -7066 let network_path = self.path_data(path_id).network_path; -7067 let new_tokens = &mut self.spaces[SpaceId::Data as usize].pending.new_tokens; -7068 new_tokens.clear(); -7069 for _ in 0..server_config.validation_token.sent { -7070 new_tokens.push(network_path); -7071 } -7072 } -7073 -7074 /// Handle new path status information: PATH_STATUS_AVAILABLE, PATH_STATUS_BACKUP -7075 fn on_path_status(&mut self, path_id: PathId, status: PathStatus, status_seq_no: VarInt) { -7076 if let Some(path) = self.paths.get_mut(&path_id) { -7077 path.data.status.remote_update(status, status_seq_no); -7078 } else { -7079 debug!("PATH_STATUS_AVAILABLE received unknown path {:?}", path_id); -7080 } -7081 self.events.push_back( -7082 PathEvent::RemoteStatus { -7083 id: path_id, -7084 status, -7085 } -7086 .into(), -7087 ); -7088 } -7089 -7090 /// Returns the maximum [`PathId`] to be used for sending in this connection. -7091 /// -7092 /// This is calculated as minimum between the local and remote's maximums when multipath is -7093 /// enabled, or `None` when disabled. -7094 /// -7095 /// For data that's received, we should use [`Self::local_max_path_id`] instead. -7096 /// The reasoning is that the remote might already have updated to its own newer -7097 /// [`Self::max_path_id`] after sending out a `MAX_PATH_ID` frame, but it got re-ordered. -7098 fn max_path_id(&self) -> Option<PathId> { -7099 if self.is_multipath_negotiated() { -7100 Some(self.remote_max_path_id.min(self.local_max_path_id)) -7101 } else { -7102 None -7103 } -7104 } -7105 -7106 /// Returns whether this connection has a socket that supports IPv6. -7107 /// -7108 /// TODO(matheus23): This is related to noq endpoint state's `ipv6` bool. We should move that info -7109 /// here instead of trying to hack around not knowing it exactly. -7110 pub(crate) fn is_ipv6(&self) -> bool { -7111 self.paths -7112 .values() -7113 .any(|p| p.data.network_path.remote.is_ipv6()) -7114 } -7115 -7116 /// Add addresses the local endpoint considers are reachable for nat traversal. -7117 pub fn add_nat_traversal_address( -7118 &mut self, -7119 address: SocketAddr, -7120 ) -> Result<(), n0_nat_traversal::Error> { -7121 if let Some(added) = self.n0_nat_traversal.add_local_address(address)? { -7122 self.spaces[SpaceId::Data].pending.add_address.insert(added); -7123 }; -7124 Ok(()) -7125 } -7126 -7127 /// Removes an address the endpoing no longer considers reachable for nat traversal -7128 /// -7129 /// Addresses not present in the set will be silently ignored. -7130 pub fn remove_nat_traversal_address( -7131 &mut self, -7132 address: SocketAddr, -7133 ) -> Result<(), n0_nat_traversal::Error> { -7134 if let Some(removed) = self.n0_nat_traversal.remove_local_address(address)? { -7135 self.spaces[SpaceId::Data] -7136 .pending -7137 .remove_address -7138 .insert(removed); -7139 } -7140 Ok(()) +7008 /// Terminate the connection instantly, without sending a close packet +7009 fn kill(&mut self, reason: ConnectionError) { +7010 self.close_common(); +7011 let was_draining = self.state.move_to_drained(Some(reason)); +7012 if !was_draining { +7013 self.endpoint_events.push_back(EndpointEventInner::Draining); +7014 } +7015 // move_to_drained checks that we were never in drained before, so we +7016 // never sent a `Drained` event before (it's illegal to send more events after drained). +7017 self.endpoint_events.push_back(EndpointEventInner::Drained); +7018 } +7019 +7020 /// Storage size required for the largest packet that can be transmitted on all currently +7021 /// available paths +7022 /// +7023 /// Buffers passed to [`Connection::poll_transmit`] should be at least this large. +7024 /// +7025 /// When multipath is enabled, this value is the minimum MTU across all available paths. +7026 pub fn current_mtu(&self) -> u16 { +7027 self.paths +7028 .iter() +7029 .filter(|&(path_id, _path_state)| !self.abandoned_paths.contains(path_id)) +7030 .map(|(_path_id, path_state)| path_state.data.current_mtu()) +7031 .min() +7032 .unwrap_or(INITIAL_MTU) +7033 } +7034 +7035 /// Size of non-frame data for a 1-RTT packet +7036 /// +7037 /// Quantifies space consumed by the QUIC header and AEAD tag. All other bytes in a packet are +7038 /// frames. Changes if the length of the remote connection ID changes, which is expected to be +7039 /// rare. If `pn` is specified, may additionally change unpredictably due to variations in +7040 /// latency and packet loss. +7041 fn predict_1rtt_overhead(&mut self, pn: u64, path: PathId) -> usize { +7042 let pn_len = PacketNumber::new( +7043 pn, +7044 self.spaces[SpaceId::Data] +7045 .for_path(path) +7046 .largest_acked_packet_pn +7047 .unwrap_or(0), +7048 ) +7049 .len(); +7050 +7051 // 1 byte for flags +7052 1 + self +7053 .remote_cids +7054 .get(&path) +7055 .map(|cids| cids.active().len()) +7056 .unwrap_or(20) // Max CID len in QUIC v1 +7057 + pn_len +7058 + self.tag_len_1rtt() +7059 } +7060 +7061 fn predict_1rtt_overhead_no_pn(&self) -> usize { +7062 let pn_len = 4; +7063 +7064 let cid_len = self +7065 .remote_cids +7066 .values() +7067 .map(|cids| cids.active().len()) +7068 .max() +7069 .unwrap_or(20); // Max CID len in QUIC v1 +7070 +7071 // 1 byte for flags +7072 1 + cid_len + pn_len + self.tag_len_1rtt() +7073 } +7074 +7075 fn tag_len_1rtt(&self) -> usize { +7076 // encryption_keys for Data space returns 1-RTT keys if available, otherwise 0-RTT keys +7077 let packet_crypto = self +7078 .crypto_state +7079 .encryption_keys(SpaceKind::Data, self.side.side()) +7080 .map(|(_header, packet, _level)| packet); +7081 // If neither Data nor 0-RTT keys are available, make a reasonable tag length guess. As of +7082 // this writing, all QUIC cipher suites use 16-byte tags. We could return `None` instead, +7083 // but that would needlessly prevent sending datagrams during 0-RTT. +7084 packet_crypto.map_or(16, |x| x.tag_len()) +7085 } +7086 +7087 /// Mark the path as validated, and enqueue NEW_TOKEN frames to be sent as appropriate +7088 fn on_path_validated(&mut self, path_id: PathId) { +7089 self.path_data_mut(path_id).validated = true; +7090 let ConnectionSide::Server { server_config } = &self.side else { +7091 return; +7092 }; +7093 let network_path = self.path_data(path_id).network_path; +7094 let new_tokens = &mut self.spaces[SpaceId::Data as usize].pending.new_tokens; +7095 new_tokens.clear(); +7096 for _ in 0..server_config.validation_token.sent { +7097 new_tokens.push(network_path); +7098 } +7099 } +7100 +7101 /// Handle new path status information: PATH_STATUS_AVAILABLE, PATH_STATUS_BACKUP +7102 fn on_path_status(&mut self, path_id: PathId, status: PathStatus, status_seq_no: VarInt) { +7103 if let Some(path) = self.paths.get_mut(&path_id) { +7104 path.data.status.remote_update(status, status_seq_no); +7105 } else { +7106 debug!("PATH_STATUS_AVAILABLE received unknown path {:?}", path_id); +7107 } +7108 self.events.push_back( +7109 PathEvent::RemoteStatus { +7110 id: path_id, +7111 status, +7112 } +7113 .into(), +7114 ); +7115 } +7116 +7117 /// Returns the maximum [`PathId`] to be used for sending in this connection. +7118 /// +7119 /// This is calculated as minimum between the local and remote's maximums when multipath is +7120 /// enabled, or `None` when disabled. +7121 /// +7122 /// For data that's received, we should use [`Self::local_max_path_id`] instead. +7123 /// The reasoning is that the remote might already have updated to its own newer +7124 /// [`Self::max_path_id`] after sending out a `MAX_PATH_ID` frame, but it got re-ordered. +7125 fn max_path_id(&self) -> Option<PathId> { +7126 if self.is_multipath_negotiated() { +7127 Some(self.remote_max_path_id.min(self.local_max_path_id)) +7128 } else { +7129 None +7130 } +7131 } +7132 +7133 /// Returns whether this connection has a socket that supports IPv6. +7134 /// +7135 /// TODO(matheus23): This is related to noq endpoint state's `ipv6` bool. We should move that info +7136 /// here instead of trying to hack around not knowing it exactly. +7137 pub(crate) fn is_ipv6(&self) -> bool { +7138 self.paths +7139 .values() +7140 .any(|p| p.data.network_path.remote.is_ipv6()) 7141 } 7142 -7143 /// Get the current local nat traversal addresses -7144 pub fn get_local_nat_traversal_addresses( -7145 &self, -7146 ) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> { -7147 self.n0_nat_traversal.get_local_nat_traversal_addresses() -7148 } -7149 -7150 /// Get the currently advertised nat traversal addresses by the server -7151 pub fn get_remote_nat_traversal_addresses( -7152 &self, -7153 ) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> { -7154 Ok(self -7155 .n0_nat_traversal -7156 .client_side()? -7157 .get_remote_nat_traversal_addresses()) -7158 } -7159 -7160 /// Initiates a new nat traversal round -7161 /// -7162 /// A nat traversal round involves advertising the client's local addresses in -7163 /// `REACH_OUT` frames, and initiating probing of the known remote addresses. When a new -7164 /// round is initiated, the previous one is cancelled. -7165 /// -7166 /// For all probes that succeed, if any, a new path will be opened on the successful -7167 /// 4-tuple. -7168 /// -7169 /// Returns the server addresses that are now being probed. If addresses fail due to -7170 /// spurious errors, these might succeed later and not be returned in this set. -7171 pub fn initiate_nat_traversal_round( -7172 &mut self, -7173 now: Instant, -7174 ) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> { -7175 if self.state.is_closed() { -7176 return Err(n0_nat_traversal::Error::Closed); -7177 } -7178 -7179 let ipv6 = self.is_ipv6(); -7180 let client_state = self.n0_nat_traversal.client_side_mut()?; -7181 let (mut reach_out_frames, probed_addrs) = -7182 client_state.initiate_nat_traversal_round(ipv6)?; -7183 if let Some(delay) = self.n0_nat_traversal.retry_delay(self.config.initial_rtt) { -7184 self.timers.set( -7185 Timer::Conn(ConnTimer::NatTraversalProbeRetry), -7186 now + delay, -7187 self.qlog.with_time(now), -7188 ); -7189 } -7190 -7191 self.spaces[SpaceId::Data] -7192 .pending -7193 .reach_out -7194 .append(&mut reach_out_frames); -7195 -7196 Ok(probed_addrs) -7197 } -7198 -7199 /// Whether the handshake is considered **confirmed**. -7200 /// -7201 /// <https://www.rfc-editor.org/rfc/rfc9001#section-4.1.2> defines a handshake to be -7202 /// confirmed when you know the peer successfully received and successfully processed -7203 /// your TLS Finished message. -7204 /// -7205 /// Implementation-wise this is the point at which the handshake crypto keys are -7206 /// discarded. So we can use this to know if the handshake is confirmed. -7207 fn is_handshake_confirmed(&self) -> bool { -7208 !self.is_handshaking() && !self.crypto_state.has_keys(EncryptionLevel::Handshake) -7209 } -7210} -7211 -7212impl fmt::Debug for Connection { -7213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -7214 f.debug_struct("Connection") -7215 .field("handshake_cid", &self.handshake_cid) -7216 .finish() -7217 } -7218} -7219 -7220/// The set of abandoned paths. -7221/// -7222/// Implementation based on ArrayRangeSet to share more code. The memory space is -7223/// proportional to the number of concurrently open paths allowed. So does not grow -7224/// unbounded. -7225#[derive(Debug, Default)] -7226struct AbandonedPaths(ArrayRangeSet<ABANDONED_PATH_INLINE_RANGES, u32>); -7227 -7228/// Size of the stack-allocated array in the [`ArrayRangeSet`]. -7229/// -7230/// A range is 2 u32's, so this is 16 * (4 + 4) = 128 bytes. A good size for inline data, -7231/// with plenty of ranges for common multipath use. -7232const ABANDONED_PATH_INLINE_RANGES: usize = 16; -7233 -7234impl AbandonedPaths { -7235 /// The number of abandoned paths. -7236 fn len(&self) -> u32 { -7237 self.0.elts_count() -7238 } -7239 -7240 /// The largest abandoned path. -7241 fn max(&self) -> Option<PathId> { -7242 self.0.max().map(PathId::from) -7243 } -7244 -7245 /// Whether the path is already abandoned. -7246 fn contains(&self, val: &PathId) -> bool { -7247 self.0.contains(val.as_u32()) -7248 } -7249 -7250 /// Adds another abandoned path. -7251 fn insert(&mut self, val: PathId) { -7252 self.0.insert_one(val.as_u32()); -7253 } -7254} -7255 -7256/// Hints when the caller identifies a network change. -7257pub trait NetworkChangeHint: std::fmt::Debug + 'static { -7258 /// Inform the connection if a path may recover after a network change. -7259 /// -7260 /// After network changes, paths may not be recoverable. In this case, waiting for the path to -7261 /// become idle may take longer than what is desirable. If [`Self::is_path_recoverable`] -7262 /// returns `false`, a multipath-enabled, client-side connection will establish a new path to -7263 /// the same remote, closing the current one, instead of migrating the path. -7264 /// -7265 /// Paths that are deemed recoverable will simply be sent a PING for a liveness check. -7266 fn is_path_recoverable(&self, path_id: PathId, network_path: FourTuple) -> bool; -7267} -7268 -7269/// Return value for [`Connection::poll_transmit_path_space`]. -7270#[derive(Debug)] -7271enum PollPathSpaceStatus { -7272 /// Nothing to send in the space, nothing was written into the [`TransmitBuf`]. -7273 NothingToSend { -7274 /// If true there was data to send but congestion control did not allow so. -7275 congestion_blocked: bool, -7276 }, -7277 /// One or more packets have been written into the [`TransmitBuf`]. -7278 WrotePacket { -7279 /// The highest packet number. -7280 last_packet_number: u64, -7281 /// Whether to pad an already started datagram in the next packet. -7282 /// -7283 /// When packets in Initial, 0-RTT or Handshake packet do not fill the entire -7284 /// datagram they may decide to coalesce with the next packet from a higher -7285 /// encryption level on the same path. But the earlier packet may require specific -7286 /// size requirements for the datagram they are sent in. -7287 /// -7288 /// If a space did not complete the datagram, they use this to request the correct -7289 /// padding in the final packet of the datagram so that the final datagram will have -7290 /// the correct size. -7291 /// -7292 /// If a space did fill an entire datagram, it leaves this to the default of -7293 /// [`PadDatagram::No`]. -7294 pad_datagram: PadDatagram, -7295 }, -7296 /// Send the contents of the transmit immediately. -7297 /// -7298 /// Packets were written and the GSO batch must end now, regardless from whether higher -7299 /// spaces still have frames to write. This is used when the last datagram written would -7300 /// require too much padding to continue a GSO batch, which would waste space on the -7301 /// wire. -7302 Send { -7303 /// The highest packet number written into the transmit. -7304 last_packet_number: u64, -7305 }, -7306} -7307 -7308/// Information used to decide what frames to schedule into which packets. -7309/// -7310/// Primarily used by [`Connection::poll_transmit_on_path`] and the functions that help -7311/// building packets for it: [`Connection::poll_transmit_path_space`] and -7312/// [`Connection::populate_packet`]. -7313#[derive(Debug, Copy, Clone)] -7314struct PathSchedulingInfo { -7315 /// Whether the path is abandoned. -7316 /// -7317 /// Note that a path that is abandoned but still has CIDs can still send a packet. After -7318 /// sending that packet the CIDs issued by the remote have to be considered retired as -7319 /// well. -7320 is_abandoned: bool, -7321 /// Whether the path may send [`SpaceKind::Data`] frames. -7322 /// -7323 /// Some paths should only send frames from [`SendableFrames::space_specific`]. All other -7324 /// frames are essentially frames that can be sent on any [`SpaceKind::Data`] space. For -7325 /// those we want to respect packet scheduling rules however. -7326 /// -7327 /// Roughly speaking data frames are only sent on spaces that have CIDs, are not -7328 /// abandoned and have no *better* spaces. However see to comments where this is -7329 /// populated for the exact packet scheduling implementation. -7330 /// -7331 /// This essentially marks this paths as the best validated space ID. Except during -7332 /// the handshake in which case it does not need to be validated. Several paths could be -7333 /// equally good and all have this set to `true`, in that case packet scheduling can -7334 /// choose which path to use. Currently it chooses the lowest path that is not -7335 /// congestion blocked. -7336 /// -7337 /// Note that once in the closed or draining states this will never be true. -7338 may_send_data: bool, -7339 /// Whether the path may send a CONNECTION_CLOSE frame. -7340 /// -7341 /// This essentially marks this path as the best validated space ID with a fallback -7342 /// to unvalidated spaces if there are no validated spaces. Like for -7343 /// [`Self::may_send_data`] other paths could be equally good. -7344 may_send_close: bool, -7345 may_self_abandon: bool, -7346} -7347 -7348#[derive(Debug, Copy, Clone, PartialEq, Eq)] -7349enum PathBlocked { -7350 No, -7351 AntiAmplification, -7352 Congestion, -7353 Pacing, -7354} -7355 -7356/// Fields of `Connection` specific to it being client-side or server-side -7357enum ConnectionSide { -7358 Client { -7359 /// Sent in every outgoing Initial packet. Always empty after Initial keys are discarded -7360 token: Bytes, -7361 token_store: Arc<dyn TokenStore>, -7362 server_name: String, -7363 }, -7364 Server { -7365 server_config: Arc<ServerConfig>, -7366 }, -7367} -7368 -7369impl ConnectionSide { -7370 fn is_client(&self) -> bool { -7371 self.side().is_client() -7372 } -7373 -7374 fn is_server(&self) -> bool { -7375 self.side().is_server() -7376 } -7377 -7378 fn side(&self) -> Side { -7379 match *self { -7380 Self::Client { .. } => Side::Client, -7381 Self::Server { .. } => Side::Server, -7382 } -7383 } -7384} -7385 -7386impl From<SideArgs> for ConnectionSide { -7387 fn from(side: SideArgs) -> Self { -7388 match side { -7389 SideArgs::Client { -7390 token_store, -7391 server_name, -7392 } => Self::Client { -7393 token: token_store.take(&server_name).unwrap_or_default(), -7394 token_store, -7395 server_name, -7396 }, -7397 SideArgs::Server { -7398 server_config, -7399 pref_addr_cid: _, -7400 path_validated: _, -7401 } => Self::Server { server_config }, -7402 } +7143 /// Add addresses the local endpoint considers are reachable for nat traversal. +7144 pub fn add_nat_traversal_address( +7145 &mut self, +7146 address: SocketAddr, +7147 ) -> Result<(), n0_nat_traversal::Error> { +7148 if let Some(added) = self.n0_nat_traversal.add_local_address(address)? { +7149 self.spaces[SpaceId::Data].pending.add_address.insert(added); +7150 }; +7151 Ok(()) +7152 } +7153 +7154 /// Removes an address the endpoing no longer considers reachable for nat traversal +7155 /// +7156 /// Addresses not present in the set will be silently ignored. +7157 pub fn remove_nat_traversal_address( +7158 &mut self, +7159 address: SocketAddr, +7160 ) -> Result<(), n0_nat_traversal::Error> { +7161 if let Some(removed) = self.n0_nat_traversal.remove_local_address(address)? { +7162 self.spaces[SpaceId::Data] +7163 .pending +7164 .remove_address +7165 .insert(removed); +7166 } +7167 Ok(()) +7168 } +7169 +7170 /// Get the current local nat traversal addresses +7171 pub fn get_local_nat_traversal_addresses( +7172 &self, +7173 ) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> { +7174 self.n0_nat_traversal.get_local_nat_traversal_addresses() +7175 } +7176 +7177 /// Get the currently advertised nat traversal addresses by the server +7178 pub fn get_remote_nat_traversal_addresses( +7179 &self, +7180 ) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> { +7181 Ok(self +7182 .n0_nat_traversal +7183 .client_side()? +7184 .get_remote_nat_traversal_addresses()) +7185 } +7186 +7187 /// Initiates a new nat traversal round +7188 /// +7189 /// A nat traversal round involves advertising the client's local addresses in +7190 /// `REACH_OUT` frames, and initiating probing of the known remote addresses. When a new +7191 /// round is initiated, the previous one is cancelled. +7192 /// +7193 /// For all probes that succeed, if any, a new path will be opened on the successful +7194 /// 4-tuple. +7195 /// +7196 /// Returns the server addresses that are now being probed. If addresses fail due to +7197 /// spurious errors, these might succeed later and not be returned in this set. +7198 pub fn initiate_nat_traversal_round( +7199 &mut self, +7200 now: Instant, +7201 ) -> Result<Vec<SocketAddr>, n0_nat_traversal::Error> { +7202 if self.state.is_closed() { +7203 return Err(n0_nat_traversal::Error::Closed); +7204 } +7205 +7206 let ipv6 = self.is_ipv6(); +7207 let client_state = self.n0_nat_traversal.client_side_mut()?; +7208 let (mut reach_out_frames, probed_addrs) = +7209 client_state.initiate_nat_traversal_round(ipv6)?; +7210 if let Some(delay) = self.n0_nat_traversal.retry_delay(self.config.initial_rtt) { +7211 self.timers.set( +7212 Timer::Conn(ConnTimer::NatTraversalProbeRetry), +7213 now + delay, +7214 self.qlog.with_time(now), +7215 ); +7216 } +7217 +7218 self.spaces[SpaceId::Data] +7219 .pending +7220 .reach_out +7221 .append(&mut reach_out_frames); +7222 +7223 Ok(probed_addrs) +7224 } +7225 +7226 /// Whether the handshake is considered **confirmed**. +7227 /// +7228 /// <https://www.rfc-editor.org/rfc/rfc9001#section-4.1.2> defines a handshake to be +7229 /// confirmed when you know the peer successfully received and successfully processed +7230 /// your TLS Finished message. +7231 /// +7232 /// Implementation-wise this is the point at which the handshake crypto keys are +7233 /// discarded. So we can use this to know if the handshake is confirmed. +7234 fn is_handshake_confirmed(&self) -> bool { +7235 !self.is_handshaking() && !self.crypto_state.has_keys(EncryptionLevel::Handshake) +7236 } +7237} +7238 +7239impl fmt::Debug for Connection { +7240 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +7241 f.debug_struct("Connection") +7242 .field("handshake_cid", &self.handshake_cid) +7243 .finish() +7244 } +7245} +7246 +7247/// The set of abandoned paths. +7248/// +7249/// Implementation based on ArrayRangeSet to share more code. The memory space is +7250/// proportional to the number of concurrently open paths allowed. So does not grow +7251/// unbounded. +7252#[derive(Debug, Default)] +7253struct AbandonedPaths(ArrayRangeSet<ABANDONED_PATH_INLINE_RANGES, u32>); +7254 +7255/// Size of the stack-allocated array in the [`ArrayRangeSet`]. +7256/// +7257/// A range is 2 u32's, so this is 16 * (4 + 4) = 128 bytes. A good size for inline data, +7258/// with plenty of ranges for common multipath use. +7259const ABANDONED_PATH_INLINE_RANGES: usize = 16; +7260 +7261impl AbandonedPaths { +7262 /// The number of abandoned paths. +7263 fn len(&self) -> u32 { +7264 self.0.elts_count() +7265 } +7266 +7267 /// The largest abandoned path. +7268 fn max(&self) -> Option<PathId> { +7269 self.0.max().map(PathId::from) +7270 } +7271 +7272 /// Whether the path is already abandoned. +7273 fn contains(&self, val: &PathId) -> bool { +7274 self.0.contains(val.as_u32()) +7275 } +7276 +7277 /// Adds another abandoned path. +7278 fn insert(&mut self, val: PathId) { +7279 self.0.insert_one(val.as_u32()); +7280 } +7281} +7282 +7283/// Hints when the caller identifies a network change. +7284pub trait NetworkChangeHint: std::fmt::Debug + 'static { +7285 /// Inform the connection if a path may recover after a network change. +7286 /// +7287 /// After network changes, paths may not be recoverable. In this case, waiting for the path to +7288 /// become idle may take longer than what is desirable. If [`Self::is_path_recoverable`] +7289 /// returns `false`, a multipath-enabled, client-side connection will establish a new path to +7290 /// the same remote, closing the current one, instead of migrating the path. +7291 /// +7292 /// Paths that are deemed recoverable will simply be sent a PING for a liveness check. +7293 fn is_path_recoverable(&self, path_id: PathId, network_path: FourTuple) -> bool; +7294} +7295 +7296/// Return value for [`Connection::poll_transmit_path_space`]. +7297#[derive(Debug)] +7298enum PollPathSpaceStatus { +7299 /// Nothing to send in the space, nothing was written into the [`TransmitBuf`]. +7300 NothingToSend { +7301 /// If true there was data to send but congestion control did not allow so. +7302 congestion_blocked: bool, +7303 }, +7304 /// One or more packets have been written into the [`TransmitBuf`]. +7305 WrotePacket { +7306 /// The highest packet number. +7307 last_packet_number: u64, +7308 /// Whether to pad an already started datagram in the next packet. +7309 /// +7310 /// When packets in Initial, 0-RTT or Handshake packet do not fill the entire +7311 /// datagram they may decide to coalesce with the next packet from a higher +7312 /// encryption level on the same path. But the earlier packet may require specific +7313 /// size requirements for the datagram they are sent in. +7314 /// +7315 /// If a space did not complete the datagram, they use this to request the correct +7316 /// padding in the final packet of the datagram so that the final datagram will have +7317 /// the correct size. +7318 /// +7319 /// If a space did fill an entire datagram, it leaves this to the default of +7320 /// [`PadDatagram::No`]. +7321 pad_datagram: PadDatagram, +7322 }, +7323 /// Send the contents of the transmit immediately. +7324 /// +7325 /// Packets were written and the GSO batch must end now, regardless from whether higher +7326 /// spaces still have frames to write. This is used when the last datagram written would +7327 /// require too much padding to continue a GSO batch, which would waste space on the +7328 /// wire. +7329 Send { +7330 /// The highest packet number written into the transmit. +7331 last_packet_number: u64, +7332 }, +7333} +7334 +7335/// Information used to decide what frames to schedule into which packets. +7336/// +7337/// Primarily used by [`Connection::poll_transmit_on_path`] and the functions that help +7338/// building packets for it: [`Connection::poll_transmit_path_space`] and +7339/// [`Connection::populate_packet`]. +7340#[derive(Debug, Copy, Clone)] +7341struct PathSchedulingInfo { +7342 /// Whether the path is abandoned. +7343 /// +7344 /// Note that a path that is abandoned but still has CIDs can still send a packet. After +7345 /// sending that packet the CIDs issued by the remote have to be considered retired as +7346 /// well. +7347 is_abandoned: bool, +7348 /// Whether the path may send [`SpaceKind::Data`] frames. +7349 /// +7350 /// Some paths should only send frames from [`SendableFrames::space_specific`]. All other +7351 /// frames are essentially frames that can be sent on any [`SpaceKind::Data`] space. For +7352 /// those we want to respect packet scheduling rules however. +7353 /// +7354 /// Roughly speaking data frames are only sent on spaces that have CIDs, are not +7355 /// abandoned and have no *better* spaces. However see to comments where this is +7356 /// populated for the exact packet scheduling implementation. +7357 /// +7358 /// This essentially marks this paths as the best validated space ID. Except during +7359 /// the handshake in which case it does not need to be validated. Several paths could be +7360 /// equally good and all have this set to `true`, in that case packet scheduling can +7361 /// choose which path to use. Currently it chooses the lowest path that is not +7362 /// congestion blocked. +7363 /// +7364 /// Note that once in the closed or draining states this will never be true. +7365 may_send_data: bool, +7366 /// Whether the path may send a CONNECTION_CLOSE frame. +7367 /// +7368 /// This essentially marks this path as the best validated space ID with a fallback +7369 /// to unvalidated spaces if there are no validated spaces. Like for +7370 /// [`Self::may_send_data`] other paths could be equally good. +7371 may_send_close: bool, +7372 may_self_abandon: bool, +7373} +7374 +7375#[derive(Debug, Copy, Clone, PartialEq, Eq)] +7376enum PathBlocked { +7377 No, +7378 AntiAmplification, +7379 Congestion, +7380 Pacing, +7381} +7382 +7383/// Fields of `Connection` specific to it being client-side or server-side +7384enum ConnectionSide { +7385 Client { +7386 /// Sent in every outgoing Initial packet. Always empty after Initial keys are discarded +7387 token: Bytes, +7388 token_store: Arc<dyn TokenStore>, +7389 server_name: String, +7390 }, +7391 Server { +7392 server_config: Arc<ServerConfig>, +7393 }, +7394} +7395 +7396impl ConnectionSide { +7397 fn is_client(&self) -> bool { +7398 self.side().is_client() +7399 } +7400 +7401 fn is_server(&self) -> bool { +7402 self.side().is_server() 7403 } -7404} -7405 -7406/// Parameters to `Connection::new` specific to it being client-side or server-side -7407pub(crate) enum SideArgs { -7408 Client { -7409 token_store: Arc<dyn TokenStore>, -7410 server_name: String, -7411 }, -7412 Server { -7413 server_config: Arc<ServerConfig>, -7414 pref_addr_cid: Option<ConnectionId>, -7415 path_validated: bool, -7416 }, -7417} -7418 -7419impl SideArgs { -7420 pub(crate) fn pref_addr_cid(&self) -> Option<ConnectionId> { -7421 match *self { -7422 Self::Client { .. } => None, -7423 Self::Server { pref_addr_cid, .. } => pref_addr_cid, -7424 } -7425 } -7426 -7427 pub(crate) fn path_validated(&self) -> bool { -7428 match *self { -7429 Self::Client { .. } => true, -7430 Self::Server { path_validated, .. } => path_validated, -7431 } -7432 } -7433 -7434 pub(crate) fn side(&self) -> Side { -7435 match *self { -7436 Self::Client { .. } => Side::Client, -7437 Self::Server { .. } => Side::Server, -7438 } -7439 } -7440} -7441 -7442/// Reasons why a connection might be lost -7443#[derive(Debug, Error, Clone, PartialEq, Eq)] -7444pub enum ConnectionError { -7445 /// The peer doesn't implement any supported version -7446 #[error("peer doesn't implement any supported version")] -7447 VersionMismatch, -7448 /// The peer violated the QUIC specification as understood by this implementation -7449 #[error(transparent)] -7450 TransportError(#[from] TransportError), -7451 /// The peer's QUIC stack aborted the connection automatically -7452 #[error("aborted by peer: {0}")] -7453 ConnectionClosed(frame::ConnectionClose), -7454 /// The peer closed the connection -7455 #[error("closed by peer: {0}")] -7456 ApplicationClosed(frame::ApplicationClose), -7457 /// The peer is unable to continue processing this connection, usually due to having restarted -7458 #[error("reset by peer")] -7459 Reset, -7460 /// Communication with the peer has lapsed for longer than the negotiated idle timeout -7461 /// -7462 /// If neither side is sending keep-alives, a connection will time out after a long enough idle -7463 /// period even if the peer is still reachable. See also [`TransportConfig::max_idle_timeout()`] -7464 /// and [`TransportConfig::keep_alive_interval()`]. -7465 #[error("timed out")] -7466 TimedOut, -7467 /// The local application closed the connection -7468 #[error("closed")] -7469 LocallyClosed, -7470 /// The connection could not be created because not enough of the CID space is available -7471 /// -7472 /// Try using longer connection IDs. -7473 #[error("CIDs exhausted")] -7474 CidsExhausted, -7475} -7476 -7477impl From<Close> for ConnectionError { -7478 fn from(x: Close) -> Self { -7479 match x { -7480 Close::Connection(reason) => Self::ConnectionClosed(reason), -7481 Close::Application(reason) => Self::ApplicationClosed(reason), -7482 } -7483 } -7484} -7485 -7486// For compatibility with API consumers -7487impl From<ConnectionError> for io::Error { -7488 fn from(x: ConnectionError) -> Self { -7489 use ConnectionError::*; -7490 let kind = match x { -7491 TimedOut => io::ErrorKind::TimedOut, -7492 Reset => io::ErrorKind::ConnectionReset, -7493 ApplicationClosed(_) | ConnectionClosed(_) => io::ErrorKind::ConnectionAborted, -7494 TransportError(_) | VersionMismatch | LocallyClosed | CidsExhausted => { -7495 io::ErrorKind::Other -7496 } -7497 }; -7498 Self::new(kind, x) -7499 } -7500} -7501 -7502/// Errors that might trigger a path being closed -7503// TODO(@divma): maybe needs to be reworked based on what we want to do with the public API -7504#[derive(Debug, Error, PartialEq, Eq, Clone, Copy)] -7505pub enum PathError { -7506 /// The extension was not negotiated with the peer -7507 #[error("multipath extension not negotiated")] -7508 MultipathNotNegotiated, -7509 /// Paths can only be opened client-side -7510 #[error("the server side may not open a path")] -7511 ServerSideNotAllowed, -7512 /// Current limits do not allow us to open more paths -7513 #[error("maximum number of concurrent paths reached")] -7514 MaxPathIdReached, -7515 /// No remote CIDs available to open a new path -7516 #[error("remoted CIDs exhausted")] -7517 RemoteCidsExhausted, -7518 /// Path could not be validated and will be abandoned -7519 #[error("path validation failed")] -7520 ValidationFailed, -7521 /// The remote address for the path is not supported by the endpoint -7522 #[error("invalid remote address")] -7523 InvalidRemoteAddress(SocketAddr), -7524} -7525 -7526/// Errors triggered when abandoning a path -7527#[derive(Debug, Error, Clone, Eq, PartialEq)] -7528pub enum ClosePathError { -7529 /// Multipath is not negotiated -7530 #[error("Multipath extension not negotiated")] -7531 MultipathNotNegotiated, -7532 /// The path is already closed or was never opened -7533 #[error("closed path")] -7534 ClosedPath, -7535 /// Cannot close the last remaining open path via the local API. -7536 /// -7537 /// Use [`Connection::close`] to end the connection instead. -7538 #[error("last open path")] -7539 LastOpenPath, -7540} -7541 -7542/// Error when the multipath extension was not negotiated, but attempted to be used. -7543#[derive(Debug, Error, Clone, Copy)] -7544#[error("Multipath extension not negotiated")] -7545pub struct MultipathNotNegotiated { -7546 _private: (), -7547} -7548 -7549/// Events of interest to the application -7550#[derive(Debug)] -7551pub enum Event { -7552 /// The connection's handshake data is ready -7553 HandshakeDataReady, -7554 /// The connection was successfully established -7555 Connected, -7556 /// The TLS handshake was confirmed -7557 HandshakeConfirmed, -7558 /// The connection was lost -7559 /// -7560 /// Emitted when the connection is closed due to an error, a timeout, or the peer closing it. -7561 /// This is **not** emitted when the local application closes the connection via -7562 /// [`Connection::close()`](crate::Connection::close). In that case, pending operations will -7563 /// fail with [`ConnectionError::LocallyClosed`]. -7564 ConnectionLost { -7565 /// Reason that the connection was closed -7566 reason: ConnectionError, -7567 }, -7568 /// Stream events -7569 Stream(StreamEvent), -7570 /// One or more application datagrams have been received -7571 DatagramReceived, -7572 /// One or more application datagrams have been sent after blocking -7573 DatagramsUnblocked, -7574 /// (Multi)Path events -7575 Path(PathEvent), -7576 /// n0's nat traversal events -7577 NatTraversal(n0_nat_traversal::Event), -7578} -7579 -7580impl From<PathEvent> for Event { -7581 fn from(source: PathEvent) -> Self { -7582 Self::Path(source) -7583 } -7584} -7585 -7586fn get_max_ack_delay(params: &TransportParameters) -> Duration { -7587 Duration::from_micros(params.max_ack_delay.0 * 1000) -7588} -7589 -7590/// Prevents overflow and improves behavior in extreme circumstances. -7591const MAX_BACKOFF_EXPONENT: u32 = 16; -7592 -7593/// The max interval between successive tail-loss probes. -7594/// -7595/// This is the "normal" value we use. -7596const MAX_PTO_INTERVAL: Duration = Duration::from_secs(2); -7597 -7598/// The idle time, below which we use the shorter [`MAX_PTO_FAST_INTERVAL`]. -7599const MIN_IDLE_FOR_FAST_PTO: Duration = Duration::from_secs(25); -7600 -7601/// The max interval between successive tail-loss probes with short idle times. -7602/// -7603/// If the path or connection idle time is less than [`MIN_IDLE_FOR_FAST_PTO`] then we use -7604/// this value to ensure we have plenty of retransmits before we reach the idle time. -7605const MAX_PTO_FAST_INTERVAL: Duration = Duration::from_secs(1); +7404 +7405 fn side(&self) -> Side { +7406 match *self { +7407 Self::Client { .. } => Side::Client, +7408 Self::Server { .. } => Side::Server, +7409 } +7410 } +7411} +7412 +7413impl From<SideArgs> for ConnectionSide { +7414 fn from(side: SideArgs) -> Self { +7415 match side { +7416 SideArgs::Client { +7417 token_store, +7418 server_name, +7419 } => Self::Client { +7420 token: token_store.take(&server_name).unwrap_or_default(), +7421 token_store, +7422 server_name, +7423 }, +7424 SideArgs::Server { +7425 server_config, +7426 pref_addr_cid: _, +7427 path_validated: _, +7428 } => Self::Server { server_config }, +7429 } +7430 } +7431} +7432 +7433/// Parameters to `Connection::new` specific to it being client-side or server-side +7434pub(crate) enum SideArgs { +7435 Client { +7436 token_store: Arc<dyn TokenStore>, +7437 server_name: String, +7438 }, +7439 Server { +7440 server_config: Arc<ServerConfig>, +7441 pref_addr_cid: Option<ConnectionId>, +7442 path_validated: bool, +7443 }, +7444} +7445 +7446impl SideArgs { +7447 pub(crate) fn pref_addr_cid(&self) -> Option<ConnectionId> { +7448 match *self { +7449 Self::Client { .. } => None, +7450 Self::Server { pref_addr_cid, .. } => pref_addr_cid, +7451 } +7452 } +7453 +7454 pub(crate) fn path_validated(&self) -> bool { +7455 match *self { +7456 Self::Client { .. } => true, +7457 Self::Server { path_validated, .. } => path_validated, +7458 } +7459 } +7460 +7461 pub(crate) fn side(&self) -> Side { +7462 match *self { +7463 Self::Client { .. } => Side::Client, +7464 Self::Server { .. } => Side::Server, +7465 } +7466 } +7467} +7468 +7469/// Reasons why a connection might be lost +7470#[derive(Debug, Error, Clone, PartialEq, Eq)] +7471pub enum ConnectionError { +7472 /// The peer doesn't implement any supported version +7473 #[error("peer doesn't implement any supported version")] +7474 VersionMismatch, +7475 /// The peer violated the QUIC specification as understood by this implementation +7476 #[error(transparent)] +7477 TransportError(#[from] TransportError), +7478 /// The peer's QUIC stack aborted the connection automatically +7479 #[error("aborted by peer: {0}")] +7480 ConnectionClosed(frame::ConnectionClose), +7481 /// The peer closed the connection +7482 #[error("closed by peer: {0}")] +7483 ApplicationClosed(frame::ApplicationClose), +7484 /// The peer is unable to continue processing this connection, usually due to having restarted +7485 #[error("reset by peer")] +7486 Reset, +7487 /// Communication with the peer has lapsed for longer than the negotiated idle timeout +7488 /// +7489 /// If neither side is sending keep-alives, a connection will time out after a long enough idle +7490 /// period even if the peer is still reachable. See also [`TransportConfig::max_idle_timeout()`] +7491 /// and [`TransportConfig::keep_alive_interval()`]. +7492 #[error("timed out")] +7493 TimedOut, +7494 /// The local application closed the connection +7495 #[error("closed")] +7496 LocallyClosed, +7497 /// The connection could not be created because not enough of the CID space is available +7498 /// +7499 /// Try using longer connection IDs. +7500 #[error("CIDs exhausted")] +7501 CidsExhausted, +7502} +7503 +7504impl From<Close> for ConnectionError { +7505 fn from(x: Close) -> Self { +7506 match x { +7507 Close::Connection(reason) => Self::ConnectionClosed(reason), +7508 Close::Application(reason) => Self::ApplicationClosed(reason), +7509 } +7510 } +7511} +7512 +7513// For compatibility with API consumers +7514impl From<ConnectionError> for io::Error { +7515 fn from(x: ConnectionError) -> Self { +7516 use ConnectionError::*; +7517 let kind = match x { +7518 TimedOut => io::ErrorKind::TimedOut, +7519 Reset => io::ErrorKind::ConnectionReset, +7520 ApplicationClosed(_) | ConnectionClosed(_) => io::ErrorKind::ConnectionAborted, +7521 TransportError(_) | VersionMismatch | LocallyClosed | CidsExhausted => { +7522 io::ErrorKind::Other +7523 } +7524 }; +7525 Self::new(kind, x) +7526 } +7527} +7528 +7529/// Errors that might trigger a path being closed +7530// TODO(@divma): maybe needs to be reworked based on what we want to do with the public API +7531#[derive(Debug, Error, PartialEq, Eq, Clone, Copy)] +7532pub enum PathError { +7533 /// The extension was not negotiated with the peer +7534 #[error("multipath extension not negotiated")] +7535 MultipathNotNegotiated, +7536 /// Paths can only be opened client-side +7537 #[error("the server side may not open a path")] +7538 ServerSideNotAllowed, +7539 /// Current limits do not allow us to open more paths +7540 #[error("maximum number of concurrent paths reached")] +7541 MaxPathIdReached, +7542 /// No remote CIDs available to open a new path +7543 #[error("remoted CIDs exhausted")] +7544 RemoteCidsExhausted, +7545 /// Path could not be validated and will be abandoned +7546 #[error("path validation failed")] +7547 ValidationFailed, +7548 /// The remote address for the path is not supported by the endpoint +7549 #[error("invalid remote address")] +7550 InvalidRemoteAddress(SocketAddr), +7551} +7552 +7553/// Errors triggered when abandoning a path +7554#[derive(Debug, Error, Clone, Eq, PartialEq)] +7555pub enum ClosePathError { +7556 /// Multipath is not negotiated +7557 #[error("Multipath extension not negotiated")] +7558 MultipathNotNegotiated, +7559 /// The path is already closed or was never opened +7560 #[error("closed path")] +7561 ClosedPath, +7562 /// Cannot close the last remaining open path via the local API. +7563 /// +7564 /// Use [`Connection::close`] to end the connection instead. +7565 #[error("last open path")] +7566 LastOpenPath, +7567} +7568 +7569/// Error when the multipath extension was not negotiated, but attempted to be used. +7570#[derive(Debug, Error, Clone, Copy)] +7571#[error("Multipath extension not negotiated")] +7572pub struct MultipathNotNegotiated { +7573 _private: (), +7574} +7575 +7576/// Events of interest to the application +7577#[derive(Debug)] +7578pub enum Event { +7579 /// The connection's handshake data is ready +7580 HandshakeDataReady, +7581 /// The connection was successfully established +7582 Connected, +7583 /// The TLS handshake was confirmed +7584 HandshakeConfirmed, +7585 /// The connection was lost +7586 /// +7587 /// Emitted when the connection is closed due to an error, a timeout, or the peer closing it. +7588 /// This is **not** emitted when the local application closes the connection via +7589 /// [`Connection::close()`](crate::Connection::close). In that case, pending operations will +7590 /// fail with [`ConnectionError::LocallyClosed`]. +7591 ConnectionLost { +7592 /// Reason that the connection was closed +7593 reason: ConnectionError, +7594 }, +7595 /// Stream events +7596 Stream(StreamEvent), +7597 /// One or more application datagrams have been received +7598 DatagramReceived, +7599 /// One or more application datagrams have been sent after blocking +7600 DatagramsUnblocked, +7601 /// (Multi)Path events +7602 Path(PathEvent), +7603 /// n0's nat traversal events +7604 NatTraversal(n0_nat_traversal::Event), +7605} 7606 -7607/// The RTT threshold above which we cap the PTO interval to 1.5 * smoothed_rtt -7608/// -7609/// This is RTT time above which 1.5 * RTT > [`MAX_PTO_INTERVAL`], for these links we want -7610/// to extend the interval between tail-loss probes to not fill the entire pipe with them. -7611const SLOW_RTT_THRESHOLD: Duration = -7612 Duration::from_millis((MAX_PTO_INTERVAL.as_millis() as u64 * 2) / 3); -7613 -7614/// Minimal remaining size to allow packet coalescing, excluding cryptographic tag -7615/// -7616/// This must be at least as large as the header for a well-formed empty packet to be coalesced, -7617/// plus some space for frames. We only care about handshake headers because short header packets -7618/// necessarily have smaller headers, and initial packets are only ever the first packet in a -7619/// datagram (because we coalesce in ascending packet space order and the only reason to split a -7620/// packet is when packet space changes). -7621const MIN_PACKET_SPACE: usize = MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE + 32; -7622 -7623/// Largest amount of space that could be occupied by a Handshake or 0-RTT packet's header -7624/// -7625/// Excludes packet-type-specific fields such as packet number or Initial token -7626// https://www.rfc-editor.org/rfc/rfc9000.html#name-0-rtt: flags + version + dcid len + dcid + -7627// scid len + scid + length + pn -7628const MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE: usize = -7629 1 + 4 + 1 + MAX_CID_SIZE + 1 + MAX_CID_SIZE + VarInt::from_u32(u16::MAX as u32).size() + 4; -7630 -7631#[derive(Default)] -7632struct SentFrames { -7633 retransmits: ThinRetransmits, -7634 /// The packet number of the largest acknowledged packet for each path -7635 largest_acked: FxHashMap<PathId, u64>, -7636 stream_frames: StreamMetaVec, -7637 /// Whether the packet contains non-retransmittable frames (like datagrams) -7638 non_retransmits: bool, -7639 /// If the datagram containing these frames should be padded to the min MTU -7640 requires_padding: bool, -7641} -7642 -7643impl SentFrames { -7644 /// Returns whether the packet contains only ACKs -7645 fn is_ack_only(&self, streams: &StreamsState) -> bool { -7646 !self.largest_acked.is_empty() -7647 && !self.non_retransmits -7648 && self.stream_frames.is_empty() -7649 && self.retransmits.is_empty(streams) -7650 } -7651 -7652 fn retransmits_mut(&mut self) -> &mut Retransmits { -7653 self.retransmits.get_or_create() -7654 } -7655 -7656 fn record_sent_frame(&mut self, frame: frame::EncodableFrame<'_>) { -7657 use frame::EncodableFrame::*; -7658 match frame { -7659 PathAck(path_ack_encoder) => { -7660 if let Some(max) = path_ack_encoder.ranges.max() { -7661 self.largest_acked.insert(path_ack_encoder.path_id, max); -7662 } -7663 } -7664 Ack(ack_encoder) => { -7665 if let Some(max) = ack_encoder.ranges.max() { -7666 self.largest_acked.insert(PathId::ZERO, max); -7667 } -7668 } -7669 Close(_) => { /* non retransmittable, but after this we don't really care */ } -7670 PathResponse(_) => self.non_retransmits = true, -7671 HandshakeDone(_) => self.retransmits_mut().handshake_done = true, -7672 ReachOut(frame) => self.retransmits_mut().reach_out.push(frame), -7673 ObservedAddr(_) => self.retransmits_mut().observed_addr = true, -7674 Ping(_) => self.non_retransmits = true, -7675 ImmediateAck(_) => self.non_retransmits = true, -7676 AckFrequency(_) => self.retransmits_mut().ack_frequency = true, -7677 PathChallenge(_) => self.non_retransmits = true, -7678 Crypto(crypto) => self.retransmits_mut().crypto.push_back(crypto), -7679 PathAbandon(path_abandon) => { -7680 self.retransmits_mut() -7681 .path_abandon -7682 .entry(path_abandon.path_id) -7683 .or_insert(path_abandon.error_code); -7684 } -7685 PathStatusAvailable(frame::PathStatusAvailable { path_id, .. }) -7686 | PathStatusBackup(frame::PathStatusBackup { path_id, .. }) => { -7687 self.retransmits_mut().path_status.insert(path_id); -7688 } -7689 MaxPathId(_) => self.retransmits_mut().max_path_id = true, -7690 PathsBlocked(_) => self.retransmits_mut().paths_blocked = true, -7691 PathCidsBlocked(path_cids_blocked) => { -7692 self.retransmits_mut() -7693 .path_cids_blocked -7694 .insert(path_cids_blocked.path_id); +7607impl From<PathEvent> for Event { +7608 fn from(source: PathEvent) -> Self { +7609 Self::Path(source) +7610 } +7611} +7612 +7613fn get_max_ack_delay(params: &TransportParameters) -> Duration { +7614 Duration::from_micros(params.max_ack_delay.0 * 1000) +7615} +7616 +7617/// Prevents overflow and improves behavior in extreme circumstances. +7618const MAX_BACKOFF_EXPONENT: u32 = 16; +7619 +7620/// The max interval between successive tail-loss probes. +7621/// +7622/// This is the "normal" value we use. +7623const MAX_PTO_INTERVAL: Duration = Duration::from_secs(2); +7624 +7625/// The idle time, below which we use the shorter [`MAX_PTO_FAST_INTERVAL`]. +7626const MIN_IDLE_FOR_FAST_PTO: Duration = Duration::from_secs(25); +7627 +7628/// The max interval between successive tail-loss probes with short idle times. +7629/// +7630/// If the path or connection idle time is less than [`MIN_IDLE_FOR_FAST_PTO`] then we use +7631/// this value to ensure we have plenty of retransmits before we reach the idle time. +7632const MAX_PTO_FAST_INTERVAL: Duration = Duration::from_secs(1); +7633 +7634/// The RTT threshold above which we cap the PTO interval to 1.5 * smoothed_rtt +7635/// +7636/// This is RTT time above which 1.5 * RTT > [`MAX_PTO_INTERVAL`], for these links we want +7637/// to extend the interval between tail-loss probes to not fill the entire pipe with them. +7638const SLOW_RTT_THRESHOLD: Duration = +7639 Duration::from_millis((MAX_PTO_INTERVAL.as_millis() as u64 * 2) / 3); +7640 +7641/// Minimal remaining size to allow packet coalescing, excluding cryptographic tag +7642/// +7643/// This must be at least as large as the header for a well-formed empty packet to be coalesced, +7644/// plus some space for frames. We only care about handshake headers because short header packets +7645/// necessarily have smaller headers, and initial packets are only ever the first packet in a +7646/// datagram (because we coalesce in ascending packet space order and the only reason to split a +7647/// packet is when packet space changes). +7648const MIN_PACKET_SPACE: usize = MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE + 32; +7649 +7650/// Largest amount of space that could be occupied by a Handshake or 0-RTT packet's header +7651/// +7652/// Excludes packet-type-specific fields such as packet number or Initial token +7653// https://www.rfc-editor.org/rfc/rfc9000.html#name-0-rtt: flags + version + dcid len + dcid + +7654// scid len + scid + length + pn +7655const MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE: usize = +7656 1 + 4 + 1 + MAX_CID_SIZE + 1 + MAX_CID_SIZE + VarInt::from_u32(u16::MAX as u32).size() + 4; +7657 +7658#[derive(Default)] +7659struct SentFrames { +7660 retransmits: ThinRetransmits, +7661 /// The packet number of the largest acknowledged packet for each path +7662 largest_acked: FxHashMap<PathId, u64>, +7663 stream_frames: StreamMetaVec, +7664 /// Whether the packet contains non-retransmittable frames (like datagrams) +7665 non_retransmits: bool, +7666 /// If the datagram containing these frames should be padded to the min MTU +7667 requires_padding: bool, +7668} +7669 +7670impl SentFrames { +7671 /// Returns whether the packet contains only ACKs +7672 fn is_ack_only(&self, streams: &StreamsState) -> bool { +7673 !self.largest_acked.is_empty() +7674 && !self.non_retransmits +7675 && self.stream_frames.is_empty() +7676 && self.retransmits.is_empty(streams) +7677 } +7678 +7679 fn retransmits_mut(&mut self) -> &mut Retransmits { +7680 self.retransmits.get_or_create() +7681 } +7682 +7683 fn record_sent_frame(&mut self, frame: frame::EncodableFrame<'_>) { +7684 use frame::EncodableFrame::*; +7685 match frame { +7686 PathAck(path_ack_encoder) => { +7687 if let Some(max) = path_ack_encoder.ranges.max() { +7688 self.largest_acked.insert(path_ack_encoder.path_id, max); +7689 } +7690 } +7691 Ack(ack_encoder) => { +7692 if let Some(max) = ack_encoder.ranges.max() { +7693 self.largest_acked.insert(PathId::ZERO, max); +7694 } 7695 } -7696 ResetStream(reset) => self -7697 .retransmits_mut() -7698 .reset_stream -7699 .push((reset.id, reset.error_code)), -7700 StopSending(stop_sending) => self.retransmits_mut().stop_sending.push(stop_sending), -7701 NewConnectionId(new_cid) => self.retransmits_mut().new_cids.push(new_cid.issued()), -7702 RetireConnectionId(retire_cid) => self -7703 .retransmits_mut() -7704 .retire_cids -7705 .push((retire_cid.path_id.unwrap_or_default(), retire_cid.sequence)), -7706 Datagram(_) => self.non_retransmits = true, -7707 NewToken(_) => {} -7708 AddAddress(add_address) => { -7709 self.retransmits_mut().add_address.insert(add_address); -7710 } -7711 RemoveAddress(remove_address) => { -7712 self.retransmits_mut().remove_address.insert(remove_address); -7713 } -7714 StreamMeta(stream_meta_encoder) => self.stream_frames.push(stream_meta_encoder.meta), -7715 MaxData(_) => self.retransmits_mut().max_data = true, -7716 MaxStreamData(max) => { -7717 self.retransmits_mut().max_stream_data.insert(max.id); -7718 } -7719 MaxStreams(max_streams) => { -7720 self.retransmits_mut().max_stream_id[max_streams.dir as usize] = true -7721 } -7722 StreamsBlocked(streams_blocked) => { -7723 self.retransmits_mut().streams_blocked[streams_blocked.dir as usize] = true -7724 } -7725 } -7726 } -7727} -7728 -7729/// Compute the negotiated idle timeout based on local and remote max_idle_timeout transport parameters. -7730/// -7731/// According to the definition of max_idle_timeout, a value of `0` means the timeout is disabled; see <https://www.rfc-editor.org/rfc/rfc9000#section-18.2-4.4.1.> -7732/// -7733/// According to the negotiation procedure, either the minimum of the timeouts or one specified is used as the negotiated value; see <https://www.rfc-editor.org/rfc/rfc9000#section-10.1-2.> -7734/// -7735/// Returns the negotiated idle timeout as a `Duration`, or `None` when both endpoints have opted out of idle timeout. -7736fn negotiate_max_idle_timeout(x: Option<VarInt>, y: Option<VarInt>) -> Option<Duration> { -7737 match (x, y) { -7738 (Some(VarInt(0)) | None, Some(VarInt(0)) | None) => None, -7739 (Some(VarInt(0)) | None, Some(y)) => Some(Duration::from_millis(y.0)), -7740 (Some(x), Some(VarInt(0)) | None) => Some(Duration::from_millis(x.0)), -7741 (Some(x), Some(y)) => Some(Duration::from_millis(cmp::min(x, y).0)), -7742 } -7743} -7744 -7745#[cfg(test)] -7746mod tests { -7747 use super::*; -7748 -7749 #[test] -7750 fn negotiate_max_idle_timeout_commutative() { -7751 let test_params = [ -7752 (None, None, None), -7753 (None, Some(VarInt(0)), None), -7754 (None, Some(VarInt(2)), Some(Duration::from_millis(2))), -7755 (Some(VarInt(0)), Some(VarInt(0)), None), -7756 ( -7757 Some(VarInt(2)), -7758 Some(VarInt(0)), -7759 Some(Duration::from_millis(2)), -7760 ), -7761 ( -7762 Some(VarInt(1)), -7763 Some(VarInt(4)), -7764 Some(Duration::from_millis(1)), -7765 ), -7766 ]; -7767 -7768 for (left, right, result) in test_params { -7769 assert_eq!(negotiate_max_idle_timeout(left, right), result); -7770 assert_eq!(negotiate_max_idle_timeout(right, left), result); -7771 } -7772 } -7773 -7774 #[test] -7775 fn abandoned_paths() { -7776 let mut t = AbandonedPaths::default(); -7777 -7778 t.insert(PathId(0)); -7779 t.insert(PathId(1)); -7780 assert_eq!(t.len(), 2); -7781 assert_eq!(t.0.range_count(), 1); // 2 elements compacted into one range -7782 assert!(t.contains(&PathId(0))); -7783 assert!(t.contains(&PathId(1))); -7784 assert!(!t.contains(&PathId(2))); -7785 assert!(!t.contains(&PathId(3))); -7786 assert_eq!(t.max(), Some(PathId(1))); -7787 -7788 t.insert(PathId(3)); -7789 assert_eq!(t.len(), 3); -7790 assert_eq!(t.0.range_count(), 2); // 3 elements compacted into 2 ranges -7791 assert!(t.contains(&PathId(0))); -7792 assert!(t.contains(&PathId(1))); -7793 assert!(!t.contains(&PathId(2))); -7794 assert!(t.contains(&PathId(3))); -7795 assert_eq!(t.max(), Some(PathId(3))); -7796 -7797 t.insert(PathId(2)); -7798 assert_eq!(t.len(), 4); -7799 assert_eq!(t.0.range_count(), 1); // 4 elements compacted into 1 range -7800 assert!(t.contains(&PathId(0))); -7801 assert!(t.contains(&PathId(1))); -7802 assert!(t.contains(&PathId(2))); -7803 assert!(t.contains(&PathId(3))); -7804 assert_eq!(t.max(), Some(PathId(3))); -7805 } -7806}

\ No newline at end of file +7696 Close(_) => { /* non retransmittable, but after this we don't really care */ } +7697 PathResponse(_) => self.non_retransmits = true, +7698 HandshakeDone(_) => self.retransmits_mut().handshake_done = true, +7699 ReachOut(frame) => self.retransmits_mut().reach_out.push(frame), +7700 ObservedAddr(_) => self.retransmits_mut().observed_addr = true, +7701 Ping(_) => self.non_retransmits = true, +7702 ImmediateAck(_) => self.non_retransmits = true, +7703 AckFrequency(_) => self.retransmits_mut().ack_frequency = true, +7704 PathChallenge(_) => self.non_retransmits = true, +7705 Crypto(crypto) => self.retransmits_mut().crypto.push_back(crypto), +7706 PathAbandon(path_abandon) => { +7707 self.retransmits_mut() +7708 .path_abandon +7709 .entry(path_abandon.path_id) +7710 .or_insert(path_abandon.error_code); +7711 } +7712 PathStatusAvailable(frame::PathStatusAvailable { path_id, .. }) +7713 | PathStatusBackup(frame::PathStatusBackup { path_id, .. }) => { +7714 self.retransmits_mut().path_status.insert(path_id); +7715 } +7716 MaxPathId(_) => self.retransmits_mut().max_path_id = true, +7717 PathsBlocked(_) => self.retransmits_mut().paths_blocked = true, +7718 PathCidsBlocked(path_cids_blocked) => { +7719 self.retransmits_mut() +7720 .path_cids_blocked +7721 .insert(path_cids_blocked.path_id); +7722 } +7723 ResetStream(reset) => self +7724 .retransmits_mut() +7725 .reset_stream +7726 .push((reset.id, reset.error_code)), +7727 StopSending(stop_sending) => self.retransmits_mut().stop_sending.push(stop_sending), +7728 NewConnectionId(new_cid) => self.retransmits_mut().new_cids.push(new_cid.issued()), +7729 RetireConnectionId(retire_cid) => self +7730 .retransmits_mut() +7731 .retire_cids +7732 .push((retire_cid.path_id.unwrap_or_default(), retire_cid.sequence)), +7733 Datagram(_) => self.non_retransmits = true, +7734 NewToken(_) => {} +7735 AddAddress(add_address) => { +7736 self.retransmits_mut().add_address.insert(add_address); +7737 } +7738 RemoveAddress(remove_address) => { +7739 self.retransmits_mut().remove_address.insert(remove_address); +7740 } +7741 StreamMeta(stream_meta_encoder) => self.stream_frames.push(stream_meta_encoder.meta), +7742 MaxData(_) => self.retransmits_mut().max_data = true, +7743 MaxStreamData(max) => { +7744 self.retransmits_mut().max_stream_data.insert(max.id); +7745 } +7746 MaxStreams(max_streams) => { +7747 self.retransmits_mut().max_stream_id[max_streams.dir as usize] = true +7748 } +7749 StreamsBlocked(streams_blocked) => { +7750 self.retransmits_mut().streams_blocked[streams_blocked.dir as usize] = true +7751 } +7752 } +7753 } +7754} +7755 +7756/// Compute the negotiated idle timeout based on local and remote max_idle_timeout transport parameters. +7757/// +7758/// According to the definition of max_idle_timeout, a value of `0` means the timeout is disabled; see <https://www.rfc-editor.org/rfc/rfc9000#section-18.2-4.4.1.> +7759/// +7760/// According to the negotiation procedure, either the minimum of the timeouts or one specified is used as the negotiated value; see <https://www.rfc-editor.org/rfc/rfc9000#section-10.1-2.> +7761/// +7762/// Returns the negotiated idle timeout as a `Duration`, or `None` when both endpoints have opted out of idle timeout. +7763fn negotiate_max_idle_timeout(x: Option<VarInt>, y: Option<VarInt>) -> Option<Duration> { +7764 match (x, y) { +7765 (Some(VarInt(0)) | None, Some(VarInt(0)) | None) => None, +7766 (Some(VarInt(0)) | None, Some(y)) => Some(Duration::from_millis(y.0)), +7767 (Some(x), Some(VarInt(0)) | None) => Some(Duration::from_millis(x.0)), +7768 (Some(x), Some(y)) => Some(Duration::from_millis(cmp::min(x, y).0)), +7769 } +7770} +7771 +7772#[cfg(test)] +7773mod tests { +7774 use super::*; +7775 +7776 #[test] +7777 fn negotiate_max_idle_timeout_commutative() { +7778 let test_params = [ +7779 (None, None, None), +7780 (None, Some(VarInt(0)), None), +7781 (None, Some(VarInt(2)), Some(Duration::from_millis(2))), +7782 (Some(VarInt(0)), Some(VarInt(0)), None), +7783 ( +7784 Some(VarInt(2)), +7785 Some(VarInt(0)), +7786 Some(Duration::from_millis(2)), +7787 ), +7788 ( +7789 Some(VarInt(1)), +7790 Some(VarInt(4)), +7791 Some(Duration::from_millis(1)), +7792 ), +7793 ]; +7794 +7795 for (left, right, result) in test_params { +7796 assert_eq!(negotiate_max_idle_timeout(left, right), result); +7797 assert_eq!(negotiate_max_idle_timeout(right, left), result); +7798 } +7799 } +7800 +7801 #[test] +7802 fn abandoned_paths() { +7803 let mut t = AbandonedPaths::default(); +7804 +7805 t.insert(PathId(0)); +7806 t.insert(PathId(1)); +7807 assert_eq!(t.len(), 2); +7808 assert_eq!(t.0.range_count(), 1); // 2 elements compacted into one range +7809 assert!(t.contains(&PathId(0))); +7810 assert!(t.contains(&PathId(1))); +7811 assert!(!t.contains(&PathId(2))); +7812 assert!(!t.contains(&PathId(3))); +7813 assert_eq!(t.max(), Some(PathId(1))); +7814 +7815 t.insert(PathId(3)); +7816 assert_eq!(t.len(), 3); +7817 assert_eq!(t.0.range_count(), 2); // 3 elements compacted into 2 ranges +7818 assert!(t.contains(&PathId(0))); +7819 assert!(t.contains(&PathId(1))); +7820 assert!(!t.contains(&PathId(2))); +7821 assert!(t.contains(&PathId(3))); +7822 assert_eq!(t.max(), Some(PathId(3))); +7823 +7824 t.insert(PathId(2)); +7825 assert_eq!(t.len(), 4); +7826 assert_eq!(t.0.range_count(), 1); // 4 elements compacted into 1 range +7827 assert!(t.contains(&PathId(0))); +7828 assert!(t.contains(&PathId(1))); +7829 assert!(t.contains(&PathId(2))); +7830 assert!(t.contains(&PathId(3))); +7831 assert_eq!(t.max(), Some(PathId(3))); +7832 } +7833}

\ No newline at end of file diff --git a/pr/707/docs/src/noq_proto/connection/paths.rs.html b/pr/707/docs/src/noq_proto/connection/paths.rs.html index c4f7f8917..74c487d2c 100644 --- a/pr/707/docs/src/noq_proto/connection/paths.rs.html +++ b/pr/707/docs/src/noq_proto/connection/paths.rs.html @@ -182,23 +182,23 @@ 182 /// I.e. when app_limited is true, the congestion controller doesn't increase the congestion window. 183 pub(super) app_limited: bool, 184 -185 /// Path challenges sent (on the wire, on-path) that we didn't receive a path response for yet -186 on_path_challenges_unconfirmed: IntMap<u64, SentChallengeInfo>, -187 /// Whether to trigger sending another PATH_CHALLENGE in the next poll_transmit. -188 /// -189 /// This is picked up by [`super::Connection::space_can_send`]. -190 /// -191 /// Only used for RFC9000-style path migration and multipath path validation (for opening). +185 /// Whether to trigger sending another PATH_CHALLENGE in the next poll_transmit. +186 /// +187 /// This is picked up by [`super::Connection::space_can_send`]. These are **not** +188 /// retransmittable, which is why they are not part of the `PathRetransmits`. +189 /// +190 /// Only used for **on-path** challenges, like RFC9000-style path migration and +191 /// multipath path validation (for opening). 192 /// -193 /// This is **not used** for n0 nat traversal challenge sending. -194 pub(super) pending_on_path_challenge: bool, -195 /// How often we've deemed a path challenge to be lost. -196 /// -197 /// Similar to [`Self::pto_count`], but for on-path path challenges. -198 /// Used to calculate exponential backoff for retrying path challenges. -199 pub(super) on_path_challenges_lost: u32, -200 /// Pending responses to PATH_CHALLENGE frames -201 pub(super) path_responses: PathResponses, +193 /// This is **not used** for n0 nat traversal challenge sending, which is off-path. +194 pub(super) pending_challenge: bool, +195 /// On-path path challenges sent that we didn't receive a path response for yet. +196 unconfirmed_challenges: IntMap<u64, SentChallengeInfo>, +197 /// How often we've deemed a path challenge to be lost. +198 /// +199 /// Similar to [`Self::pto_count`], but for on-path path challenges. +200 /// Used to calculate exponential backoff for retrying path challenges. +201 pub(super) lost_challenge_count: u32, 202 /// Whether we're certain the peer can both send and receive on this address 203 /// 204 /// Initially equal to `use_stateless_retry` for servers, and becomes false again on every @@ -266,919 +266,896 @@ 266 /// <https://www.rfc-editor.org/rfc/rfc9000.html#section-10.1>. 267 pub(super) permit_idle_reset: bool, 268 -269 /// Whether the path has already been considered opened from an application perspective. +269 /// Whether we're currently draining the path after having abandoned it. 270 /// -271 /// This means, for paths other than the original [`PathId::ZERO`], a first path challenge has -272 /// been responded to, regardless of the initial validation status of the path. This state is -273 /// irreversible, since it's not affected by the path being closed. -274 /// -275 /// Sending a PATH_CHALLENGE and receiving a valid response before the application is informed -276 /// of the path, is a way to ensure the path is usable before it is reported. This is not -277 /// required by the spec, and in the future might be changed for simply requiring a first ack'd -278 /// packet. -279 pub(super) open_status: OpenStatus, +271 /// This should only be true when a path discard timer is armed, and after the path was +272 /// abandoned (and added to the abandoned_paths set). +273 /// +274 /// This will only ever be set from false to true. +275 pub(super) draining: bool, +276 +277 /// Snapshot of the qlog recovery metrics +278 #[cfg(feature = "qlog")] +279 recovery_metrics: RecoveryMetrics, 280 -281 /// Whether we're currently draining the path after having abandoned it. +281 /// Tag uniquely identifying a path in a connection. 282 /// -283 /// This should only be true when a path discard timer is armed, and after the path was -284 /// abandoned (and added to the abandoned_paths set). -285 /// -286 /// This will only ever be set from false to true. -287 pub(super) draining: bool, +283 /// When a migration happens on the same [`PathId`] we still detect a change in the +284 /// 4-tuple and generate a new [`PathData`] for it. Each such generation has a unique +285 /// value to keep track of which 4-tuple a packet belonged to. +286 generation: u64, +287} 288 -289 /// Snapshot of the qlog recovery metrics -290 #[cfg(feature = "qlog")] -291 recovery_metrics: RecoveryMetrics, -292 -293 /// Tag uniquely identifying a path in a connection. -294 /// -295 /// When a migration happens on the same [`PathId`] we still detect a change in the -296 /// 4-tuple and generate a new [`PathData`] for it. Each such generation has a unique -297 /// value to keep track of which 4-tuple a packet belonged to. -298 generation: u64, -299} -300 -301impl PathData { -302 pub(super) fn new( -303 network_path: FourTuple, -304 allow_mtud: bool, -305 peer_max_udp_payload_size: Option<u16>, -306 generation: u64, -307 now: Instant, -308 config: &TransportConfig, -309 ) -> Self { -310 let congestion = config -311 .congestion_controller_factory -312 .clone() -313 .build(now, config.get_initial_mtu()); -314 Self { -315 network_path, -316 rtt: RttEstimator::new(config.initial_rtt), -317 sending_ecn: true, -318 pacing: Pacer::new( -319 config.initial_rtt, -320 congestion.initial_window(), -321 config.get_initial_mtu(), -322 config.max_outgoing_bytes_per_second, -323 now, -324 ), -325 congestion, -326 app_limited: false, -327 on_path_challenges_unconfirmed: Default::default(), -328 on_path_challenges_lost: 0, -329 pending_on_path_challenge: false, -330 path_responses: PathResponses::default(), -331 validated: false, -332 total_sent: 0, -333 total_recvd: 0, -334 mtud: config -335 .mtu_discovery_config -336 .as_ref() -337 .filter(|_| allow_mtud) -338 .map_or_else( -339 || MtuDiscovery::disabled(config.get_initial_mtu(), config.min_mtu), -340 |mtud_config| { -341 MtuDiscovery::new( -342 config.get_initial_mtu(), -343 config.min_mtu, -344 peer_max_udp_payload_size, -345 mtud_config.clone(), -346 ) -347 }, -348 ), -349 first_packet_after_rtt_sample: None, -350 in_flight: InFlight::new(), -351 observed_addr_sent: false, -352 last_observed_addr_report: None, -353 status: Default::default(), -354 first_packet: None, -355 pto_count: 0, -356 idle_timeout: config.default_path_max_idle_timeout, -357 keep_alive: config.default_path_keep_alive_interval, -358 permit_idle_reset: true, -359 open_status: OpenStatus::default(), -360 draining: false, -361 #[cfg(feature = "qlog")] -362 recovery_metrics: RecoveryMetrics::default(), -363 generation, -364 } -365 } -366 -367 /// Create a new path from a previous one. -368 /// -369 /// This should only be called when migrating paths. -370 pub(super) fn from_previous( -371 network_path: FourTuple, -372 prev: &Self, -373 generation: u64, -374 now: Instant, -375 ) -> Self { -376 let congestion = prev.congestion.clone_box(); -377 let smoothed_rtt = prev.rtt.get(); -378 Self { -379 network_path, -380 rtt: prev.rtt, -381 pacing: Pacer::new( -382 smoothed_rtt, -383 congestion.window(), -384 prev.current_mtu(), -385 prev.pacing.max_bytes_per_second(), -386 now, -387 ), -388 sending_ecn: true, -389 congestion, -390 app_limited: false, -391 on_path_challenges_unconfirmed: Default::default(), -392 on_path_challenges_lost: 0, -393 pending_on_path_challenge: false, -394 path_responses: PathResponses::default(), -395 validated: false, -396 total_sent: 0, -397 total_recvd: 0, -398 mtud: prev.mtud.clone(), -399 first_packet_after_rtt_sample: prev.first_packet_after_rtt_sample, -400 in_flight: InFlight::new(), -401 observed_addr_sent: false, -402 last_observed_addr_report: None, -403 status: prev.status.clone(), -404 first_packet: None, -405 pto_count: 0, -406 idle_timeout: prev.idle_timeout, -407 keep_alive: prev.keep_alive, -408 permit_idle_reset: true, -409 open_status: OpenStatus::default(), -410 draining: false, -411 #[cfg(feature = "qlog")] -412 recovery_metrics: prev.recovery_metrics.clone(), -413 generation, -414 } +289impl PathData { +290 pub(super) fn new( +291 network_path: FourTuple, +292 allow_mtud: bool, +293 peer_max_udp_payload_size: Option<u16>, +294 generation: u64, +295 now: Instant, +296 config: &TransportConfig, +297 ) -> Self { +298 let congestion = config +299 .congestion_controller_factory +300 .clone() +301 .build(now, config.get_initial_mtu()); +302 Self { +303 network_path, +304 rtt: RttEstimator::new(config.initial_rtt), +305 sending_ecn: true, +306 pacing: Pacer::new( +307 config.initial_rtt, +308 congestion.initial_window(), +309 config.get_initial_mtu(), +310 config.max_outgoing_bytes_per_second, +311 now, +312 ), +313 congestion, +314 app_limited: false, +315 unconfirmed_challenges: Default::default(), +316 lost_challenge_count: 0, +317 pending_challenge: false, +318 validated: false, +319 total_sent: 0, +320 total_recvd: 0, +321 mtud: config +322 .mtu_discovery_config +323 .as_ref() +324 .filter(|_| allow_mtud) +325 .map_or_else( +326 || MtuDiscovery::disabled(config.get_initial_mtu(), config.min_mtu), +327 |mtud_config| { +328 MtuDiscovery::new( +329 config.get_initial_mtu(), +330 config.min_mtu, +331 peer_max_udp_payload_size, +332 mtud_config.clone(), +333 ) +334 }, +335 ), +336 first_packet_after_rtt_sample: None, +337 in_flight: InFlight::new(), +338 observed_addr_sent: false, +339 last_observed_addr_report: None, +340 status: Default::default(), +341 first_packet: None, +342 pto_count: 0, +343 idle_timeout: config.default_path_max_idle_timeout, +344 keep_alive: config.default_path_keep_alive_interval, +345 permit_idle_reset: true, +346 draining: false, +347 #[cfg(feature = "qlog")] +348 recovery_metrics: RecoveryMetrics::default(), +349 generation, +350 } +351 } +352 +353 /// Create a new path from a previous one. +354 /// +355 /// This should only be called when migrating paths. +356 pub(super) fn from_previous( +357 network_path: FourTuple, +358 prev: &Self, +359 generation: u64, +360 now: Instant, +361 ) -> Self { +362 let congestion = prev.congestion.clone_box(); +363 let smoothed_rtt = prev.rtt.get(); +364 Self { +365 network_path, +366 rtt: prev.rtt, +367 pacing: Pacer::new( +368 smoothed_rtt, +369 congestion.window(), +370 prev.current_mtu(), +371 prev.pacing.max_bytes_per_second(), +372 now, +373 ), +374 sending_ecn: true, +375 congestion, +376 app_limited: false, +377 unconfirmed_challenges: Default::default(), +378 lost_challenge_count: 0, +379 pending_challenge: false, +380 validated: false, +381 total_sent: 0, +382 total_recvd: 0, +383 mtud: prev.mtud.clone(), +384 first_packet_after_rtt_sample: prev.first_packet_after_rtt_sample, +385 in_flight: InFlight::new(), +386 observed_addr_sent: false, +387 last_observed_addr_report: None, +388 status: prev.status.clone(), +389 first_packet: None, +390 pto_count: 0, +391 idle_timeout: prev.idle_timeout, +392 keep_alive: prev.keep_alive, +393 permit_idle_reset: true, +394 draining: false, +395 #[cfg(feature = "qlog")] +396 recovery_metrics: prev.recovery_metrics.clone(), +397 generation, +398 } +399 } +400 +401 /// Whether we're in the process of validating this path with PATH_CHALLENGEs +402 pub(super) fn is_validating_path(&self) -> bool { +403 !self.unconfirmed_challenges.is_empty() || self.pending_challenge +404 } +405 +406 /// Indicates whether we're a server that hasn't validated the peer's address and hasn't +407 /// received enough data from the peer to permit sending `bytes_to_send` additional bytes +408 pub(super) fn anti_amplification_blocked(&self, bytes_to_send: u64) -> bool { +409 !self.validated && self.total_recvd * 3 < self.total_sent + bytes_to_send +410 } +411 +412 /// Returns the path's current MTU +413 pub(super) fn current_mtu(&self) -> u16 { +414 self.mtud.current_mtu() 415 } 416 -417 /// Whether we're in the process of validating this path with PATH_CHALLENGEs -418 pub(super) fn is_validating_path(&self) -> bool { -419 !self.on_path_challenges_unconfirmed.is_empty() || self.pending_on_path_challenge -420 } -421 -422 /// Indicates whether we're a server that hasn't validated the peer's address and hasn't -423 /// received enough data from the peer to permit sending `bytes_to_send` additional bytes -424 pub(super) fn anti_amplification_blocked(&self, bytes_to_send: u64) -> bool { -425 !self.validated && self.total_recvd * 3 < self.total_sent + bytes_to_send +417 /// Account for transmission of `packet` with number `pn` in `space` +418 pub(super) fn sent(&mut self, pn: u64, packet: SentPacket, space: &mut PacketNumberSpace) { +419 self.in_flight.insert(&packet); +420 if self.first_packet.is_none() { +421 self.first_packet = Some(pn); +422 } +423 if let Some(forgotten) = space.sent(pn, packet) { +424 self.remove_in_flight(&forgotten); +425 } 426 } 427 -428 /// Returns the path's current MTU -429 pub(super) fn current_mtu(&self) -> u16 { -430 self.mtud.current_mtu() -431 } -432 -433 /// Account for transmission of `packet` with number `pn` in `space` -434 pub(super) fn sent(&mut self, pn: u64, packet: SentPacket, space: &mut PacketNumberSpace) { -435 self.in_flight.insert(&packet); -436 if self.first_packet.is_none() { -437 self.first_packet = Some(pn); -438 } -439 if let Some(forgotten) = space.sent(pn, packet) { -440 self.remove_in_flight(&forgotten); -441 } -442 } -443 -444 pub(super) fn record_path_challenge_sent( -445 &mut self, -446 now: Instant, -447 token: u64, -448 network_path: FourTuple, -449 ) { -450 let info = SentChallengeInfo { -451 sent_instant: now, -452 network_path, -453 }; -454 debug_assert_eq!(network_path, self.network_path); -455 self.on_path_challenges_unconfirmed.insert(token, info); -456 } -457 -458 /// Remove `packet` with number `pn` from this path's congestion control counters, or return -459 /// `false` if `pn` was sent before this path was established. -460 pub(super) fn remove_in_flight(&mut self, packet: &SentPacket) -> bool { -461 if packet.path_generation != self.generation { -462 return false; -463 } -464 self.in_flight.remove(packet); -465 true -466 } -467 -468 /// Increment the total size of sent UDP datagrams -469 pub(super) fn inc_total_sent(&mut self, inc: u64) { -470 self.total_sent = self.total_sent.saturating_add(inc); -471 if !self.validated { -472 trace!( -473 network_path = %self.network_path, -474 anti_amplification_budget = %(self.total_recvd * 3).saturating_sub(self.total_sent), -475 "anti amplification budget decreased" -476 ); -477 } -478 } -479 -480 /// Increment the total size of received UDP datagrams -481 pub(super) fn inc_total_recvd(&mut self, inc: u64) { -482 self.total_recvd = self.total_recvd.saturating_add(inc); -483 if !self.validated { -484 trace!( -485 network_path = %self.network_path, -486 anti_amplification_budget = %(self.total_recvd * 3).saturating_sub(self.total_sent), -487 "anti amplification budget increased" -488 ); -489 } -490 } -491 -492 /// The earliest time at which an on-path challenge we sent is considered lost. -493 pub(super) fn earliest_on_path_expiring_challenge(&self) -> Option<Instant> { -494 if self.on_path_challenges_unconfirmed.is_empty() { -495 return None; -496 } -497 let duration = self.on_path_challenge_expiry(); -498 self.on_path_challenges_unconfirmed -499 .values() -500 .map(|info| info.sent_instant + duration) -501 .min() -502 } -503 -504 pub(super) fn on_path_challenge_expiry(&self) -> Duration { -505 let backoff = 2u32.pow(self.on_path_challenges_lost.min(MAX_BACKOFF_EXPONENT)); -506 let duration = self.rtt.pto_base() * backoff; -507 duration.min(MAX_PTO_INTERVAL) -508 } -509 -510 /// Handle receiving a PATH_RESPONSE. -511 pub(super) fn on_path_response_received( -512 &mut self, -513 now: Instant, -514 token: u64, -515 network_path: FourTuple, -516 ) -> OnPathResponseReceived { -517 // > § 8.2.3 -518 // > Path validation succeeds when a PATH_RESPONSE frame is received that contains the -519 // > data that was sent in a previous PATH_CHALLENGE frame. A PATH_RESPONSE frame -520 // > received on any network path validates the path on which the PATH_CHALLENGE was -521 // > sent. -522 // -523 // At this point we have three potentially different network paths: -524 // - current network path (`Self::network_path`) -525 // - network path used to send the path challenge (`SentChallengeInfo::network_path`) -526 // - network path over which the response arrived (`network_path`) -527 // -528 // As per the spec, this only validates the network path on which this was *sent*. -529 match self.on_path_challenges_unconfirmed.remove(&token) { -530 // Response to an on-path PathChallenge that validates this path. -531 // The sent path should match the current path. However, it's possible that the -532 // challenge was sent when no local_ip was known. This case is allowed as well. -533 Some(info) if info.network_path.is_probably_same_path(&self.network_path) => { -534 self.network_path.update_local_if_same_remote(&network_path); -535 let sent_instant = info.sent_instant; -536 if !std::mem::replace(&mut self.validated, true) { -537 trace!("new path validated"); -538 } -539 // Clear any other on-path sent challenges and stop sending new ones. -540 self.reset_on_path_challenges(); -541 -542 // This RTT can only be used for the initial RTT, not as a normal -543 // sample: https://www.rfc-editor.org/rfc/rfc9002#section-6.2.2-2. -544 let rtt = now.saturating_duration_since(sent_instant); -545 self.rtt.reset_initial_rtt(rtt); -546 -547 let prev_status = std::mem::replace(&mut self.open_status, OpenStatus::Informed); -548 OnPathResponseReceived::OnPath { -549 was_open: matches!(prev_status, OpenStatus::Informed), -550 } -551 } -552 // Response to an on-path PathChallenge that does not validate this path. -553 Some(info) => { -554 // This is a valid path response, but this validates a 4-tuple we no longer -555 // have in use. Keep only sent challenges for the current path. -556 self.on_path_challenges_unconfirmed -557 .retain(|_token, i| i.network_path == self.network_path); -558 -559 // If there are no challenges for the current path, schedule one -560 if !self.on_path_challenges_unconfirmed.is_empty() { -561 self.pending_on_path_challenge = true; -562 } -563 OnPathResponseReceived::Ignored { -564 sent_on: info.network_path, -565 current_path: self.network_path, -566 } -567 } -568 None => { -569 // Response to an unknown PathChallenge. Does not indicate failure. -570 OnPathResponseReceived::Unknown -571 } -572 } -573 } -574 -575 /// Removes all on-path challenges we remember and cancels sending new on-path challenges. -576 pub(super) fn reset_on_path_challenges(&mut self) { -577 self.on_path_challenges_unconfirmed.clear(); -578 self.pending_on_path_challenge = false; -579 self.on_path_challenges_lost = 0; -580 } -581 -582 #[cfg(feature = "qlog")] -583 pub(super) fn qlog_recovery_metrics( -584 &mut self, -585 path_id: PathId, -586 ) -> Option<RecoveryMetricsUpdated> { -587 let controller_metrics = self.congestion.metrics(); +428 pub(super) fn record_path_challenge_sent( +429 &mut self, +430 now: Instant, +431 token: u64, +432 network_path: FourTuple, +433 ) { +434 let info = SentChallengeInfo { +435 sent_instant: now, +436 network_path, +437 }; +438 debug_assert_eq!(network_path, self.network_path); +439 self.unconfirmed_challenges.insert(token, info); +440 } +441 +442 /// Remove `packet` with number `pn` from this path's congestion control counters, or return +443 /// `false` if `pn` was sent before this path was established. +444 pub(super) fn remove_in_flight(&mut self, packet: &SentPacket) -> bool { +445 if packet.path_generation != self.generation { +446 return false; +447 } +448 self.in_flight.remove(packet); +449 true +450 } +451 +452 /// Increment the total size of sent UDP datagrams +453 pub(super) fn inc_total_sent(&mut self, inc: u64) { +454 self.total_sent = self.total_sent.saturating_add(inc); +455 if !self.validated { +456 trace!( +457 network_path = %self.network_path, +458 anti_amplification_budget = %(self.total_recvd * 3).saturating_sub(self.total_sent), +459 "anti amplification budget decreased" +460 ); +461 } +462 } +463 +464 /// Increment the total size of received UDP datagrams +465 pub(super) fn inc_total_recvd(&mut self, inc: u64) { +466 self.total_recvd = self.total_recvd.saturating_add(inc); +467 if !self.validated { +468 trace!( +469 network_path = %self.network_path, +470 anti_amplification_budget = %(self.total_recvd * 3).saturating_sub(self.total_sent), +471 "anti amplification budget increased" +472 ); +473 } +474 } +475 +476 /// The earliest time at which an on-path challenge we sent is considered lost. +477 pub(super) fn earliest_on_path_expiring_challenge(&self) -> Option<Instant> { +478 if self.unconfirmed_challenges.is_empty() { +479 return None; +480 } +481 let duration = self.on_path_challenge_expiry(); +482 self.unconfirmed_challenges +483 .values() +484 .map(|info| info.sent_instant + duration) +485 .min() +486 } +487 +488 pub(super) fn on_path_challenge_expiry(&self) -> Duration { +489 let backoff = 2u32.pow(self.lost_challenge_count.min(MAX_BACKOFF_EXPONENT)); +490 let duration = self.rtt.pto_base() * backoff; +491 duration.min(MAX_PTO_INTERVAL) +492 } +493 +494 /// Handle receiving a PATH_RESPONSE. +495 pub(super) fn on_path_response_received( +496 &mut self, +497 now: Instant, +498 token: u64, +499 network_path: FourTuple, +500 ) -> OnPathResponseReceived { +501 // > § 8.2.3 +502 // > Path validation succeeds when a PATH_RESPONSE frame is received that contains the +503 // > data that was sent in a previous PATH_CHALLENGE frame. A PATH_RESPONSE frame +504 // > received on any network path validates the path on which the PATH_CHALLENGE was +505 // > sent. +506 // +507 // At this point we have three potentially different network paths: +508 // - current network path (`Self::network_path`) +509 // - network path used to send the path challenge (`SentChallengeInfo::network_path`) +510 // - network path over which the response arrived (`network_path`) +511 // +512 // As per the spec, this only validates the network path on which this was *sent*. +513 match self.unconfirmed_challenges.remove(&token) { +514 // Response to an on-path PathChallenge that validates this path. +515 // The sent path should match the current path. However, it's possible that the +516 // challenge was sent when no local_ip was known. This case is allowed as well. +517 Some(info) if info.network_path.is_probably_same_path(&self.network_path) => { +518 self.network_path.update_local_if_same_remote(&network_path); +519 let sent_instant = info.sent_instant; +520 if !std::mem::replace(&mut self.validated, true) { +521 trace!("new path validated"); +522 } +523 // Clear any other on-path sent challenges and stop sending new ones. +524 self.reset_on_path_challenges(); +525 +526 // This RTT can only be used for the initial RTT, not as a normal +527 // sample: https://www.rfc-editor.org/rfc/rfc9002#section-6.2.2-2. +528 let rtt = now.saturating_duration_since(sent_instant); +529 self.rtt.reset_initial_rtt(rtt); +530 +531 OnPathResponseReceived::OnPath +532 } +533 // Response to an on-path PathChallenge that does not validate this path. +534 Some(info) => { +535 // This is a valid path response, but this validates a 4-tuple we no longer +536 // have in use. Keep only sent challenges for the current path. +537 self.unconfirmed_challenges +538 .retain(|_token, i| i.network_path == self.network_path); +539 +540 // If there are no challenges for the current path, schedule one +541 if !self.unconfirmed_challenges.is_empty() { +542 self.pending_challenge = true; +543 } +544 OnPathResponseReceived::Ignored { +545 sent_on: info.network_path, +546 current_path: self.network_path, +547 } +548 } +549 None => { +550 // Response to an unknown PathChallenge. Does not indicate failure. +551 OnPathResponseReceived::Unknown +552 } +553 } +554 } +555 +556 /// Removes all on-path challenges we remember and cancels sending new on-path challenges. +557 pub(super) fn reset_on_path_challenges(&mut self) { +558 self.unconfirmed_challenges.clear(); +559 self.pending_challenge = false; +560 self.lost_challenge_count = 0; +561 } +562 +563 #[cfg(feature = "qlog")] +564 pub(super) fn qlog_recovery_metrics( +565 &mut self, +566 path_id: PathId, +567 ) -> Option<RecoveryMetricsUpdated> { +568 let controller_metrics = self.congestion.metrics(); +569 +570 let metrics = RecoveryMetrics { +571 min_rtt: Some(self.rtt.min), +572 smoothed_rtt: Some(self.rtt.get()), +573 latest_rtt: Some(self.rtt.latest), +574 rtt_variance: Some(self.rtt.var), +575 pto_count: Some(self.pto_count), +576 bytes_in_flight: Some(self.in_flight.bytes), +577 packets_in_flight: Some(self.in_flight.ack_eliciting), +578 +579 congestion_window: Some(controller_metrics.congestion_window), +580 ssthresh: controller_metrics.ssthresh, +581 pacing_rate: controller_metrics.pacing_rate, +582 }; +583 +584 let event = metrics.to_qlog_event(path_id, &self.recovery_metrics); +585 self.recovery_metrics = metrics; +586 event +587 } 588 -589 let metrics = RecoveryMetrics { -590 min_rtt: Some(self.rtt.min), -591 smoothed_rtt: Some(self.rtt.get()), -592 latest_rtt: Some(self.rtt.latest), -593 rtt_variance: Some(self.rtt.var), -594 pto_count: Some(self.pto_count), -595 bytes_in_flight: Some(self.in_flight.bytes), -596 packets_in_flight: Some(self.in_flight.ack_eliciting), -597 -598 congestion_window: Some(controller_metrics.congestion_window), -599 ssthresh: controller_metrics.ssthresh, -600 pacing_rate: controller_metrics.pacing_rate, -601 }; -602 -603 let event = metrics.to_qlog_event(path_id, &self.recovery_metrics); -604 self.recovery_metrics = metrics; -605 event -606 } -607 -608 /// Return how long we need to wait before sending `bytes_to_send` -609 /// -610 /// See [`Pacer::delay`]. -611 pub(super) fn pacing_delay(&mut self, bytes_to_send: u64, now: Instant) -> Option<Duration> { -612 let smoothed_rtt = self.rtt.get(); -613 let metrics = self.congestion.metrics(); -614 self.pacing.delay( -615 smoothed_rtt, -616 bytes_to_send, -617 self.current_mtu(), -618 metrics.congestion_window, -619 now, -620 metrics.send_quantum, -621 metrics.pacing_rate, -622 ) -623 } -624 -625 /// Updates the last observed address report received on this path. -626 /// -627 /// If the address was updated, it's returned to be informed to the application. -628 #[must_use = "updated observed address must be reported to the application"] -629 pub(super) fn update_observed_addr_report( -630 &mut self, -631 observed: ObservedAddr, -632 ) -> Option<SocketAddr> { -633 match self.last_observed_addr_report.as_mut() { -634 Some(prev) => { -635 if prev.seq_no >= observed.seq_no { -636 // frames that do not increase the sequence number on this path are ignored -637 None -638 } else if prev.ip == observed.ip && prev.port == observed.port { -639 // keep track of the last seq_no but do not report the address as updated -640 prev.seq_no = observed.seq_no; -641 None -642 } else { -643 let addr = observed.socket_addr(); -644 self.last_observed_addr_report = Some(observed); -645 Some(addr) -646 } -647 } -648 None => { -649 let addr = observed.socket_addr(); -650 self.last_observed_addr_report = Some(observed); -651 Some(addr) -652 } -653 } -654 } -655 -656 pub(crate) fn remote_status(&self) -> Option<PathStatus> { -657 self.status.remote_status.map(|(_seq, status)| status) -658 } -659 -660 pub(crate) fn local_status(&self) -> PathStatus { -661 self.status.local_status -662 } -663 -664 /// Tag uniquely identifying a path in a connection. -665 /// -666 /// When a migration happens on the same [`PathId`] we still detect a change in the -667 /// 4-tuple and generate a new [`PathData`] for it. Each such generation has a unique -668 /// value to keep track of which 4-tuple a packet belonged to. -669 pub(super) fn generation(&self) -> u64 { -670 self.generation -671 } -672} -673 -674pub(super) enum OnPathResponseReceived { -675 /// This response validates the path on its current remote address. -676 OnPath { was_open: bool }, -677 /// The received token is unknown. -678 Unknown, -679 /// The response is valid but it's not usable for path validation. -680 Ignored { -681 sent_on: FourTuple, -682 current_path: FourTuple, -683 }, +589 /// Return how long we need to wait before sending `bytes_to_send` +590 /// +591 /// See [`Pacer::delay`]. +592 pub(super) fn pacing_delay(&mut self, bytes_to_send: u64, now: Instant) -> Option<Duration> { +593 let smoothed_rtt = self.rtt.get(); +594 let metrics = self.congestion.metrics(); +595 self.pacing.delay( +596 smoothed_rtt, +597 bytes_to_send, +598 self.current_mtu(), +599 metrics.congestion_window, +600 now, +601 metrics.send_quantum, +602 metrics.pacing_rate, +603 ) +604 } +605 +606 /// Updates the last observed address report received on this path. +607 /// +608 /// If the address was updated, it's returned to be informed to the application. +609 #[must_use = "updated observed address must be reported to the application"] +610 pub(super) fn update_observed_addr_report( +611 &mut self, +612 observed: ObservedAddr, +613 ) -> Option<SocketAddr> { +614 match self.last_observed_addr_report.as_mut() { +615 Some(prev) => { +616 if prev.seq_no >= observed.seq_no { +617 // frames that do not increase the sequence number on this path are ignored +618 None +619 } else if prev.ip == observed.ip && prev.port == observed.port { +620 // keep track of the last seq_no but do not report the address as updated +621 prev.seq_no = observed.seq_no; +622 None +623 } else { +624 let addr = observed.socket_addr(); +625 self.last_observed_addr_report = Some(observed); +626 Some(addr) +627 } +628 } +629 None => { +630 let addr = observed.socket_addr(); +631 self.last_observed_addr_report = Some(observed); +632 Some(addr) +633 } +634 } +635 } +636 +637 pub(crate) fn remote_status(&self) -> Option<PathStatus> { +638 self.status.remote_status.map(|(_seq, status)| status) +639 } +640 +641 pub(crate) fn local_status(&self) -> PathStatus { +642 self.status.local_status +643 } +644 +645 /// Tag uniquely identifying a path in a connection. +646 /// +647 /// When a migration happens on the same [`PathId`] we still detect a change in the +648 /// 4-tuple and generate a new [`PathData`] for it. Each such generation has a unique +649 /// value to keep track of which 4-tuple a packet belonged to. +650 pub(super) fn generation(&self) -> u64 { +651 self.generation +652 } +653} +654 +655pub(super) enum OnPathResponseReceived { +656 /// This response validates the path on its current remote address. +657 OnPath, +658 /// The received token is unknown. +659 Unknown, +660 /// The response is valid but it's not usable for path validation. +661 Ignored { +662 sent_on: FourTuple, +663 current_path: FourTuple, +664 }, +665} +666 +667/// Congestion metrics as described in [`recovery_metrics_updated`]. +668/// +669/// [`recovery_metrics_updated`]: https://datatracker.ietf.org/doc/html/draft-ietf-quic-qlog-quic-events.html#name-recovery_metrics_updated +670#[cfg(feature = "qlog")] +671#[derive(Default, Clone, PartialEq, Debug)] +672#[non_exhaustive] +673struct RecoveryMetrics { +674 pub min_rtt: Option<Duration>, +675 pub smoothed_rtt: Option<Duration>, +676 pub latest_rtt: Option<Duration>, +677 pub rtt_variance: Option<Duration>, +678 pub pto_count: Option<u32>, +679 pub bytes_in_flight: Option<u64>, +680 pub packets_in_flight: Option<u64>, +681 pub congestion_window: Option<u64>, +682 pub ssthresh: Option<u64>, +683 pub pacing_rate: Option<u64>, 684} 685 -686#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] -687pub(super) enum OpenStatus { -688 /// A first packet has not been sent using this [`PathId`]. -689 #[default] -690 Pending, -691 /// The first packet has been sent using this [`PathId`]. However, it is not yet deemed good -692 /// enough to be reported to the application. -693 Sent, -694 /// The application has been informed of this path. -695 Informed, -696} -697 -698/// Congestion metrics as described in [`recovery_metrics_updated`]. -699/// -700/// [`recovery_metrics_updated`]: https://datatracker.ietf.org/doc/html/draft-ietf-quic-qlog-quic-events.html#name-recovery_metrics_updated -701#[cfg(feature = "qlog")] -702#[derive(Default, Clone, PartialEq, Debug)] -703#[non_exhaustive] -704struct RecoveryMetrics { -705 pub min_rtt: Option<Duration>, -706 pub smoothed_rtt: Option<Duration>, -707 pub latest_rtt: Option<Duration>, -708 pub rtt_variance: Option<Duration>, -709 pub pto_count: Option<u32>, -710 pub bytes_in_flight: Option<u64>, -711 pub packets_in_flight: Option<u64>, -712 pub congestion_window: Option<u64>, -713 pub ssthresh: Option<u64>, -714 pub pacing_rate: Option<u64>, -715} -716 -717#[cfg(feature = "qlog")] -718impl RecoveryMetrics { -719 /// Retain only values that have been updated since the last snapshot. -720 fn retain_updated(&self, previous: &Self) -> Self { -721 macro_rules! keep_if_changed { -722 ($name:ident) => { -723 if previous.$name == self.$name { -724 None -725 } else { -726 self.$name -727 } -728 }; -729 } -730 -731 Self { -732 min_rtt: keep_if_changed!(min_rtt), -733 smoothed_rtt: keep_if_changed!(smoothed_rtt), -734 latest_rtt: keep_if_changed!(latest_rtt), -735 rtt_variance: keep_if_changed!(rtt_variance), -736 pto_count: keep_if_changed!(pto_count), -737 bytes_in_flight: keep_if_changed!(bytes_in_flight), -738 packets_in_flight: keep_if_changed!(packets_in_flight), -739 congestion_window: keep_if_changed!(congestion_window), -740 ssthresh: keep_if_changed!(ssthresh), -741 pacing_rate: keep_if_changed!(pacing_rate), -742 } -743 } -744 -745 /// Emit a `MetricsUpdated` event containing only updated values -746 fn to_qlog_event(&self, path_id: PathId, previous: &Self) -> Option<RecoveryMetricsUpdated> { -747 let updated = self.retain_updated(previous); -748 -749 if updated == Self::default() { -750 return None; -751 } -752 -753 Some(RecoveryMetricsUpdated { -754 min_rtt: updated.min_rtt.map(|rtt| rtt.as_micros() as f32 / 1000.0), -755 smoothed_rtt: updated -756 .smoothed_rtt -757 .map(|rtt| rtt.as_micros() as f32 / 1000.0), -758 latest_rtt: updated -759 .latest_rtt -760 .map(|rtt| rtt.as_micros() as f32 / 1000.0), -761 rtt_variance: updated -762 .rtt_variance -763 .map(|rtt| rtt.as_micros() as f32 / 1000.0), -764 pto_count: updated -765 .pto_count -766 .map(|count| count.try_into().unwrap_or(u16::MAX)), -767 bytes_in_flight: updated.bytes_in_flight, -768 packets_in_flight: updated.packets_in_flight, -769 congestion_window: updated.congestion_window, -770 ssthresh: updated.ssthresh, -771 pacing_rate: updated.pacing_rate, -772 path_id: Some(path_id.as_u32() as u64), -773 ex_data: Default::default(), -774 }) -775 } -776} -777 -778/// RTT estimation for a particular network path -779#[derive(Copy, Clone, Debug)] -780pub struct RttEstimator { -781 /// The most recent RTT measurement made when receiving an ack for a previously unacked packet -782 latest: Duration, -783 /// The smoothed RTT of the connection, computed as described in RFC6298 -784 smoothed: Option<Duration>, -785 /// The RTT variance, computed as described in RFC6298 -786 var: Duration, -787 /// The minimum RTT seen in the connection, ignoring ack delay. -788 min: Duration, -789} -790 -791impl RttEstimator { -792 pub(super) fn new(initial_rtt: Duration) -> Self { -793 Self { -794 latest: initial_rtt, -795 smoothed: None, -796 var: initial_rtt / 2, -797 min: initial_rtt, -798 } -799 } -800 -801 /// Resets the estimator using a new initial_rtt value. -802 /// -803 /// This only resets the initial_rtt **if** no samples have been recorded yet. If there -804 /// are any recorded samples the initial estimate can not be adjusted after the fact. -805 /// -806 /// This is useful when you receive a PATH_RESPONSE in the first packet received on a -807 /// new path. In this case you can use the delay of the PATH_CHALLENGE-PATH_RESPONSE as -808 /// the initial RTT to get a better expected estimation. -809 /// -810 /// A PATH_CHALLENGE-PATH_RESPONSE pair later in the connection should not be used -811 /// explicitly as an estimation since PATH_CHALLENGE is an ACK-eliciting packet itself -812 /// already. -813 pub(crate) fn reset_initial_rtt(&mut self, initial_rtt: Duration) { -814 if self.smoothed.is_none() { -815 self.latest = initial_rtt; -816 self.var = initial_rtt / 2; -817 self.min = initial_rtt; -818 } -819 } -820 -821 /// The current best RTT estimation. -822 pub fn get(&self) -> Duration { -823 self.smoothed.unwrap_or(self.latest) -824 } -825 -826 /// Conservative estimate of RTT -827 /// -828 /// Takes the maximum of smoothed and latest RTT, as recommended -829 /// in 6.1.2 of the recovery spec (draft 29). -830 pub fn conservative(&self) -> Duration { -831 self.get().max(self.latest) -832 } -833 -834 /// Minimum RTT registered so far for this estimator. -835 pub fn min(&self) -> Duration { -836 self.min -837 } -838 -839 /// PTO computed as described in RFC9002#6.2.1. -840 pub(crate) fn pto_base(&self) -> Duration { -841 self.get() + cmp::max(4 * self.var, TIMER_GRANULARITY) -842 } -843 -844 /// Records an RTT sample. -845 pub(crate) fn update(&mut self, ack_delay: Duration, rtt: Duration) { -846 self.latest = rtt; -847 // https://www.rfc-editor.org/rfc/rfc9002.html#section-5.2-3: -848 // min_rtt does not adjust for ack_delay to avoid underestimating. -849 self.min = cmp::min(self.min, self.latest); -850 // Based on RFC6298. -851 if let Some(smoothed) = self.smoothed { -852 let adjusted_rtt = if self.min + ack_delay <= self.latest { -853 self.latest - ack_delay -854 } else { -855 self.latest -856 }; -857 let var_sample = smoothed.abs_diff(adjusted_rtt); -858 self.var = (3 * self.var + var_sample) / 4; -859 self.smoothed = Some((7 * smoothed + adjusted_rtt) / 8); -860 } else { -861 self.smoothed = Some(self.latest); -862 self.var = self.latest / 2; -863 self.min = self.latest; -864 } -865 } -866} -867 -868#[derive(Default, Debug)] -869pub(crate) struct PathResponses { -870 pending: Vec<PathResponse>, -871} -872 -873impl PathResponses { -874 pub(crate) fn push(&mut self, packet: u64, token: u64, network_path: FourTuple) { -875 /// An arbitrary permissive limit to prevent abuse. -876 /// -877 /// If we've negotiated the n0 NAT Traversal extension, and one user might have a lot -878 /// of addresses, e.g. because of having lots of interfaces (we've seen >25 interfaces -879 /// on Macs with docker and other things), then we need to be able to process at least -880 /// as many PATH_CHALLENGE frames as there are interfaces. -881 /// On top of that, there are retries, which make it possible that we need to process -882 /// even more. -883 /// -884 /// Considering that there can be up to 2 `PathData`s per active `PathId`, and -885 /// reasonable default values for maximum concurrent multipath paths are ~8 and each -886 /// `PathResponse` struct takes up 72 bytes at the moment this, means an attacker can -887 /// cause us to keep `32 * 2 * 8 * 72 = ~37KB` of data around. -888 const MAX_PATH_RESPONSES: usize = 32; -889 let response = PathResponse { -890 packet, -891 token, -892 network_path, -893 }; -894 let existing = self -895 .pending -896 .iter_mut() -897 .find(|x| x.network_path.remote == network_path.remote); -898 if let Some(existing) = existing { -899 // Update a queued response -900 if existing.packet <= packet { -901 *existing = response; -902 } -903 return; -904 } -905 if self.pending.len() < MAX_PATH_RESPONSES { -906 self.pending.push(response); -907 } else { -908 // We don't expect to ever hit this with well-behaved peers, so we don't bother dropping -909 // older challenges. -910 trace!("ignoring excessive PATH_CHALLENGE"); -911 } -912 } -913 -914 pub(crate) fn pop_off_path(&mut self, network_path: FourTuple) -> Option<(u64, FourTuple)> { -915 let response = *self.pending.last()?; -916 // We use an exact comparison here, because once we've received for the first time, -917 // we really should either already have a local_ip, or we will never get one -918 // (because our OS doesn't support it). -919 if response.network_path == network_path { -920 // We don't bother searching further because we expect that the on-path response will -921 // get drained in the immediate future by a call to `pop_on_path` -922 return None; -923 } -924 self.pending.pop(); -925 Some((response.token, response.network_path)) -926 } -927 -928 pub(crate) fn pop_on_path(&mut self, network_path: FourTuple) -> Option<u64> { -929 let response = *self.pending.last()?; -930 // Using an exact comparison. See explanation in `pop_off_path`. -931 if response.network_path != network_path { -932 // We don't bother searching further because we expect that the off-path response will -933 // get drained in the immediate future by a call to `pop_off_path` -934 return None; -935 } -936 self.pending.pop(); -937 Some(response.token) -938 } -939 -940 pub(crate) fn is_empty(&self) -> bool { -941 self.pending.is_empty() -942 } -943} -944 -945#[derive(Copy, Clone, Debug)] -946struct PathResponse { -947 /// The packet number the corresponding PATH_CHALLENGE was received in -948 packet: u64, -949 /// The token of the PATH_CHALLENGE -950 token: u64, -951 /// The path the corresponding PATH_CHALLENGE was received from -952 network_path: FourTuple, -953} -954 -955/// Summary statistics of packets that have been sent on a particular path, but which have not yet -956/// been acked or deemed lost -957#[derive(Debug)] -958pub(super) struct InFlight { -959 /// Sum of the sizes of all sent packets considered "in flight" by congestion control -960 /// -961 /// The size does not include IP or UDP overhead. Packets only containing ACK frames do not -962 /// count towards this to ensure congestion control does not impede congestion feedback. -963 pub(super) bytes: u64, -964 /// Number of packets in flight containing frames other than ACK and PADDING -965 /// -966 /// This can be 0 even when bytes is not 0 because PADDING frames cause a packet to be -967 /// considered "in flight" by congestion control. However, if this is nonzero, bytes will always -968 /// also be nonzero. -969 pub(super) ack_eliciting: u64, -970} -971 -972impl InFlight { -973 fn new() -> Self { -974 Self { -975 bytes: 0, -976 ack_eliciting: 0, -977 } -978 } -979 -980 fn insert(&mut self, packet: &SentPacket) { -981 self.bytes += u64::from(packet.size); -982 self.ack_eliciting += u64::from(packet.ack_eliciting); -983 } -984 -985 /// Update counters to account for a packet becoming acknowledged, lost, or abandoned -986 fn remove(&mut self, packet: &SentPacket) { -987 self.bytes -= u64::from(packet.size); -988 self.ack_eliciting -= u64::from(packet.ack_eliciting); -989 } -990} -991 -992/// State for QUIC-MULTIPATH PATH_STATUS_AVAILABLE and PATH_STATUS_BACKUP frames -993#[derive(Debug, Clone, Default)] -994pub(super) struct PathStatusState { -995 /// The local status -996 local_status: PathStatus, -997 /// Local sequence number, for both PATH_STATUS_AVAILABLE and PATH_STATUS_BACKUP -998 /// -999 /// This is the number of the *next* path status frame to be sent. -1000 local_seq: VarInt, -1001 /// The status set by the remote -1002 remote_status: Option<(VarInt, PathStatus)>, -1003} -1004 -1005impl PathStatusState { -1006 /// To be called on received PATH_STATUS_AVAILABLE/PATH_STATUS_BACKUP frames -1007 pub(super) fn remote_update(&mut self, status: PathStatus, seq: VarInt) { -1008 if self.remote_status.is_some_and(|(curr, _)| curr >= seq) { -1009 return trace!(%seq, "ignoring path status update"); -1010 } +686#[cfg(feature = "qlog")] +687impl RecoveryMetrics { +688 /// Retain only values that have been updated since the last snapshot. +689 fn retain_updated(&self, previous: &Self) -> Self { +690 macro_rules! keep_if_changed { +691 ($name:ident) => { +692 if previous.$name == self.$name { +693 None +694 } else { +695 self.$name +696 } +697 }; +698 } +699 +700 Self { +701 min_rtt: keep_if_changed!(min_rtt), +702 smoothed_rtt: keep_if_changed!(smoothed_rtt), +703 latest_rtt: keep_if_changed!(latest_rtt), +704 rtt_variance: keep_if_changed!(rtt_variance), +705 pto_count: keep_if_changed!(pto_count), +706 bytes_in_flight: keep_if_changed!(bytes_in_flight), +707 packets_in_flight: keep_if_changed!(packets_in_flight), +708 congestion_window: keep_if_changed!(congestion_window), +709 ssthresh: keep_if_changed!(ssthresh), +710 pacing_rate: keep_if_changed!(pacing_rate), +711 } +712 } +713 +714 /// Emit a `MetricsUpdated` event containing only updated values +715 fn to_qlog_event(&self, path_id: PathId, previous: &Self) -> Option<RecoveryMetricsUpdated> { +716 let updated = self.retain_updated(previous); +717 +718 if updated == Self::default() { +719 return None; +720 } +721 +722 Some(RecoveryMetricsUpdated { +723 min_rtt: updated.min_rtt.map(|rtt| rtt.as_micros() as f32 / 1000.0), +724 smoothed_rtt: updated +725 .smoothed_rtt +726 .map(|rtt| rtt.as_micros() as f32 / 1000.0), +727 latest_rtt: updated +728 .latest_rtt +729 .map(|rtt| rtt.as_micros() as f32 / 1000.0), +730 rtt_variance: updated +731 .rtt_variance +732 .map(|rtt| rtt.as_micros() as f32 / 1000.0), +733 pto_count: updated +734 .pto_count +735 .map(|count| count.try_into().unwrap_or(u16::MAX)), +736 bytes_in_flight: updated.bytes_in_flight, +737 packets_in_flight: updated.packets_in_flight, +738 congestion_window: updated.congestion_window, +739 ssthresh: updated.ssthresh, +740 pacing_rate: updated.pacing_rate, +741 path_id: Some(path_id.as_u32() as u64), +742 ex_data: Default::default(), +743 }) +744 } +745} +746 +747/// RTT estimation for a particular network path +748#[derive(Copy, Clone, Debug)] +749pub struct RttEstimator { +750 /// The most recent RTT measurement made when receiving an ack for a previously unacked packet +751 latest: Duration, +752 /// The smoothed RTT of the connection, computed as described in RFC6298 +753 smoothed: Option<Duration>, +754 /// The RTT variance, computed as described in RFC6298 +755 var: Duration, +756 /// The minimum RTT seen in the connection, ignoring ack delay. +757 min: Duration, +758} +759 +760impl RttEstimator { +761 pub(super) fn new(initial_rtt: Duration) -> Self { +762 Self { +763 latest: initial_rtt, +764 smoothed: None, +765 var: initial_rtt / 2, +766 min: initial_rtt, +767 } +768 } +769 +770 /// Resets the estimator using a new initial_rtt value. +771 /// +772 /// This only resets the initial_rtt **if** no samples have been recorded yet. If there +773 /// are any recorded samples the initial estimate can not be adjusted after the fact. +774 /// +775 /// This is useful when you receive a PATH_RESPONSE in the first packet received on a +776 /// new path. In this case you can use the delay of the PATH_CHALLENGE-PATH_RESPONSE as +777 /// the initial RTT to get a better expected estimation. +778 /// +779 /// A PATH_CHALLENGE-PATH_RESPONSE pair later in the connection should not be used +780 /// explicitly as an estimation since PATH_CHALLENGE is an ACK-eliciting packet itself +781 /// already. +782 pub(crate) fn reset_initial_rtt(&mut self, initial_rtt: Duration) { +783 if self.smoothed.is_none() { +784 self.latest = initial_rtt; +785 self.var = initial_rtt / 2; +786 self.min = initial_rtt; +787 } +788 } +789 +790 /// The current best RTT estimation. +791 pub fn get(&self) -> Duration { +792 self.smoothed.unwrap_or(self.latest) +793 } +794 +795 /// Conservative estimate of RTT +796 /// +797 /// Takes the maximum of smoothed and latest RTT, as recommended +798 /// in 6.1.2 of the recovery spec (draft 29). +799 pub fn conservative(&self) -> Duration { +800 self.get().max(self.latest) +801 } +802 +803 /// Minimum RTT registered so far for this estimator. +804 pub fn min(&self) -> Duration { +805 self.min +806 } +807 +808 /// PTO computed as described in RFC9002#6.2.1. +809 pub(crate) fn pto_base(&self) -> Duration { +810 self.get() + cmp::max(4 * self.var, TIMER_GRANULARITY) +811 } +812 +813 /// Records an RTT sample. +814 pub(crate) fn update(&mut self, ack_delay: Duration, rtt: Duration) { +815 self.latest = rtt; +816 // https://www.rfc-editor.org/rfc/rfc9002.html#section-5.2-3: +817 // min_rtt does not adjust for ack_delay to avoid underestimating. +818 self.min = cmp::min(self.min, self.latest); +819 // Based on RFC6298. +820 if let Some(smoothed) = self.smoothed { +821 let adjusted_rtt = if self.min + ack_delay <= self.latest { +822 self.latest - ack_delay +823 } else { +824 self.latest +825 }; +826 let var_sample = smoothed.abs_diff(adjusted_rtt); +827 self.var = (3 * self.var + var_sample) / 4; +828 self.smoothed = Some((7 * smoothed + adjusted_rtt) / 8); +829 } else { +830 self.smoothed = Some(self.latest); +831 self.var = self.latest / 2; +832 self.min = self.latest; +833 } +834 } +835} +836 +837#[derive(Default, Debug)] +838pub(crate) struct PathResponses { +839 pending: Vec<PathResponse>, +840} +841 +842impl PathResponses { +843 pub(crate) fn push(&mut self, packet: u64, token: u64, network_path: FourTuple) { +844 /// An arbitrary permissive limit to prevent abuse. +845 /// +846 /// If we've negotiated the n0 NAT Traversal extension, and one user might have a lot +847 /// of addresses, e.g. because of having lots of interfaces (we've seen >25 interfaces +848 /// on Macs with docker and other things), then we need to be able to process at least +849 /// as many PATH_CHALLENGE frames as there are interfaces. +850 /// On top of that, there are retries, which make it possible that we need to process +851 /// even more. +852 /// +853 /// Considering that there can be up to 2 `PathData`s per active `PathId`, and +854 /// reasonable default values for maximum concurrent multipath paths are ~8 and each +855 /// `PathResponse` struct takes up 72 bytes at the moment this, means an attacker can +856 /// cause us to keep `32 * 2 * 8 * 72 = ~37KB` of data around. +857 const MAX_PATH_RESPONSES: usize = 32; +858 let response = PathResponse { +859 packet, +860 token, +861 network_path, +862 }; +863 let existing = self +864 .pending +865 .iter_mut() +866 .find(|x| x.network_path.remote == network_path.remote); +867 if let Some(existing) = existing { +868 // Update a queued response +869 if existing.packet <= packet { +870 *existing = response; +871 } +872 return; +873 } +874 if self.pending.len() < MAX_PATH_RESPONSES { +875 self.pending.push(response); +876 } else { +877 // We don't expect to ever hit this with well-behaved peers, so we don't bother dropping +878 // older challenges. +879 trace!("ignoring excessive PATH_CHALLENGE"); +880 } +881 } +882 +883 pub(crate) fn pop_off_path(&mut self, network_path: FourTuple) -> Option<(u64, FourTuple)> { +884 let response = *self.pending.last()?; +885 // We use an exact comparison here, because once we've received for the first time, +886 // we really should either already have a local_ip, or we will never get one +887 // (because our OS doesn't support it). And even if we get it wrong we are only +888 // slightly less efficient and would not include other on-path data in the packet. +889 if response.network_path == network_path { +890 // We don't bother searching further because we expect that the on-path response will +891 // get drained in the immediate future by a call to `pop_on_path` +892 return None; +893 } +894 self.pending.pop(); +895 Some((response.token, response.network_path)) +896 } +897 +898 pub(crate) fn pop_on_path(&mut self, network_path: FourTuple) -> Option<u64> { +899 let response = *self.pending.last()?; +900 // Using an exact comparison. See explanation in `pop_off_path`. +901 if response.network_path != network_path { +902 // We don't bother searching further because we expect that the off-path response will +903 // get drained in the immediate future by a call to `pop_off_path` +904 return None; +905 } +906 self.pending.pop(); +907 Some(response.token) +908 } +909 +910 /// Whether the next [`Self::pop_on_path`] will return something to send. +911 pub(crate) fn has_pending_on_path(&self, network_path: FourTuple) -> bool { +912 self.pending +913 .last() +914 .is_some_and(|response| response.network_path == network_path) +915 } +916 +917 pub(crate) fn is_empty(&self) -> bool { +918 self.pending.is_empty() +919 } +920} +921 +922#[derive(Copy, Clone, Debug)] +923struct PathResponse { +924 /// The packet number the corresponding PATH_CHALLENGE was received in +925 packet: u64, +926 /// The token of the PATH_CHALLENGE +927 token: u64, +928 /// The path the corresponding PATH_CHALLENGE was received from +929 network_path: FourTuple, +930} +931 +932/// Summary statistics of packets that have been sent on a particular path, but which have not yet +933/// been acked or deemed lost +934#[derive(Debug)] +935pub(super) struct InFlight { +936 /// Sum of the sizes of all sent packets considered "in flight" by congestion control +937 /// +938 /// The size does not include IP or UDP overhead. Packets only containing ACK frames do not +939 /// count towards this to ensure congestion control does not impede congestion feedback. +940 pub(super) bytes: u64, +941 /// Number of packets in flight containing frames other than ACK and PADDING +942 /// +943 /// This can be 0 even when bytes is not 0 because PADDING frames cause a packet to be +944 /// considered "in flight" by congestion control. However, if this is nonzero, bytes will always +945 /// also be nonzero. +946 pub(super) ack_eliciting: u64, +947} +948 +949impl InFlight { +950 fn new() -> Self { +951 Self { +952 bytes: 0, +953 ack_eliciting: 0, +954 } +955 } +956 +957 fn insert(&mut self, packet: &SentPacket) { +958 self.bytes += u64::from(packet.size); +959 self.ack_eliciting += u64::from(packet.ack_eliciting); +960 } +961 +962 /// Update counters to account for a packet becoming acknowledged, lost, or abandoned +963 fn remove(&mut self, packet: &SentPacket) { +964 self.bytes -= u64::from(packet.size); +965 self.ack_eliciting -= u64::from(packet.ack_eliciting); +966 } +967} +968 +969/// State for QUIC-MULTIPATH PATH_STATUS_AVAILABLE and PATH_STATUS_BACKUP frames +970#[derive(Debug, Clone, Default)] +971pub(super) struct PathStatusState { +972 /// The local status +973 local_status: PathStatus, +974 /// Local sequence number, for both PATH_STATUS_AVAILABLE and PATH_STATUS_BACKUP +975 /// +976 /// This is the number of the *next* path status frame to be sent. +977 local_seq: VarInt, +978 /// The status set by the remote +979 remote_status: Option<(VarInt, PathStatus)>, +980} +981 +982impl PathStatusState { +983 /// To be called on received PATH_STATUS_AVAILABLE/PATH_STATUS_BACKUP frames +984 pub(super) fn remote_update(&mut self, status: PathStatus, seq: VarInt) { +985 if self.remote_status.is_some_and(|(curr, _)| curr >= seq) { +986 return trace!(%seq, "ignoring path status update"); +987 } +988 +989 let prev = self.remote_status.replace((seq, status)).map(|(_, s)| s); +990 if prev != Some(status) { +991 debug!(?status, ?seq, "remote changed path status"); +992 } +993 } +994 +995 /// Updates the local status +996 /// +997 /// If the local status changed, the previous value is returned +998 pub(super) fn local_update(&mut self, status: PathStatus) -> Option<PathStatus> { +999 if self.local_status == status { +1000 return None; +1001 } +1002 +1003 self.local_seq = self.local_seq.saturating_add(1u8); +1004 Some(std::mem::replace(&mut self.local_status, status)) +1005 } +1006 +1007 pub(crate) fn seq(&self) -> VarInt { +1008 self.local_seq +1009 } +1010} 1011 -1012 let prev = self.remote_status.replace((seq, status)).map(|(_, s)| s); -1013 if prev != Some(status) { -1014 debug!(?status, ?seq, "remote changed path status"); -1015 } -1016 } -1017 -1018 /// Updates the local status -1019 /// -1020 /// If the local status changed, the previous value is returned -1021 pub(super) fn local_update(&mut self, status: PathStatus) -> Option<PathStatus> { -1022 if self.local_status == status { -1023 return None; -1024 } -1025 -1026 self.local_seq = self.local_seq.saturating_add(1u8); -1027 Some(std::mem::replace(&mut self.local_status, status)) -1028 } -1029 -1030 pub(crate) fn seq(&self) -> VarInt { -1031 self.local_seq -1032 } -1033} -1034 -1035/// The QUIC-MULTIPATH path status -1036/// -1037/// See section "3.3 Path Status Management": -1038/// <https://quicwg.org/multipath/draft-ietf-quic-multipath.html#name-path-status-management> -1039#[cfg_attr(test, derive(test_strategy::Arbitrary))] -1040#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] -1041pub enum PathStatus { -1042 /// Paths marked with as available will be used when scheduling packets +1012/// The QUIC-MULTIPATH path status +1013/// +1014/// See section "3.3 Path Status Management": +1015/// <https://quicwg.org/multipath/draft-ietf-quic-multipath.html#name-path-status-management> +1016#[cfg_attr(test, derive(test_strategy::Arbitrary))] +1017#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] +1018pub enum PathStatus { +1019 /// Paths marked with as available will be used when scheduling packets +1020 /// +1021 /// If multiple paths are available, packets will be scheduled on whichever has +1022 /// capacity. +1023 #[default] +1024 Available, +1025 /// Paths marked as backup will only be used if there are no available paths +1026 /// +1027 /// If the max_idle_timeout is specified the path will be kept alive so that it does not +1028 /// expire. +1029 Backup, +1030} +1031 +1032/// Application events about paths +1033#[derive(Debug, Clone, PartialEq, Eq)] +1034#[non_exhaustive] +1035pub enum PathEvent { +1036 /// A new path has established connection with the peer. +1037 #[non_exhaustive] +1038 Established { +1039 /// The path which can now be used for application data. +1040 id: PathId, +1041 }, +1042 /// A path was abandoned and is no longer usable. 1043 /// -1044 /// If multiple paths are available, packets will be scheduled on whichever has -1045 /// capacity. -1046 #[default] -1047 Available, -1048 /// Paths marked as backup will only be used if there are no available paths -1049 /// -1050 /// If the max_idle_timeout is specified the path will be kept alive so that it does not -1051 /// expire. -1052 Backup, -1053} -1054 -1055/// Application events about paths -1056#[derive(Debug, Clone, PartialEq, Eq)] -1057#[non_exhaustive] -1058pub enum PathEvent { -1059 /// A new path has established connection with the peer. -1060 #[non_exhaustive] -1061 Established { -1062 /// The path which can now be used for application data. -1063 id: PathId, -1064 }, -1065 /// A path was abandoned and is no longer usable. -1066 /// -1067 /// Note that this may be the first event for a path: If a path is abandoned -1068 /// before having been established, no [`Self::Established`] event is emitted. -1069 /// -1070 /// This event will always be followed by [`Self::Discarded`] after some time. -1071 #[non_exhaustive] -1072 Abandoned { -1073 /// The path that was abandoned. -1074 id: PathId, -1075 /// Reason why this path was abandoned. -1076 reason: PathAbandonReason, -1077 }, -1078 /// A path was discarded and all remaining state for it has been removed. -1079 /// -1080 /// This event is the last event for a path, and is always emitted after [`Self::Abandoned`]. -1081 #[non_exhaustive] -1082 Discarded { -1083 /// Which path had its state dropped +1044 /// Note that this may be the first event for a path: If a path is abandoned +1045 /// before having been established, no [`Self::Established`] event is emitted. +1046 /// +1047 /// This event will always be followed by [`Self::Discarded`] after some time. +1048 #[non_exhaustive] +1049 Abandoned { +1050 /// The path that was abandoned. +1051 id: PathId, +1052 /// Reason why this path was abandoned. +1053 reason: PathAbandonReason, +1054 }, +1055 /// A path was discarded and all remaining state for it has been removed. +1056 /// +1057 /// This event is the last event for a path, and is always emitted after [`Self::Abandoned`]. +1058 #[non_exhaustive] +1059 Discarded { +1060 /// Which path had its state dropped +1061 id: PathId, +1062 /// The final path stats, they are no longer available via [`Connection::stats`] +1063 /// +1064 /// [`Connection::stats`]: super::Connection::stats +1065 path_stats: Box<PathStats>, +1066 }, +1067 /// The remote changed the status of the path +1068 /// +1069 /// The local status is not changed because of this event. It is up to the application +1070 /// to update the local status, which is used for packet scheduling, when the remote +1071 /// changes the status. +1072 #[non_exhaustive] +1073 RemoteStatus { +1074 /// Path which has changed status +1075 id: PathId, +1076 /// The new status set by the remote +1077 status: PathStatus, +1078 }, +1079 /// Received an observation of our external address from the peer. +1080 #[non_exhaustive] +1081 ObservedAddr { +1082 /// Path over which the observed address was reported, [`PathId::ZERO`] when multipath is +1083 /// not negotiated 1084 id: PathId, -1085 /// The final path stats, they are no longer available via [`Connection::stats`] -1086 /// -1087 /// [`Connection::stats`]: super::Connection::stats -1088 path_stats: Box<PathStats>, -1089 }, -1090 /// The remote changed the status of the path -1091 /// -1092 /// The local status is not changed because of this event. It is up to the application -1093 /// to update the local status, which is used for packet scheduling, when the remote -1094 /// changes the status. -1095 #[non_exhaustive] -1096 RemoteStatus { -1097 /// Path which has changed status -1098 id: PathId, -1099 /// The new status set by the remote -1100 status: PathStatus, -1101 }, -1102 /// Received an observation of our external address from the peer. -1103 #[non_exhaustive] -1104 ObservedAddr { -1105 /// Path over which the observed address was reported, [`PathId::ZERO`] when multipath is -1106 /// not negotiated -1107 id: PathId, -1108 /// The address observed by the remote over this path -1109 addr: SocketAddr, -1110 }, -1111} -1112 -1113/// Reason for why a path was abandoned. -1114#[derive(Debug, Clone, Eq, PartialEq)] -1115pub enum PathAbandonReason { -1116 /// The path was closed locally by the application. -1117 ApplicationClosed { -1118 /// The error code to be sent with the abandon frame. -1119 error_code: VarInt, -1120 }, -1121 /// We didn't receive a path response in time after opening this path. -1122 ValidationFailed, -1123 /// We didn't receive any data from the remote within the path's idle timeout. -1124 TimedOut, -1125 /// The path became unusable after a local network change. -1126 UnusableAfterNetworkChange, -1127 /// The remote closed the path. -1128 RemoteAbandoned { -1129 /// The error that was sent with the abandon frame. -1130 error_code: VarInt, -1131 }, -1132} -1133 -1134impl PathAbandonReason { -1135 /// Whether this abandon was initiated by the remote peer. -1136 pub(crate) fn is_remote(&self) -> bool { -1137 matches!(self, Self::RemoteAbandoned { .. }) -1138 } +1085 /// The address observed by the remote over this path +1086 addr: SocketAddr, +1087 }, +1088} +1089 +1090/// Reason for why a path was abandoned. +1091#[derive(Debug, Clone, Eq, PartialEq)] +1092pub enum PathAbandonReason { +1093 /// The path was closed locally by the application. +1094 ApplicationClosed { +1095 /// The error code to be sent with the abandon frame. +1096 error_code: VarInt, +1097 }, +1098 /// We didn't receive a path response in time after opening this path. +1099 ValidationFailed, +1100 /// We didn't receive any data from the remote within the path's idle timeout. +1101 TimedOut, +1102 /// The path became unusable after a local network change. +1103 UnusableAfterNetworkChange, +1104 /// The remote closed the path. +1105 RemoteAbandoned { +1106 /// The error that was sent with the abandon frame. +1107 error_code: VarInt, +1108 }, +1109} +1110 +1111impl PathAbandonReason { +1112 /// Whether this abandon was initiated by the remote peer. +1113 pub(crate) fn is_remote(&self) -> bool { +1114 matches!(self, Self::RemoteAbandoned { .. }) +1115 } +1116 +1117 /// Returns the error code to send with a PATH_ABANDON frame. +1118 pub(crate) fn error_code(&self) -> TransportErrorCode { +1119 match self { +1120 Self::ApplicationClosed { error_code } => (*error_code).into(), +1121 Self::ValidationFailed | Self::TimedOut | Self::UnusableAfterNetworkChange => { +1122 TransportErrorCode::PATH_UNSTABLE_OR_POOR +1123 } +1124 Self::RemoteAbandoned { error_code } => (*error_code).into(), +1125 } +1126 } +1127} +1128 +1129/// Error from setting path status +1130#[derive(Debug, Error, Clone, PartialEq, Eq)] +1131pub enum SetPathStatusError { +1132 /// Error indicating that a path has not been opened or has already been abandoned +1133 #[error("closed path")] +1134 ClosedPath, +1135 /// Error indicating that this operation requires multipath to be negotiated whereas it hasn't been +1136 #[error("multipath not negotiated")] +1137 MultipathNotNegotiated, +1138} 1139 -1140 /// Returns the error code to send with a PATH_ABANDON frame. -1141 pub(crate) fn error_code(&self) -> TransportErrorCode { -1142 match self { -1143 Self::ApplicationClosed { error_code } => (*error_code).into(), -1144 Self::ValidationFailed | Self::TimedOut | Self::UnusableAfterNetworkChange => { -1145 TransportErrorCode::PATH_UNSTABLE_OR_POOR -1146 } -1147 Self::RemoteAbandoned { error_code } => (*error_code).into(), -1148 } -1149 } -1150} -1151 -1152/// Error from setting path status -1153#[derive(Debug, Error, Clone, PartialEq, Eq)] -1154pub enum SetPathStatusError { -1155 /// Error indicating that a path has not been opened or has already been abandoned -1156 #[error("closed path")] -1157 ClosedPath, -1158 /// Error indicating that this operation requires multipath to be negotiated whereas it hasn't been -1159 #[error("multipath not negotiated")] -1160 MultipathNotNegotiated, -1161} -1162 -1163/// Error indicating that a path has not been opened or has already been abandoned -1164#[derive(Debug, Default, Error, Clone, PartialEq, Eq)] -1165#[error("closed path")] -1166pub struct ClosedPath { -1167 pub(super) _private: (), -1168} -1169 -1170#[cfg(test)] -1171mod tests { -1172 use super::*; -1173 -1174 #[test] -1175 fn test_path_id_saturating_add() { -1176 // add within range behaves normally -1177 let large: PathId = u16::MAX.into(); -1178 let next = u32::from(u16::MAX) + 1; -1179 assert_eq!(large.saturating_add(1u8), PathId::from(next)); -1180 -1181 // outside range saturates -1182 assert_eq!(PathId::MAX.saturating_add(1u8), PathId::MAX) -1183 } -1184}

\ No newline at end of file +1140/// Error indicating that a path has not been opened or has already been abandoned +1141#[derive(Debug, Default, Error, Clone, PartialEq, Eq)] +1142#[error("closed path")] +1143pub struct ClosedPath { +1144 pub(super) _private: (), +1145} +1146 +1147#[cfg(test)] +1148mod tests { +1149 use super::*; +1150 +1151 #[test] +1152 fn test_path_id_saturating_add() { +1153 // add within range behaves normally +1154 let large: PathId = u16::MAX.into(); +1155 let next = u32::from(u16::MAX) + 1; +1156 assert_eq!(large.saturating_add(1u8), PathId::from(next)); +1157 +1158 // outside range saturates +1159 assert_eq!(PathId::MAX.saturating_add(1u8), PathId::MAX) +1160 } +1161}
\ No newline at end of file diff --git a/pr/707/docs/src/noq_proto/connection/spaces.rs.html b/pr/707/docs/src/noq_proto/connection/spaces.rs.html index 1233116da..7ccf16bee 100644 --- a/pr/707/docs/src/noq_proto/connection/spaces.rs.html +++ b/pr/707/docs/src/noq_proto/connection/spaces.rs.html @@ -10,7 +10,7 @@ 10use sorted_index_buffer::SortedIndexBuffer; 11use tracing::trace; 12 -13use super::PathId; +13use super::{PathId, paths::PathResponses}; 14use crate::{ 15 Dir, Duration, FourTuple, Instant, StreamId, TransportError, TransportErrorCode, VarInt, 16 connection::StreamsState, @@ -103,7 +103,7 @@ 103 if request_immediate_ack { 104 // The probe should be ACKed without delay (should only be used in the Data space and 105 // when the peer supports the acknowledgement frequency extension) -106 self.for_path(path_id).immediate_ack_pending = true; +106 self.for_path(path_id).pending_immediate_ack = true; 107 } 108 109 // We prefer to send new data to make most efficient use of bandwidth. @@ -129,8 +129,8 @@ 129 // Nothing new to send and nothing to retransmit, so fall back on a ping. This should only 130 // happen in rare cases during the handshake when the server becomes blocked by 131 // anti-amplification. -132 if !self.for_path(path_id).immediate_ack_pending { -133 self.for_path(path_id).ping_pending = true; +132 if !self.for_path(path_id).pending_immediate_ack { +133 self.for_path(path_id).pending_ping = true; 134 } 135 } 136 @@ -146,1450 +146,1483 @@ 146 .number_spaces 147 .values() 148 .any(|pns| pns.pending_acks.can_send()); -149 let space_specific = self -150 .number_spaces -151 .get(&path_id) -152 .is_some_and(|s| s.ping_pending || s.immediate_ack_pending); -153 let other = !self.pending.is_empty(streams); -154 SendableFrames { -155 acks, -156 close: false, -157 space_specific, -158 other, -159 } -160 } -161} -162 -163impl Index<SpaceId> for [PacketSpace; 3] { -164 type Output = PacketSpace; -165 fn index(&self, space: SpaceId) -> &PacketSpace { -166 &self.as_ref()[space as usize] -167 } -168} -169 -170impl IndexMut<SpaceId> for [PacketSpace; 3] { -171 fn index_mut(&mut self, space: SpaceId) -> &mut PacketSpace { -172 &mut self.as_mut()[space as usize] -173 } -174} -175 -176/// The three QUIC packet number space kinds -177/// -178/// Unlike [`SpaceId`], this always has exactly three variants — it represents the -179/// encryption level / space kind, not a specific packet number space identity. -180#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -181pub(crate) enum SpaceKind { -182 /// Initial packets (client and server). -183 Initial = 0, -184 /// Handshake packets. -185 Handshake = 1, -186 /// Data (1-RTT and 0-RTT) -187 Data = 2, -188} -189 -190impl SpaceKind { -191 /// Returns the encryption level for this space kind. -192 pub(crate) fn encryption_level(self) -> super::EncryptionLevel { -193 match self { -194 Self::Initial => super::EncryptionLevel::Initial, -195 Self::Handshake => super::EncryptionLevel::Handshake, -196 Self::Data => super::EncryptionLevel::OneRtt, -197 } -198 } -199} -200 -201impl Index<SpaceKind> for [PacketSpace; 3] { -202 type Output = PacketSpace; -203 fn index(&self, space: SpaceKind) -> &PacketSpace { -204 &self.as_ref()[space as usize] -205 } -206} -207 -208impl IndexMut<SpaceKind> for [PacketSpace; 3] { -209 fn index_mut(&mut self, space: SpaceKind) -> &mut PacketSpace { -210 &mut self.as_mut()[space as usize] -211 } -212} -213 -214/// The state of a single packet number space. -215/// -216/// In RFC9000 there are 3 packet number spaces: Initial, Handshake and Data. In QUIC -217/// Multipath there are multiple packet number spaces for Data, each identified by a -218/// [`PathId`]. -219/// -220/// This contains the state for a packet number space which is not specific to the 4-tuple -221/// this space is currently using. The 4-tuple specific state, like congestion controller, -222/// pacing, ECN, MTU etc, is stored in [`PathData`]. -223/// -224/// Note that the `Initial`, `Handshake` and `Data(PathId(0))` space all share the same -225/// [`PathData`]. -226/// -227/// You should access this via [`PacketSpace::for_path`]. -228/// -229/// [`PathData`]: super::paths::PathData -230pub(super) struct PacketNumberSpace { -231 /// Highest received packet number, if any -232 pub(super) largest_received_packet_number: Option<u64>, -233 /// The packet number of the next packet that will be sent, if any. In the Data space, the -234 /// packet number stored here is sometimes skipped by [`PacketNumberFilter`] logic. -235 pub(super) next_packet_number: u64, -236 /// The largest packet number the remote peer acknowledged in an ACK frame. -237 pub(super) largest_acked_packet_pn: Option<u64>, -238 pub(super) largest_acked_packet_send_time: Instant, -239 /// The highest-numbered ACK-eliciting packet we've sent -240 pub(super) largest_ack_eliciting_sent: u64, -241 /// Number of packets in `sent_packets` with numbers above `largest_ack_eliciting_sent` -242 pub(super) unacked_non_ack_eliciting_tail: u64, -243 /// Transmitted but not acked -244 // We use a BTreeMap here so we can efficiently query by range on ACK and for loss detection -245 pub(super) sent_packets: SortedIndexBuffer<SentPacket>, -246 /// Packets that were deemed lost -247 // Older packets are regularly removed in `Connection::drain_lost_packets`. -248 pub(super) lost_packets: SortedIndexBuffer<LostPacket>, -249 /// Number of explicit congestion notification codepoints seen on incoming packets -250 pub(super) ecn_counters: frame::EcnCounts, -251 /// Recent ECN counters sent by the peer in ACK frames -252 /// -253 /// Updated (and inspected) whenever we receive an ACK with a new highest acked packet -254 /// number. Stored per-space to simplify verification, which would otherwise have difficulty -255 /// distinguishing between ECN bleaching and counts having been updated by a near-simultaneous -256 /// ACK already processed in another space. -257 pub(super) ecn_feedback: frame::EcnCounts, -258 /// A PING frame needs to be sent on this path -259 pub(super) ping_pending: bool, -260 /// An IMMEDIATE_ACK (draft-ietf-quic-ack-frequency) frame needs to be sent on this path -261 pub(super) immediate_ack_pending: bool, -262 /// Packet deduplicator -263 pub(super) dedup: Dedup, -264 /// Packet numbers to acknowledge -265 pub(super) pending_acks: PendingAcks, -266 -267 // -268 // Loss Detection -269 // -270 /// The time the most recently sent retransmittable packet was sent. -271 pub(super) time_of_last_ack_eliciting_packet: Option<Instant>, -272 /// Earliest time when we might declare a packet lost. -273 /// -274 /// The time at which the earliest sent packet in this space will be considered lost -275 /// based on exceeding the reordering window in time. Only set for packets numbered -276 /// prior to a packet that has been acknowledged. -277 pub(super) loss_time: Option<Instant>, -278 /// Number of tail loss probes to send -279 pub(super) loss_probes: u32, -280 -281 /// Packet numbers to skip, only used in the data package space. -282 pn_filter: Option<PacketNumberFilter>, -283} -284 -285impl PacketNumberSpace { -286 pub(super) fn new(now: Instant, space: SpaceId, rng: &mut (impl CryptoRng + ?Sized)) -> Self { -287 let pn_filter = match space { -288 SpaceId::Initial | SpaceId::Handshake => None, -289 SpaceId::Data => Some(PacketNumberFilter::new(rng)), -290 }; -291 Self { -292 largest_received_packet_number: None, -293 next_packet_number: 0, -294 largest_acked_packet_pn: None, -295 largest_acked_packet_send_time: now, -296 largest_ack_eliciting_sent: 0, -297 unacked_non_ack_eliciting_tail: 0, -298 sent_packets: SortedIndexBuffer::new(), -299 lost_packets: SortedIndexBuffer::new(), -300 ecn_counters: frame::EcnCounts::ZERO, -301 ecn_feedback: frame::EcnCounts::ZERO, -302 ping_pending: false, -303 immediate_ack_pending: false, -304 dedup: Default::default(), -305 pending_acks: PendingAcks::new(), -306 time_of_last_ack_eliciting_packet: None, -307 loss_time: None, -308 loss_probes: 0, -309 pn_filter, -310 } -311 } -312 -313 #[cfg(test)] -314 fn new_deterministic(now: Instant, space: SpaceId) -> Self { -315 let pn_filter = match space { -316 SpaceId::Initial | SpaceId::Handshake => None, -317 SpaceId::Data => Some(PacketNumberFilter::disabled()), -318 }; -319 Self { -320 largest_received_packet_number: None, -321 next_packet_number: 0, -322 largest_acked_packet_pn: None, -323 largest_acked_packet_send_time: now, -324 largest_ack_eliciting_sent: 0, -325 unacked_non_ack_eliciting_tail: 0, -326 sent_packets: SortedIndexBuffer::new(), -327 lost_packets: SortedIndexBuffer::new(), -328 ecn_counters: frame::EcnCounts::ZERO, -329 ecn_feedback: frame::EcnCounts::ZERO, -330 ping_pending: false, -331 immediate_ack_pending: false, -332 dedup: Default::default(), -333 pending_acks: PendingAcks::new(), -334 time_of_last_ack_eliciting_packet: None, -335 loss_time: None, -336 loss_probes: 0, -337 pn_filter, -338 } -339 } -340 -341 /// Get the next outgoing packet number in this space -342 /// -343 /// In the Data space, the connection's [`PacketNumberFilter`] must be used rather than calling -344 /// this directly. -345 pub(super) fn get_tx_number(&mut self, rng: &mut (impl CryptoRng + ?Sized)) -> u64 { -346 // TODO: Handle packet number overflow gracefully -347 assert!(self.next_packet_number < 2u64.pow(62)); -348 let mut pn = self.next_packet_number; -349 self.next_packet_number += 1; -350 -351 // Skip this number if the filter says so, only enabled in the data space -352 if let Some(ref mut filter) = self.pn_filter -353 && filter.skip_pn(pn, rng) -354 { -355 pn = self.next_packet_number; -356 self.next_packet_number += 1; -357 } -358 pn -359 } -360 -361 pub(super) fn peek_tx_number(&mut self) -> u64 { -362 let pn = self.next_packet_number; -363 if let Some(ref filter) = self.pn_filter -364 && pn == filter.next_skipped_packet_number -365 { -366 return pn + 1; -367 } -368 pn -369 } -370 -371 /// Checks whether a skipped packet number was ACKed. -372 pub(super) fn check_ack(&self, range: std::ops::Range<u64>) -> Result<(), TransportError> { -373 if let Some(ref filter) = self.pn_filter -374 && filter -375 .prev_skipped_packet_number -376 .is_some_and(|pn| range.contains(&pn)) -377 { -378 return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked")); -379 } -380 Ok(()) -381 } -382 -383 /// Verifies sanity of an ECN block and returns whether congestion was encountered. -384 pub(super) fn detect_ecn( -385 &mut self, -386 newly_acked: u64, -387 ecn: frame::EcnCounts, -388 ) -> Result<bool, &'static str> { -389 let ect0_increase = ecn -390 .ect0 -391 .checked_sub(self.ecn_feedback.ect0) -392 .ok_or("peer ECT(0) count regression")?; -393 let ect1_increase = ecn -394 .ect1 -395 .checked_sub(self.ecn_feedback.ect1) -396 .ok_or("peer ECT(1) count regression")?; -397 let ce_increase = ecn -398 .ce -399 .checked_sub(self.ecn_feedback.ce) -400 .ok_or("peer CE count regression")?; -401 let total_increase = ect0_increase + ect1_increase + ce_increase; -402 if total_increase < newly_acked { -403 return Err("ECN bleaching"); -404 } -405 if (ect0_increase + ce_increase) < newly_acked || ect1_increase != 0 { -406 return Err("ECN corruption"); -407 } -408 // If total_increase > newly_acked (which happens when ACKs are lost), this is required by -409 // the draft so that long-term drift does not occur. If =, then the only question is whether -410 // to count CE packets as CE or ECT0. Recording them as CE is more consistent and keeps the -411 // congestion check obvious. -412 self.ecn_feedback = ecn; -413 Ok(ce_increase != 0) -414 } -415 -416 /// Stop tracking sent packet `number`, and return what we knew about it -417 pub(super) fn take(&mut self, number: u64) -> Option<SentPacket> { -418 let packet = self.sent_packets.remove(number)?; -419 if !packet.ack_eliciting && number > self.largest_ack_eliciting_sent { -420 self.unacked_non_ack_eliciting_tail = -421 self.unacked_non_ack_eliciting_tail.checked_sub(1).unwrap(); -422 } -423 Some(packet) -424 } -425 -426 /// May return a packet that should be forgotten -427 pub(super) fn sent(&mut self, number: u64, packet: SentPacket) -> Option<SentPacket> { -428 // Retain state for at most this many non-ACK-eliciting packets sent after the most recently -429 // sent ACK-eliciting packet. We're never guaranteed to receive an ACK for those, and we -430 // can't judge them as lost without an ACK, so to limit memory in applications which receive -431 // packets but don't send ACK-eliciting data for long periods use we must eventually start -432 // forgetting about them, although it might also be reasonable to just kill the connection -433 // due to weird peer behavior. -434 const MAX_UNACKED_NON_ACK_ELICTING_TAIL: u64 = 1_000; -435 -436 let mut forgotten = None; -437 if packet.ack_eliciting { -438 self.unacked_non_ack_eliciting_tail = 0; -439 self.largest_ack_eliciting_sent = number; -440 } else if self.unacked_non_ack_eliciting_tail > MAX_UNACKED_NON_ACK_ELICTING_TAIL { -441 let oldest_after_ack_eliciting = self -442 .sent_packets -443 .keys_range(( -444 Bound::Excluded(self.largest_ack_eliciting_sent), -445 Bound::Unbounded, -446 )) -447 .next() -448 .unwrap(); -449 // Per https://www.rfc-editor.org/rfc/rfc9000.html#name-frames-and-frame-types, -450 // non-ACK-eliciting packets must only contain PADDING, ACK, and CONNECTION_CLOSE -451 // frames, which require no special handling on ACK or loss beyond removal from -452 // in-flight counters if padded. -453 let packet = self -454 .sent_packets -455 .remove(oldest_after_ack_eliciting) -456 .unwrap(); -457 debug_assert!(!packet.ack_eliciting); -458 forgotten = Some(packet); -459 } else { -460 self.unacked_non_ack_eliciting_tail += 1; -461 } -462 -463 self.sent_packets.insert(number, packet); -464 forgotten -465 } -466 -467 /// Whether any congestion-controlled packets in this space are not yet acknowledged or lost -468 pub(super) fn has_in_flight(&self) -> bool { -469 // The number of non-congestion-controlled (i.e. size == 0) packets in flight at a time -470 // should be small, since otherwise congestion control wouldn't be effective. Therefore, -471 // this shouldn't need to visit many packets before finishing one way or another. -472 self.sent_packets.values().any(|x| x.size != 0) -473 } -474} -475 -476/// Represents one or more packets subject to retransmission -477#[derive(Debug, Clone)] -478pub(super) struct SentPacket { -479 /// [`PathData::generation`](super::PathData::generation) of the path on which this packet was sent -480 pub(super) path_generation: u64, -481 /// The time the packet was sent. -482 pub(super) time_sent: Instant, -483 /// The number of bytes sent in the packet, not including UDP or IP overhead, but including QUIC -484 /// framing overhead. Zero if this packet is not counted towards congestion control, i.e. not an -485 /// "in flight" packet. -486 pub(super) size: u16, -487 /// Whether an acknowledgement is expected directly in response to this packet. -488 pub(super) ack_eliciting: bool, -489 /// The largest packet number acknowledged by this packet -490 pub(super) largest_acked: FxHashMap<PathId, u64>, -491 /// Data which needs to be retransmitted in case the packet is lost. -492 /// The data is boxed to minimize `SentPacket` size for the typical case of -493 /// packets only containing ACKs and STREAM frames. -494 pub(super) retransmits: ThinRetransmits, -495 /// Metadata for stream frames in a packet -496 /// -497 /// The actual application data is stored with the stream state. -498 pub(super) stream_frames: frame::StreamMetaVec, -499} -500 -501/// Represents one or more packets that are deemed lost. -502#[derive(Debug)] -503pub(super) struct LostPacket { -504 /// The time the packet was sent. -505 pub(super) time_sent: Instant, -506} -507 -508/// Retransmittable data queue -509#[allow(unreachable_pub)] // fuzzing only -510#[derive(Debug, Default, Clone)] -511pub struct Retransmits { -512 pub(super) max_data: bool, -513 pub(super) max_stream_id: [bool; 2], -514 pub(super) streams_blocked: [bool; 2], -515 pub(super) reset_stream: Vec<(StreamId, VarInt)>, -516 pub(super) stop_sending: Vec<frame::StopSending>, -517 pub(super) max_stream_data: FxHashSet<StreamId>, -518 pub(super) crypto: VecDeque<frame::Crypto>, -519 pub(super) new_cids: PendingNewCids, -520 pub(super) retire_cids: Vec<(PathId, u64)>, -521 pub(super) ack_frequency: bool, -522 pub(super) handshake_done: bool, -523 pub(super) observed_addr: bool, -524 /// Whether we should inform the peer we will allow higher [`PathId`]s. -525 pub(super) max_path_id: bool, -526 /// Whether we should inform the peer that their max [`PathId`] is blocking our attempt to open -527 /// new paths. -528 // TODO(@divma): we need logic to prevent sending this more than once after being ack-d once -529 pub(super) paths_blocked: bool, -530 /// For each enqueued NEW_TOKEN frame, a copy of the path's remote address -531 /// -532 /// There are 2 reasons this is unusual: -533 /// -534 /// - If the path changes, NEW_TOKEN frames bound for the old path are not retransmitted on the -535 /// new path. That is why this field stores the remote address: so that ones for old paths -536 /// can be filtered out. -537 /// - If a token is lost, a new randomly generated token is re-transmitted, rather than the -538 /// original. This is so that if both transmissions are received, the client won't risk -539 /// sending the same token twice. That is why this field does _not_ store any actual token. -540 /// -541 /// It is true that a QUIC endpoint will only want to effectively have NEW_TOKEN frames -542 /// enqueued for its current path at a given point in time. Based on that, we could conceivably -543 /// change this from a vector to an `Option<(FourTuple, usize)>` or just a `usize` or -544 /// something. However, due to the architecture of noq, it is considerably simpler to not do -545 /// that; consider what such a change would mean for implementing `BitOrAssign` on Self. -546 pub(super) new_tokens: Vec<FourTuple>, -547 /// Paths which need to be abandoned -548 pub(super) path_abandon: BTreeMap<PathId, TransportErrorCode>, -549 /// If a [`frame::PathStatusAvailable`] and [`frame::PathStatusBackup`] need to be sent for a path -550 pub(super) path_status: BTreeSet<PathId>, -551 /// If a PATH_CIDS_BLOCKED frame needs to be sent for a path -552 pub(super) path_cids_blocked: BTreeSet<PathId>, -553 -554 // Nat traversal data -555 /// Addresses to report in `ADD_ADDRESS` frames -556 pub(super) add_address: BTreeSet<AddAddress>, -557 /// Address IDs to remove in `REMOVE_ADDRESS` frames -558 pub(super) remove_address: BTreeSet<RemoveAddress>, -559 /// Round and local addresses to advertise in `REACH_OUT` frames -560 pub(super) reach_out: PendingReachOutFrames, -561} -562 -563impl Retransmits { -564 pub(super) fn is_empty(&self, streams: &StreamsState) -> bool { -565 let Self { -566 max_data, -567 max_stream_id, -568 streams_blocked, -569 reset_stream, -570 stop_sending, -571 max_stream_data, -572 crypto, -573 new_cids, -574 retire_cids, -575 ack_frequency, -576 handshake_done, -577 observed_addr, -578 max_path_id, -579 paths_blocked, -580 new_tokens, -581 path_abandon, -582 path_status, -583 path_cids_blocked, -584 add_address, -585 remove_address, -586 reach_out, -587 } = &self; -588 !max_data -589 && !max_stream_id.iter().any(|x| *x) -590 && !streams_blocked.iter().any(|x| *x) -591 && reset_stream.is_empty() -592 && stop_sending.is_empty() -593 && max_stream_data -594 .iter() -595 .all(|&id| !streams.can_send_flow_control(id)) -596 && crypto.is_empty() -597 && new_cids.is_empty() -598 && retire_cids.is_empty() -599 && !ack_frequency -600 && !handshake_done -601 && !observed_addr -602 && !max_path_id -603 && !paths_blocked -604 && new_tokens.is_empty() -605 && path_abandon.is_empty() -606 && path_status.is_empty() -607 && path_cids_blocked.is_empty() -608 && add_address.is_empty() -609 && remove_address.is_empty() -610 && reach_out.is_empty() -611 } -612} -613 -614impl ::std::ops::BitOrAssign for Retransmits { -615 fn bitor_assign(&mut self, rhs: Self) { -616 let Self { -617 max_data, -618 max_stream_id, -619 streams_blocked, -620 reset_stream, -621 stop_sending, -622 max_stream_data, -623 crypto, -624 new_cids, -625 retire_cids, -626 ack_frequency, -627 handshake_done, -628 observed_addr, -629 max_path_id, -630 paths_blocked, -631 new_tokens, -632 mut path_abandon, -633 mut path_status, -634 mut path_cids_blocked, -635 add_address, -636 remove_address, -637 mut reach_out, -638 } = rhs; -639 -640 // We reduce in-stream head-of-line blocking by queueing retransmits before other data for -641 // STREAM and CRYPTO frames. -642 self.max_data |= max_data; -643 for dir in Dir::iter() { -644 self.max_stream_id[dir as usize] |= max_stream_id[dir as usize]; -645 self.streams_blocked[dir as usize] |= streams_blocked[dir as usize]; -646 } -647 self.reset_stream.extend_from_slice(&reset_stream); -648 self.stop_sending.extend_from_slice(&stop_sending); -649 self.max_stream_data.extend(&max_stream_data); -650 for crypto in crypto.into_iter().rev() { -651 self.crypto.push_front(crypto); -652 } -653 self.new_cids.extend(&new_cids); -654 self.retire_cids.extend(retire_cids); -655 self.ack_frequency |= ack_frequency; -656 self.handshake_done |= handshake_done; -657 self.observed_addr |= observed_addr; -658 self.max_path_id |= max_path_id; -659 self.paths_blocked |= paths_blocked; -660 self.new_tokens.extend_from_slice(&new_tokens); -661 self.path_abandon.append(&mut path_abandon); -662 self.path_status.append(&mut path_status); -663 self.path_cids_blocked.append(&mut path_cids_blocked); -664 self.add_address.extend(add_address.iter().copied()); -665 self.remove_address.extend(remove_address.iter().copied()); -666 self.reach_out.append(&mut reach_out); -667 } -668} -669 -670impl ::std::ops::BitOrAssign<ThinRetransmits> for Retransmits { -671 fn bitor_assign(&mut self, rhs: ThinRetransmits) { -672 let ThinRetransmits { retransmits } = rhs; -673 if let Some(retransmits) = retransmits { -674 self.bitor_assign(*retransmits) -675 } -676 } -677} -678 -679impl ::std::iter::FromIterator<Self> for Retransmits { -680 fn from_iter<T>(iter: T) -> Self -681 where -682 T: IntoIterator<Item = Self>, -683 { -684 let mut result = Self::default(); -685 for packet in iter { -686 result |= packet; -687 } -688 result -689 } -690} -691 -692/// The queue of new CIDs to be transmitted to the peer. -693/// -694/// This queue is always sorted, so that popping off the last item is always the lowest -695/// sequence number of the lowest path ID. Which is the CID you want to be issued next. -696/// -697/// This is but a newtype over a `Vec` to enforce the sorted invariant. -698#[derive(Clone, Debug, Default)] -699pub(super) struct PendingNewCids { -700 /// The CIDs themselves. -701 cids: Vec<IssuedCid>, -702 /// Whether [`Self::cids`] is sorted or not. -703 sorted: bool, -704} -705 -706impl PendingNewCids { -707 /// Inserts an issued CID into the queue. -708 pub(super) fn push(&mut self, cid: IssuedCid) { -709 self.cids.push(cid); -710 self.sorted = false; -711 } -712 -713 /// Pops the next issued CID to transmit from the queue. -714 pub(super) fn pop(&mut self) -> Option<IssuedCid> { -715 if !std::mem::replace(&mut self.sorted, true) { -716 self.cids -717 .sort_by_key(|cid| cmp::Reverse((cid.path_id, cid.sequence))); -718 } -719 self.cids.pop() -720 } -721 -722 pub(super) fn is_empty(&self) -> bool { -723 self.cids.is_empty() -724 } -725 -726 pub(super) fn extend(&mut self, other: &Self) { -727 self.cids.extend(&other.cids); -728 self.sorted = false; -729 } -730 -731 pub(super) fn retain<F>(&mut self, f: F) -732 where -733 F: FnMut(&IssuedCid) -> bool, -734 { -735 self.cids.retain(f); -736 } +149 let space_specific = self.number_spaces.get(&path_id).is_some_and(|s| { +150 s.pending_ping || s.pending_immediate_ack || !s.pending_path_responses.is_empty() +151 }); +152 let other = !self.pending.is_empty(streams); +153 SendableFrames { +154 acks, +155 close: false, +156 space_specific, +157 other, +158 } +159 } +160} +161 +162impl Index<SpaceId> for [PacketSpace; 3] { +163 type Output = PacketSpace; +164 fn index(&self, space: SpaceId) -> &PacketSpace { +165 &self.as_ref()[space as usize] +166 } +167} +168 +169impl IndexMut<SpaceId> for [PacketSpace; 3] { +170 fn index_mut(&mut self, space: SpaceId) -> &mut PacketSpace { +171 &mut self.as_mut()[space as usize] +172 } +173} +174 +175/// The three QUIC packet number space kinds +176/// +177/// Unlike [`SpaceId`], this always has exactly three variants — it represents the +178/// encryption level / space kind, not a specific packet number space identity. +179#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +180pub(crate) enum SpaceKind { +181 /// Initial packets (client and server). +182 Initial = 0, +183 /// Handshake packets. +184 Handshake = 1, +185 /// Data (1-RTT and 0-RTT) +186 Data = 2, +187} +188 +189impl SpaceKind { +190 /// Returns the encryption level for this space kind. +191 pub(crate) fn encryption_level(self) -> super::EncryptionLevel { +192 match self { +193 Self::Initial => super::EncryptionLevel::Initial, +194 Self::Handshake => super::EncryptionLevel::Handshake, +195 Self::Data => super::EncryptionLevel::OneRtt, +196 } +197 } +198} +199 +200impl Index<SpaceKind> for [PacketSpace; 3] { +201 type Output = PacketSpace; +202 fn index(&self, space: SpaceKind) -> &PacketSpace { +203 &self.as_ref()[space as usize] +204 } +205} +206 +207impl IndexMut<SpaceKind> for [PacketSpace; 3] { +208 fn index_mut(&mut self, space: SpaceKind) -> &mut PacketSpace { +209 &mut self.as_mut()[space as usize] +210 } +211} +212 +213/// The state of a single packet number space. +214/// +215/// In RFC9000 there are 3 packet number spaces: Initial, Handshake and Data. In QUIC +216/// Multipath there are multiple packet number spaces for Data, each identified by a +217/// [`PathId`]. +218/// +219/// This contains the state for a packet number space which is not specific to the 4-tuple +220/// this space is currently using. The 4-tuple specific state, like congestion controller, +221/// pacing, ECN, MTU etc, is stored in [`PathData`]. +222/// +223/// Note that the `Initial`, `Handshake` and `Data(PathId(0))` space all share the same +224/// [`PathData`]. +225/// +226/// You should access this via [`PacketSpace::for_path`]. +227/// +228/// [`PathData`]: super::paths::PathData +229pub(super) struct PacketNumberSpace { +230 /// Whether the path has already been considered opened from an application perspective. +231 /// +232 /// This means, for paths other than the original [`PathId::ZERO`], a first path +233 /// challenge has been responded to, regardless of the initial validation status of the +234 /// path. This state is irreversible, since it's not affected by the path being closed. +235 /// +236 /// Sending a PATH_CHALLENGE and receiving a valid response before the application is +237 /// informed of the path, is a way to ensure the path is usable before it is +238 /// reported. This is not required by the spec, and in the future might be changed for +239 /// simply requiring a first ack'd packet. +240 pub(super) open_status: OpenStatus, +241 +242 /// Highest received packet number, if any +243 pub(super) largest_received_packet_number: Option<u64>, +244 /// The packet number of the next packet that will be sent, if any. In the Data space, the +245 /// packet number stored here is sometimes skipped by [`PacketNumberFilter`] logic. +246 pub(super) next_packet_number: u64, +247 /// The largest packet number the remote peer acknowledged in an ACK frame. +248 pub(super) largest_acked_packet_pn: Option<u64>, +249 pub(super) largest_acked_packet_send_time: Instant, +250 /// The highest-numbered ACK-eliciting packet we've sent +251 pub(super) largest_ack_eliciting_sent: u64, +252 /// Number of packets in `sent_packets` with numbers above `largest_ack_eliciting_sent` +253 pub(super) unacked_non_ack_eliciting_tail: u64, +254 /// Transmitted but not acked +255 // We use a BTreeMap here so we can efficiently query by range on ACK and for loss detection +256 pub(super) sent_packets: SortedIndexBuffer<SentPacket>, +257 /// Packets that were deemed lost +258 // Older packets are regularly removed in `Connection::drain_lost_packets`. +259 pub(super) lost_packets: SortedIndexBuffer<LostPacket>, +260 /// Number of explicit congestion notification codepoints seen on incoming packets +261 pub(super) ecn_counters: frame::EcnCounts, +262 /// Recent ECN counters sent by the peer in ACK frames +263 /// +264 /// Updated (and inspected) whenever we receive an ACK with a new highest acked packet +265 /// number. Stored per-space to simplify verification, which would otherwise have difficulty +266 /// distinguishing between ECN bleaching and counts having been updated by a near-simultaneous +267 /// ACK already processed in another space. +268 pub(super) ecn_feedback: frame::EcnCounts, +269 /// A PING frame needs to be sent on this path. +270 pub(super) pending_ping: bool, +271 /// Packet numbers to acknowledge. +272 pub(super) pending_acks: PendingAcks, +273 /// An IMMEDIATE_ACK (draft-ietf-quic-ack-frequency) frame needs to be sent on this path. +274 pub(super) pending_immediate_ack: bool, +275 /// Responses to path challenges that need to be sent, on and off-path. +276 /// +277 /// Responses are only tied to the 4-tuple they were received on, not to a specific path +278 /// generation. Whether the response is on or off-path only depends on the path's +279 /// 4-tuple at sending time. +280 pub(super) pending_path_responses: PathResponses, +281 /// Packet deduplicator +282 pub(super) dedup: Dedup, +283 +284 // +285 // Loss Detection +286 // +287 /// The time the most recently sent retransmittable packet was sent. +288 pub(super) time_of_last_ack_eliciting_packet: Option<Instant>, +289 /// Earliest time when we might declare a packet lost. +290 /// +291 /// The time at which the earliest sent packet in this space will be considered lost +292 /// based on exceeding the reordering window in time. Only set for packets numbered +293 /// prior to a packet that has been acknowledged. +294 pub(super) loss_time: Option<Instant>, +295 /// Number of tail loss probes to send +296 pub(super) loss_probes: u32, +297 +298 /// Packet numbers to skip, only used in the data package space. +299 pn_filter: Option<PacketNumberFilter>, +300} +301 +302impl PacketNumberSpace { +303 pub(super) fn new(now: Instant, space: SpaceId, rng: &mut (impl CryptoRng + ?Sized)) -> Self { +304 let pn_filter = match space { +305 SpaceId::Initial | SpaceId::Handshake => None, +306 SpaceId::Data => Some(PacketNumberFilter::new(rng)), +307 }; +308 Self { +309 open_status: OpenStatus::default(), +310 largest_received_packet_number: None, +311 next_packet_number: 0, +312 largest_acked_packet_pn: None, +313 largest_acked_packet_send_time: now, +314 largest_ack_eliciting_sent: 0, +315 unacked_non_ack_eliciting_tail: 0, +316 sent_packets: SortedIndexBuffer::new(), +317 lost_packets: SortedIndexBuffer::new(), +318 ecn_counters: frame::EcnCounts::ZERO, +319 ecn_feedback: frame::EcnCounts::ZERO, +320 pending_ping: false, +321 pending_acks: PendingAcks::new(), +322 pending_immediate_ack: false, +323 pending_path_responses: PathResponses::default(), +324 dedup: Default::default(), +325 time_of_last_ack_eliciting_packet: None, +326 loss_time: None, +327 loss_probes: 0, +328 pn_filter, +329 } +330 } +331 +332 #[cfg(test)] +333 fn new_deterministic(now: Instant, space: SpaceId) -> Self { +334 let pn_filter = match space { +335 SpaceId::Initial | SpaceId::Handshake => None, +336 SpaceId::Data => Some(PacketNumberFilter::disabled()), +337 }; +338 Self { +339 open_status: OpenStatus::default(), +340 largest_received_packet_number: None, +341 next_packet_number: 0, +342 largest_acked_packet_pn: None, +343 largest_acked_packet_send_time: now, +344 largest_ack_eliciting_sent: 0, +345 unacked_non_ack_eliciting_tail: 0, +346 sent_packets: SortedIndexBuffer::new(), +347 lost_packets: SortedIndexBuffer::new(), +348 ecn_counters: frame::EcnCounts::ZERO, +349 ecn_feedback: frame::EcnCounts::ZERO, +350 pending_ping: false, +351 pending_acks: PendingAcks::new(), +352 pending_immediate_ack: false, +353 pending_path_responses: PathResponses::default(), +354 dedup: Default::default(), +355 time_of_last_ack_eliciting_packet: None, +356 loss_time: None, +357 loss_probes: 0, +358 pn_filter, +359 } +360 } +361 +362 /// Get the next outgoing packet number in this space +363 /// +364 /// In the Data space, the connection's [`PacketNumberFilter`] must be used rather than calling +365 /// this directly. +366 pub(super) fn get_tx_number(&mut self, rng: &mut (impl CryptoRng + ?Sized)) -> u64 { +367 // TODO: Handle packet number overflow gracefully +368 assert!(self.next_packet_number < 2u64.pow(62)); +369 let mut pn = self.next_packet_number; +370 self.next_packet_number += 1; +371 +372 // Skip this number if the filter says so, only enabled in the data space +373 if let Some(ref mut filter) = self.pn_filter +374 && filter.skip_pn(pn, rng) +375 { +376 pn = self.next_packet_number; +377 self.next_packet_number += 1; +378 } +379 pn +380 } +381 +382 pub(super) fn peek_tx_number(&mut self) -> u64 { +383 let pn = self.next_packet_number; +384 if let Some(ref filter) = self.pn_filter +385 && pn == filter.next_skipped_packet_number +386 { +387 return pn + 1; +388 } +389 pn +390 } +391 +392 /// Checks whether a skipped packet number was ACKed. +393 pub(super) fn check_ack(&self, range: std::ops::Range<u64>) -> Result<(), TransportError> { +394 if let Some(ref filter) = self.pn_filter +395 && filter +396 .prev_skipped_packet_number +397 .is_some_and(|pn| range.contains(&pn)) +398 { +399 return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked")); +400 } +401 Ok(()) +402 } +403 +404 /// Verifies sanity of an ECN block and returns whether congestion was encountered. +405 pub(super) fn detect_ecn( +406 &mut self, +407 newly_acked: u64, +408 ecn: frame::EcnCounts, +409 ) -> Result<bool, &'static str> { +410 let ect0_increase = ecn +411 .ect0 +412 .checked_sub(self.ecn_feedback.ect0) +413 .ok_or("peer ECT(0) count regression")?; +414 let ect1_increase = ecn +415 .ect1 +416 .checked_sub(self.ecn_feedback.ect1) +417 .ok_or("peer ECT(1) count regression")?; +418 let ce_increase = ecn +419 .ce +420 .checked_sub(self.ecn_feedback.ce) +421 .ok_or("peer CE count regression")?; +422 let total_increase = ect0_increase + ect1_increase + ce_increase; +423 if total_increase < newly_acked { +424 return Err("ECN bleaching"); +425 } +426 if (ect0_increase + ce_increase) < newly_acked || ect1_increase != 0 { +427 return Err("ECN corruption"); +428 } +429 // If total_increase > newly_acked (which happens when ACKs are lost), this is required by +430 // the draft so that long-term drift does not occur. If =, then the only question is whether +431 // to count CE packets as CE or ECT0. Recording them as CE is more consistent and keeps the +432 // congestion check obvious. +433 self.ecn_feedback = ecn; +434 Ok(ce_increase != 0) +435 } +436 +437 /// Stop tracking sent packet `number`, and return what we knew about it +438 pub(super) fn take(&mut self, number: u64) -> Option<SentPacket> { +439 let packet = self.sent_packets.remove(number)?; +440 if !packet.ack_eliciting && number > self.largest_ack_eliciting_sent { +441 self.unacked_non_ack_eliciting_tail = +442 self.unacked_non_ack_eliciting_tail.checked_sub(1).unwrap(); +443 } +444 Some(packet) +445 } +446 +447 /// May return a packet that should be forgotten +448 pub(super) fn sent(&mut self, number: u64, packet: SentPacket) -> Option<SentPacket> { +449 // Retain state for at most this many non-ACK-eliciting packets sent after the most recently +450 // sent ACK-eliciting packet. We're never guaranteed to receive an ACK for those, and we +451 // can't judge them as lost without an ACK, so to limit memory in applications which receive +452 // packets but don't send ACK-eliciting data for long periods use we must eventually start +453 // forgetting about them, although it might also be reasonable to just kill the connection +454 // due to weird peer behavior. +455 const MAX_UNACKED_NON_ACK_ELICTING_TAIL: u64 = 1_000; +456 +457 let mut forgotten = None; +458 if packet.ack_eliciting { +459 self.unacked_non_ack_eliciting_tail = 0; +460 self.largest_ack_eliciting_sent = number; +461 } else if self.unacked_non_ack_eliciting_tail > MAX_UNACKED_NON_ACK_ELICTING_TAIL { +462 let oldest_after_ack_eliciting = self +463 .sent_packets +464 .keys_range(( +465 Bound::Excluded(self.largest_ack_eliciting_sent), +466 Bound::Unbounded, +467 )) +468 .next() +469 .unwrap(); +470 // Per https://www.rfc-editor.org/rfc/rfc9000.html#name-frames-and-frame-types, +471 // non-ACK-eliciting packets must only contain PADDING, ACK, and CONNECTION_CLOSE +472 // frames, which require no special handling on ACK or loss beyond removal from +473 // in-flight counters if padded. +474 let packet = self +475 .sent_packets +476 .remove(oldest_after_ack_eliciting) +477 .unwrap(); +478 debug_assert!(!packet.ack_eliciting); +479 forgotten = Some(packet); +480 } else { +481 self.unacked_non_ack_eliciting_tail += 1; +482 } +483 +484 self.sent_packets.insert(number, packet); +485 forgotten +486 } +487 +488 /// Whether any congestion-controlled packets in this space are not yet acknowledged or lost +489 pub(super) fn has_in_flight(&self) -> bool { +490 // The number of non-congestion-controlled (i.e. size == 0) packets in flight at a time +491 // should be small, since otherwise congestion control wouldn't be effective. Therefore, +492 // this shouldn't need to visit many packets before finishing one way or another. +493 self.sent_packets.values().any(|x| x.size != 0) +494 } +495} +496 +497#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +498pub(super) enum OpenStatus { +499 /// A first packet has not been sent using this [`PathId`]. +500 #[default] +501 Pending, +502 /// The first packet has been sent using this [`PathId`]. However, it is not yet deemed good +503 /// enough to be reported to the application. +504 Sent, +505 /// The application has been informed of this path. +506 Informed, +507} +508 +509/// Represents one or more packets subject to retransmission +510#[derive(Debug, Clone)] +511pub(super) struct SentPacket { +512 /// [`PathData::generation`](super::PathData::generation) of the path on which this packet was sent +513 pub(super) path_generation: u64, +514 /// The time the packet was sent. +515 pub(super) time_sent: Instant, +516 /// The number of bytes sent in the packet, not including UDP or IP overhead, but including QUIC +517 /// framing overhead. Zero if this packet is not counted towards congestion control, i.e. not an +518 /// "in flight" packet. +519 pub(super) size: u16, +520 /// Whether an acknowledgement is expected directly in response to this packet. +521 pub(super) ack_eliciting: bool, +522 /// The largest packet number acknowledged by this packet +523 pub(super) largest_acked: FxHashMap<PathId, u64>, +524 /// Data which needs to be retransmitted in case the packet is lost. +525 /// The data is boxed to minimize `SentPacket` size for the typical case of +526 /// packets only containing ACKs and STREAM frames. +527 pub(super) retransmits: ThinRetransmits, +528 /// Metadata for stream frames in a packet +529 /// +530 /// The actual application data is stored with the stream state. +531 pub(super) stream_frames: frame::StreamMetaVec, +532} +533 +534/// Represents one or more packets that are deemed lost. +535#[derive(Debug)] +536pub(super) struct LostPacket { +537 /// The time the packet was sent. +538 pub(super) time_sent: Instant, +539} +540 +541/// Retransmittable data queue +542#[allow(unreachable_pub)] // fuzzing only +543#[derive(Debug, Default, Clone)] +544pub struct Retransmits { +545 pub(super) max_data: bool, +546 pub(super) max_stream_id: [bool; 2], +547 pub(super) streams_blocked: [bool; 2], +548 pub(super) reset_stream: Vec<(StreamId, VarInt)>, +549 pub(super) stop_sending: Vec<frame::StopSending>, +550 pub(super) max_stream_data: FxHashSet<StreamId>, +551 pub(super) crypto: VecDeque<frame::Crypto>, +552 pub(super) new_cids: PendingNewCids, +553 pub(super) retire_cids: Vec<(PathId, u64)>, +554 pub(super) ack_frequency: bool, +555 pub(super) handshake_done: bool, +556 pub(super) observed_addr: bool, +557 /// Whether we should inform the peer we will allow higher [`PathId`]s. +558 pub(super) max_path_id: bool, +559 /// Whether we should inform the peer that their max [`PathId`] is blocking our attempt to open +560 /// new paths. +561 // TODO(@divma): we need logic to prevent sending this more than once after being ack-d once +562 pub(super) paths_blocked: bool, +563 /// For each enqueued NEW_TOKEN frame, a copy of the path's remote address +564 /// +565 /// There are 2 reasons this is unusual: +566 /// +567 /// - If the path changes, NEW_TOKEN frames bound for the old path are not retransmitted on the +568 /// new path. That is why this field stores the remote address: so that ones for old paths +569 /// can be filtered out. +570 /// - If a token is lost, a new randomly generated token is re-transmitted, rather than the +571 /// original. This is so that if both transmissions are received, the client won't risk +572 /// sending the same token twice. That is why this field does _not_ store any actual token. +573 /// +574 /// It is true that a QUIC endpoint will only want to effectively have NEW_TOKEN frames +575 /// enqueued for its current path at a given point in time. Based on that, we could conceivably +576 /// change this from a vector to an `Option<(FourTuple, usize)>` or just a `usize` or +577 /// something. However, due to the architecture of noq, it is considerably simpler to not do +578 /// that; consider what such a change would mean for implementing `BitOrAssign` on Self. +579 pub(super) new_tokens: Vec<FourTuple>, +580 /// Paths which need to be abandoned +581 pub(super) path_abandon: BTreeMap<PathId, TransportErrorCode>, +582 /// If a [`frame::PathStatusAvailable`] and [`frame::PathStatusBackup`] need to be sent for a path +583 pub(super) path_status: BTreeSet<PathId>, +584 /// If a PATH_CIDS_BLOCKED frame needs to be sent for a path +585 pub(super) path_cids_blocked: BTreeSet<PathId>, +586 +587 // Nat traversal data +588 /// Addresses to report in `ADD_ADDRESS` frames +589 pub(super) add_address: BTreeSet<AddAddress>, +590 /// Address IDs to remove in `REMOVE_ADDRESS` frames +591 pub(super) remove_address: BTreeSet<RemoveAddress>, +592 /// Round and local addresses to advertise in `REACH_OUT` frames +593 pub(super) reach_out: PendingReachOutFrames, +594} +595 +596impl Retransmits { +597 pub(super) fn is_empty(&self, streams: &StreamsState) -> bool { +598 let Self { +599 max_data, +600 max_stream_id, +601 streams_blocked, +602 reset_stream, +603 stop_sending, +604 max_stream_data, +605 crypto, +606 new_cids, +607 retire_cids, +608 ack_frequency, +609 handshake_done, +610 observed_addr, +611 max_path_id, +612 paths_blocked, +613 new_tokens, +614 path_abandon, +615 path_status, +616 path_cids_blocked, +617 add_address, +618 remove_address, +619 reach_out, +620 } = &self; +621 !max_data +622 && !max_stream_id.iter().any(|x| *x) +623 && !streams_blocked.iter().any(|x| *x) +624 && reset_stream.is_empty() +625 && stop_sending.is_empty() +626 && max_stream_data +627 .iter() +628 .all(|&id| !streams.can_send_flow_control(id)) +629 && crypto.is_empty() +630 && new_cids.is_empty() +631 && retire_cids.is_empty() +632 && !ack_frequency +633 && !handshake_done +634 && !observed_addr +635 && !max_path_id +636 && !paths_blocked +637 && new_tokens.is_empty() +638 && path_abandon.is_empty() +639 && path_status.is_empty() +640 && path_cids_blocked.is_empty() +641 && add_address.is_empty() +642 && remove_address.is_empty() +643 && reach_out.is_empty() +644 } +645} +646 +647impl ::std::ops::BitOrAssign for Retransmits { +648 fn bitor_assign(&mut self, rhs: Self) { +649 let Self { +650 max_data, +651 max_stream_id, +652 streams_blocked, +653 reset_stream, +654 stop_sending, +655 max_stream_data, +656 crypto, +657 new_cids, +658 retire_cids, +659 ack_frequency, +660 handshake_done, +661 observed_addr, +662 max_path_id, +663 paths_blocked, +664 new_tokens, +665 mut path_abandon, +666 mut path_status, +667 mut path_cids_blocked, +668 add_address, +669 remove_address, +670 mut reach_out, +671 } = rhs; +672 +673 // We reduce in-stream head-of-line blocking by queueing retransmits before other data for +674 // STREAM and CRYPTO frames. +675 self.max_data |= max_data; +676 for dir in Dir::iter() { +677 self.max_stream_id[dir as usize] |= max_stream_id[dir as usize]; +678 self.streams_blocked[dir as usize] |= streams_blocked[dir as usize]; +679 } +680 self.reset_stream.extend_from_slice(&reset_stream); +681 self.stop_sending.extend_from_slice(&stop_sending); +682 self.max_stream_data.extend(&max_stream_data); +683 for crypto in crypto.into_iter().rev() { +684 self.crypto.push_front(crypto); +685 } +686 self.new_cids.extend(&new_cids); +687 self.retire_cids.extend(retire_cids); +688 self.ack_frequency |= ack_frequency; +689 self.handshake_done |= handshake_done; +690 self.observed_addr |= observed_addr; +691 self.max_path_id |= max_path_id; +692 self.paths_blocked |= paths_blocked; +693 self.new_tokens.extend_from_slice(&new_tokens); +694 self.path_abandon.append(&mut path_abandon); +695 self.path_status.append(&mut path_status); +696 self.path_cids_blocked.append(&mut path_cids_blocked); +697 self.add_address.extend(add_address.iter().copied()); +698 self.remove_address.extend(remove_address.iter().copied()); +699 self.reach_out.append(&mut reach_out); +700 } +701} +702 +703impl ::std::ops::BitOrAssign<ThinRetransmits> for Retransmits { +704 fn bitor_assign(&mut self, rhs: ThinRetransmits) { +705 let ThinRetransmits { retransmits } = rhs; +706 if let Some(retransmits) = retransmits { +707 self.bitor_assign(*retransmits) +708 } +709 } +710} +711 +712impl ::std::iter::FromIterator<Self> for Retransmits { +713 fn from_iter<T>(iter: T) -> Self +714 where +715 T: IntoIterator<Item = Self>, +716 { +717 let mut result = Self::default(); +718 for packet in iter { +719 result |= packet; +720 } +721 result +722 } +723} +724 +725/// The queue of new CIDs to be transmitted to the peer. +726/// +727/// This queue is always sorted, so that popping off the last item is always the lowest +728/// sequence number of the lowest path ID. Which is the CID you want to be issued next. +729/// +730/// This is but a newtype over a `Vec` to enforce the sorted invariant. +731#[derive(Clone, Debug, Default)] +732pub(super) struct PendingNewCids { +733 /// The CIDs themselves. +734 cids: Vec<IssuedCid>, +735 /// Whether [`Self::cids`] is sorted or not. +736 sorted: bool, 737} 738 -739/// Logically a Vec of REACH_OUT frames queued for transmit. -740/// -741/// This keeps track of the highest round ID and automatically drops frames with a lower -742/// round ID. -743/// -744/// The API is directly modelled on [`Vec`]. -745#[derive(Debug, Default, Clone)] -746pub(crate) struct PendingReachOutFrames { -747 /// The round ID of the REACH_OUT frames currently pending. -748 round: VarInt, -749 /// The REACH_OUT frames, always all having the same round ID. -750 frames: Vec<frame::ReachOut>, -751} -752 -753impl PendingReachOutFrames { -754 pub(crate) fn len(&self) -> usize { -755 self.frames.len() -756 } -757 -758 pub(crate) fn is_empty(&self) -> bool { -759 self.frames.is_empty() -760 } -761 -762 pub(crate) fn push(&mut self, frame: frame::ReachOut) { -763 if frame.round < self.round { -764 return; -765 } else if frame.round > self.round { -766 self.round = frame.round; -767 self.frames.clear(); -768 } -769 self.frames.push(frame); -770 } +739impl PendingNewCids { +740 /// Inserts an issued CID into the queue. +741 pub(super) fn push(&mut self, cid: IssuedCid) { +742 self.cids.push(cid); +743 self.sorted = false; +744 } +745 +746 /// Pops the next issued CID to transmit from the queue. +747 pub(super) fn pop(&mut self) -> Option<IssuedCid> { +748 if !std::mem::replace(&mut self.sorted, true) { +749 self.cids +750 .sort_by_key(|cid| cmp::Reverse((cid.path_id, cid.sequence))); +751 } +752 self.cids.pop() +753 } +754 +755 pub(super) fn is_empty(&self) -> bool { +756 self.cids.is_empty() +757 } +758 +759 pub(super) fn extend(&mut self, other: &Self) { +760 self.cids.extend(&other.cids); +761 self.sorted = false; +762 } +763 +764 pub(super) fn retain<F>(&mut self, f: F) +765 where +766 F: FnMut(&IssuedCid) -> bool, +767 { +768 self.cids.retain(f); +769 } +770} 771 -772 pub(crate) fn append(&mut self, other: &mut Self) { -773 if other.round < self.round { -774 other.frames.clear(); -775 return; -776 } else if other.round > self.round { -777 self.round = other.round; -778 self.frames.clear(); -779 } -780 self.frames.append(&mut other.frames); -781 } -782 -783 pub(crate) fn pop_if( -784 &mut self, -785 predicate: impl FnOnce(&mut frame::ReachOut) -> bool, -786 ) -> Option<frame::ReachOut> { -787 self.frames.pop_if(predicate) -788 } -789} +772/// Logically a Vec of REACH_OUT frames queued for transmit. +773/// +774/// This keeps track of the highest round ID and automatically drops frames with a lower +775/// round ID. +776/// +777/// The API is directly modelled on [`Vec`]. +778#[derive(Debug, Default, Clone)] +779pub(crate) struct PendingReachOutFrames { +780 /// The round ID of the REACH_OUT frames currently pending. +781 round: VarInt, +782 /// The REACH_OUT frames, always all having the same round ID. +783 frames: Vec<frame::ReachOut>, +784} +785 +786impl PendingReachOutFrames { +787 pub(crate) fn len(&self) -> usize { +788 self.frames.len() +789 } 790 -791impl FromIterator<frame::ReachOut> for PendingReachOutFrames { -792 fn from_iter<T: IntoIterator<Item = frame::ReachOut>>(iter: T) -> Self { -793 let iter = iter.into_iter(); -794 let size_hint = iter.size_hint(); -795 let mut this = Self { -796 round: Default::default(), -797 frames: Vec::with_capacity(size_hint.1.unwrap_or(size_hint.0)), -798 }; -799 for frame in iter { -800 this.push(frame); +791 pub(crate) fn is_empty(&self) -> bool { +792 self.frames.is_empty() +793 } +794 +795 pub(crate) fn push(&mut self, frame: frame::ReachOut) { +796 if frame.round < self.round { +797 return; +798 } else if frame.round > self.round { +799 self.round = frame.round; +800 self.frames.clear(); 801 } -802 this +802 self.frames.push(frame); 803 } -804} -805 -806/// A variant of `Retransmits` which only allocates storage when required -807#[derive(Debug, Default, Clone)] -808pub(super) struct ThinRetransmits { -809 retransmits: Option<Box<Retransmits>>, -810} -811 -812impl ThinRetransmits { -813 /// Returns `true` if no retransmits are necessary -814 pub(super) fn is_empty(&self, streams: &StreamsState) -> bool { -815 match &self.retransmits { -816 Some(retransmits) => retransmits.is_empty(streams), -817 None => true, -818 } -819 } -820 -821 /// Returns a reference to the retransmits stored in this box -822 pub(super) fn get(&self) -> Option<&Retransmits> { -823 self.retransmits.as_deref() -824 } -825 -826 /// Returns a mutable reference to the retransmits stored in this box -827 pub(super) fn get_mut(&mut self) -> Option<&mut Retransmits> { -828 self.retransmits.as_deref_mut() -829 } -830 -831 /// Returns a mutable reference to the stored retransmits -832 /// -833 /// This function will allocate a backing storage if required. -834 pub(super) fn get_or_create(&mut self) -> &mut Retransmits { -835 if self.retransmits.is_none() { -836 self.retransmits = Some(Box::default()); -837 } -838 self.retransmits.as_deref_mut().unwrap() -839 } -840} -841 -842/// RFC4303-style sliding window packet number deduplicator. -843/// -844/// A contiguous bitfield, where each bit corresponds to a packet number and the rightmost bit is -845/// always set. A set bit represents a packet that has been successfully authenticated. Bits left of -846/// the window are assumed to be set. -847/// -848/// ```text -849/// ...xxxxxxxxx 1 0 -850/// ^ ^ ^ -851/// window highest next -852/// ``` -853#[derive(Debug, Default)] -854pub(super) struct Dedup { -855 window: Window, -856 /// Lowest packet number higher than all yet authenticated. -857 next: u64, -858} -859 -860/// Inner bitfield type. -861/// -862/// Because QUIC never reuses packet numbers, this only needs to be large enough to deal with -863/// packets that are reordered but still delivered in a timely manner. -864type Window = u128; -865 -866/// Number of packets tracked by `Dedup`. -867const WINDOW_SIZE: u64 = 1 + mem::size_of::<Window>() as u64 * 8; -868 -869impl Dedup { -870 /// Construct an empty window positioned at the start. -871 #[cfg(test)] -872 pub(super) fn new() -> Self { -873 Self { window: 0, next: 0 } -874 } -875 -876 /// Highest packet number authenticated. -877 fn highest(&self) -> u64 { -878 self.next - 1 -879 } -880 -881 /// Record a newly authenticated packet number. -882 /// -883 /// Returns whether the packet might be a duplicate. -884 pub(super) fn insert(&mut self, packet: u64) -> bool { -885 if let Some(diff) = packet.checked_sub(self.next) { -886 // Right of window -887 self.window = ((self.window << 1) | 1) -888 .checked_shl(cmp::min(diff, u64::from(u32::MAX)) as u32) -889 .unwrap_or(0); -890 self.next = packet + 1; -891 false -892 } else if self.highest() - packet < WINDOW_SIZE { -893 // Within window -894 if let Some(bit) = (self.highest() - packet).checked_sub(1) { -895 // < highest -896 let mask = 1 << bit; -897 let duplicate = self.window & mask != 0; -898 self.window |= mask; -899 duplicate -900 } else { -901 // == highest -902 true -903 } -904 } else { -905 // Left of window -906 true -907 } -908 } -909 -910 /// Returns the packet number of the smallest packet missing between the provided interval -911 /// -912 /// If there are no missing packets, returns `None` -913 fn smallest_missing_in_interval(&self, lower_bound: u64, upper_bound: u64) -> Option<u64> { -914 debug_assert!(lower_bound <= upper_bound); -915 debug_assert!(upper_bound <= self.highest()); -916 const BITFIELD_SIZE: u64 = (mem::size_of::<Window>() * 8) as u64; -917 -918 // Since we already know the packets at the boundaries have been received, we only need to -919 // check those in between them (this removes the necessity of extra logic to deal with the -920 // highest packet, which is stored outside the bitfield) -921 let lower_bound = lower_bound + 1; -922 let upper_bound = upper_bound.saturating_sub(1); -923 -924 // Note: the offsets are counted from the right -925 // The highest packet is not included in the bitfield, so we subtract 1 to account for that -926 let start_offset = (self.highest() - upper_bound).max(1) - 1; -927 if start_offset >= BITFIELD_SIZE { -928 // The start offset is outside of the window. All packets outside of the window are -929 // considered to be received. -930 return None; -931 } -932 -933 let end_offset_exclusive = self.highest().saturating_sub(lower_bound); -934 -935 // The range is clamped at the edge of the window, because any earlier packets are -936 // considered to be received -937 let range_len = end_offset_exclusive -938 .saturating_sub(start_offset) -939 .min(BITFIELD_SIZE); -940 if range_len == 0 { -941 return None; -942 } -943 -944 // Ensure the shift is within bounds (we already know start_offset < BITFIELD_SIZE, -945 // because of the early return) -946 let mask = if range_len == BITFIELD_SIZE { -947 u128::MAX -948 } else { -949 ((1u128 << range_len) - 1) << start_offset -950 }; -951 let gaps = !self.window & mask; -952 -953 let smallest_missing_offset = 128 - gaps.leading_zeros() as u64; -954 let smallest_missing_packet = self.highest() - smallest_missing_offset; -955 -956 if smallest_missing_packet <= upper_bound { -957 Some(smallest_missing_packet) -958 } else { -959 None -960 } -961 } -962 -963 /// Returns true if there are any missing packets between the provided interval -964 /// -965 /// The provided packet numbers must have been received before calling this function -966 fn missing_in_interval(&self, lower_bound: u64, upper_bound: u64) -> bool { -967 self.smallest_missing_in_interval(lower_bound, upper_bound) -968 .is_some() -969 } -970} -971 -972/// Indicates which data is available for sending. -973/// -974/// This applies to a particular space ID that was queried and all refers to on-path data. -975#[derive(Clone, Copy, PartialEq, Eq, Debug)] -976pub(super) struct SendableFrames { -977 /// Whether there are ACK frames to send, these are not ack-eliciting. -978 pub(super) acks: bool, -979 /// Whether there is a CONNECTION_CLOSE to send, this is not ack-eliciting. -980 pub(super) close: bool, -981 /// Whether there are any frames that must be sent on this specific space. -982 /// -983 /// A space here in the sense of a QUIC Multipath packet number space: `Initial`, -984 /// `Handshake` and all `Data(PathId)` spaces. -985 /// -986 /// These are ack-eliciting. Some frames are scheduled per path, e.g. PING, -987 /// IMMEDIATE_ACK, PATH_CHALLENGE or PATH_RESPONSE. -988 pub(super) space_specific: bool, -989 /// Whether there are any other frames to send, these are ack-eliciting. -990 pub(super) other: bool, -991} -992 -993impl SendableFrames { -994 /// Returns that no data is available for sending -995 pub(super) fn empty() -> Self { -996 Self { -997 acks: false, -998 close: false, -999 space_specific: false, -1000 other: false, -1001 } +804 +805 pub(crate) fn append(&mut self, other: &mut Self) { +806 if other.round < self.round { +807 other.frames.clear(); +808 return; +809 } else if other.round > self.round { +810 self.round = other.round; +811 self.frames.clear(); +812 } +813 self.frames.append(&mut other.frames); +814 } +815 +816 pub(crate) fn pop_if( +817 &mut self, +818 predicate: impl FnOnce(&mut frame::ReachOut) -> bool, +819 ) -> Option<frame::ReachOut> { +820 self.frames.pop_if(predicate) +821 } +822} +823 +824impl FromIterator<frame::ReachOut> for PendingReachOutFrames { +825 fn from_iter<T: IntoIterator<Item = frame::ReachOut>>(iter: T) -> Self { +826 let iter = iter.into_iter(); +827 let size_hint = iter.size_hint(); +828 let mut this = Self { +829 round: Default::default(), +830 frames: Vec::with_capacity(size_hint.1.unwrap_or(size_hint.0)), +831 }; +832 for frame in iter { +833 this.push(frame); +834 } +835 this +836 } +837} +838 +839/// A variant of `Retransmits` which only allocates storage when required +840#[derive(Debug, Default, Clone)] +841pub(super) struct ThinRetransmits { +842 retransmits: Option<Box<Retransmits>>, +843} +844 +845impl ThinRetransmits { +846 /// Returns `true` if no retransmits are necessary +847 pub(super) fn is_empty(&self, streams: &StreamsState) -> bool { +848 match &self.retransmits { +849 Some(retransmits) => retransmits.is_empty(streams), +850 None => true, +851 } +852 } +853 +854 /// Returns a reference to the retransmits stored in this box +855 pub(super) fn get(&self) -> Option<&Retransmits> { +856 self.retransmits.as_deref() +857 } +858 +859 /// Returns a mutable reference to the retransmits stored in this box +860 pub(super) fn get_mut(&mut self) -> Option<&mut Retransmits> { +861 self.retransmits.as_deref_mut() +862 } +863 +864 /// Returns a mutable reference to the stored retransmits +865 /// +866 /// This function will allocate a backing storage if required. +867 pub(super) fn get_or_create(&mut self) -> &mut Retransmits { +868 if self.retransmits.is_none() { +869 self.retransmits = Some(Box::default()); +870 } +871 self.retransmits.as_deref_mut().unwrap() +872 } +873} +874 +875/// RFC4303-style sliding window packet number deduplicator. +876/// +877/// A contiguous bitfield, where each bit corresponds to a packet number and the rightmost bit is +878/// always set. A set bit represents a packet that has been successfully authenticated. Bits left of +879/// the window are assumed to be set. +880/// +881/// ```text +882/// ...xxxxxxxxx 1 0 +883/// ^ ^ ^ +884/// window highest next +885/// ``` +886#[derive(Debug, Default)] +887pub(super) struct Dedup { +888 window: Window, +889 /// Lowest packet number higher than all yet authenticated. +890 next: u64, +891} +892 +893/// Inner bitfield type. +894/// +895/// Because QUIC never reuses packet numbers, this only needs to be large enough to deal with +896/// packets that are reordered but still delivered in a timely manner. +897type Window = u128; +898 +899/// Number of packets tracked by `Dedup`. +900const WINDOW_SIZE: u64 = 1 + mem::size_of::<Window>() as u64 * 8; +901 +902impl Dedup { +903 /// Construct an empty window positioned at the start. +904 #[cfg(test)] +905 pub(super) fn new() -> Self { +906 Self { window: 0, next: 0 } +907 } +908 +909 /// Highest packet number authenticated. +910 fn highest(&self) -> u64 { +911 self.next - 1 +912 } +913 +914 /// Record a newly authenticated packet number. +915 /// +916 /// Returns whether the packet might be a duplicate. +917 pub(super) fn insert(&mut self, packet: u64) -> bool { +918 if let Some(diff) = packet.checked_sub(self.next) { +919 // Right of window +920 self.window = ((self.window << 1) | 1) +921 .checked_shl(cmp::min(diff, u64::from(u32::MAX)) as u32) +922 .unwrap_or(0); +923 self.next = packet + 1; +924 false +925 } else if self.highest() - packet < WINDOW_SIZE { +926 // Within window +927 if let Some(bit) = (self.highest() - packet).checked_sub(1) { +928 // < highest +929 let mask = 1 << bit; +930 let duplicate = self.window & mask != 0; +931 self.window |= mask; +932 duplicate +933 } else { +934 // == highest +935 true +936 } +937 } else { +938 // Left of window +939 true +940 } +941 } +942 +943 /// Returns the packet number of the smallest packet missing between the provided interval +944 /// +945 /// If there are no missing packets, returns `None` +946 fn smallest_missing_in_interval(&self, lower_bound: u64, upper_bound: u64) -> Option<u64> { +947 debug_assert!(lower_bound <= upper_bound); +948 debug_assert!(upper_bound <= self.highest()); +949 const BITFIELD_SIZE: u64 = (mem::size_of::<Window>() * 8) as u64; +950 +951 // Since we already know the packets at the boundaries have been received, we only need to +952 // check those in between them (this removes the necessity of extra logic to deal with the +953 // highest packet, which is stored outside the bitfield) +954 let lower_bound = lower_bound + 1; +955 let upper_bound = upper_bound.saturating_sub(1); +956 +957 // Note: the offsets are counted from the right +958 // The highest packet is not included in the bitfield, so we subtract 1 to account for that +959 let start_offset = (self.highest() - upper_bound).max(1) - 1; +960 if start_offset >= BITFIELD_SIZE { +961 // The start offset is outside of the window. All packets outside of the window are +962 // considered to be received. +963 return None; +964 } +965 +966 let end_offset_exclusive = self.highest().saturating_sub(lower_bound); +967 +968 // The range is clamped at the edge of the window, because any earlier packets are +969 // considered to be received +970 let range_len = end_offset_exclusive +971 .saturating_sub(start_offset) +972 .min(BITFIELD_SIZE); +973 if range_len == 0 { +974 return None; +975 } +976 +977 // Ensure the shift is within bounds (we already know start_offset < BITFIELD_SIZE, +978 // because of the early return) +979 let mask = if range_len == BITFIELD_SIZE { +980 u128::MAX +981 } else { +982 ((1u128 << range_len) - 1) << start_offset +983 }; +984 let gaps = !self.window & mask; +985 +986 let smallest_missing_offset = 128 - gaps.leading_zeros() as u64; +987 let smallest_missing_packet = self.highest() - smallest_missing_offset; +988 +989 if smallest_missing_packet <= upper_bound { +990 Some(smallest_missing_packet) +991 } else { +992 None +993 } +994 } +995 +996 /// Returns true if there are any missing packets between the provided interval +997 /// +998 /// The provided packet numbers must have been received before calling this function +999 fn missing_in_interval(&self, lower_bound: u64, upper_bound: u64) -> bool { +1000 self.smallest_missing_in_interval(lower_bound, upper_bound) +1001 .is_some() 1002 } -1003 -1004 /// Whether an ack-eliciting packet will be sent. -1005 pub(super) fn is_ack_eliciting(&self) -> bool { -1006 let Self { -1007 acks: _, -1008 close, -1009 space_specific, -1010 other, -1011 } = *self; -1012 if close { -1013 // No ack-eliciting frames are included with a CONNECTION_CLOSE, only acks. -1014 return false; -1015 } -1016 space_specific || other -1017 } -1018 -1019 /// Whether no data is sendable. -1020 pub(super) fn is_empty(&self) -> bool { -1021 let Self { -1022 acks, -1023 close, -1024 space_specific, -1025 other, -1026 } = *self; -1027 !acks && !close && !space_specific && !other -1028 } -1029} -1030 -1031impl ::std::ops::BitOrAssign for SendableFrames { -1032 fn bitor_assign(&mut self, rhs: Self) { -1033 let Self { -1034 acks, -1035 close, -1036 space_specific, -1037 other, -1038 } = rhs; -1039 -1040 self.acks |= acks; -1041 self.close |= close; -1042 self.space_specific |= space_specific; -1043 self.other |= other; -1044 } -1045} -1046 -1047#[derive(Debug)] -1048pub(super) struct PendingAcks { -1049 /// Whether we should send an ACK immediately, even if that means sending an ACK-only packet -1050 /// -1051 /// When `immediate_ack_required` is false, the normal behavior is to send ACK frames only when -1052 /// there is other data to send, or when the `MaxAckDelay` timer expires. -1053 immediate_ack_required: bool, -1054 /// The number of ack-eliciting packets received since the last ACK frame was sent -1055 /// -1056 /// Once the count _exceeds_ `ack_eliciting_threshold`, an immediate ACK is required -1057 ack_eliciting_since_last_ack_sent: u64, -1058 non_ack_eliciting_since_last_ack_sent: u64, -1059 ack_eliciting_threshold: u64, -1060 /// The reordering threshold, controlling how we respond to out-of-order ack-eliciting packets -1061 /// -1062 /// Different values enable different behavior: -1063 /// -1064 /// * `0`: no special action is taken -1065 /// * `1`: an ACK is immediately sent if it is out-of-order according to RFC 9000 -1066 /// * `>1`: an ACK is immediately sent if it is out-of-order according to the ACK frequency draft -1067 reordering_threshold: u64, -1068 /// The earliest ack-eliciting packet since the last ACK was sent, used to calculate the moment -1069 /// upon which `max_ack_delay` elapses -1070 earliest_ack_eliciting_since_last_ack_sent: Option<Instant>, -1071 /// Packet number ranges for which to still send acknowledgements. -1072 /// -1073 /// These are packet number ranges of ack-eliciting packets the peer has sent and which -1074 /// need to be acknowledged. Packet numbers are only removed from here once the peer has -1075 /// acknowledged the ACKs for them. -1076 ranges: ArrayRangeSet, -1077 /// The largest packet number received and the time it was received -1078 /// -1079 /// Used to calculate ACK delay in [`PendingAcks::ack_delay`]. -1080 largest_packet: Option<(u64, Instant)>, -1081 /// The ack-eliciting packet we have received with the largest packet number -1082 largest_ack_eliciting_packet: Option<u64>, -1083 /// The largest acknowledged packet number sent in an ACK frame -1084 largest_acked: Option<u64>, -1085} -1086 -1087impl PendingAcks { -1088 fn new() -> Self { -1089 Self { -1090 immediate_ack_required: false, -1091 ack_eliciting_since_last_ack_sent: 0, -1092 non_ack_eliciting_since_last_ack_sent: 0, -1093 ack_eliciting_threshold: 1, -1094 reordering_threshold: 1, -1095 earliest_ack_eliciting_since_last_ack_sent: None, -1096 ranges: Default::default(), -1097 largest_packet: Default::default(), -1098 largest_ack_eliciting_packet: Default::default(), -1099 largest_acked: Default::default(), -1100 } -1101 } -1102 -1103 pub(super) fn set_ack_frequency_params(&mut self, frame: &frame::AckFrequency) { -1104 self.ack_eliciting_threshold = frame.ack_eliciting_threshold.into_inner(); -1105 self.reordering_threshold = frame.reordering_threshold.into_inner(); -1106 } -1107 -1108 pub(super) fn set_immediate_ack_required(&mut self) { -1109 self.immediate_ack_required = true; -1110 } -1111 -1112 pub(super) fn on_max_ack_delay_timeout(&mut self) { -1113 self.immediate_ack_required = self.ack_eliciting_since_last_ack_sent > 0; -1114 } -1115 -1116 pub(super) fn max_ack_delay_timeout(&self, max_ack_delay: Duration) -> Option<Instant> { -1117 self.earliest_ack_eliciting_since_last_ack_sent -1118 .map(|earliest_unacked| earliest_unacked + max_ack_delay) -1119 } -1120 -1121 /// Whether any ACK frames SHOULD be sent -1122 /// -1123 /// This is used in the top-level [`Connection::space_can_send`], so determines if a -1124 /// packet will be built. It is often possible to construct new ACK ranges to send -1125 /// before this returns `true`. This results in more ACK frames being sent, and -1126 /// processing those at the receiver costs CPU for very little improvements. -1127 /// -1128 /// [`Connection::space_can_send`]: super::Connection::space_can_send -1129 pub(super) fn can_send(&self) -> bool { -1130 self.immediate_ack_required && !self.ranges.is_empty() -1131 } -1132 -1133 /// Returns the delay since the packet with the largest packet number was received -1134 pub(super) fn ack_delay(&self, now: Instant) -> Duration { -1135 self.largest_packet -1136 .map_or_else(Duration::default, |(_, received)| now - received) -1137 } -1138 -1139 /// Handle receipt of a new packet -1140 /// -1141 /// Returns true if the max ack delay timer should be armed -1142 pub(super) fn packet_received( -1143 &mut self, -1144 now: Instant, -1145 packet_number: u64, -1146 ack_eliciting: bool, -1147 dedup: &Dedup, -1148 ) -> bool { -1149 if !ack_eliciting { -1150 self.non_ack_eliciting_since_last_ack_sent += 1; -1151 return false; -1152 } +1003} +1004 +1005/// Indicates which data is available for sending. +1006/// +1007/// This applies to a particular space ID that was queried and all refers to on-path data. +1008#[derive(Clone, Copy, PartialEq, Eq, Debug)] +1009pub(super) struct SendableFrames { +1010 /// Whether there are ACK frames to send, these are not ack-eliciting. +1011 pub(super) acks: bool, +1012 /// Whether there is a CONNECTION_CLOSE to send, this is not ack-eliciting. +1013 pub(super) close: bool, +1014 /// Whether there are any frames that must be sent on this specific space. +1015 /// +1016 /// A space here in the sense of a QUIC Multipath packet number space: `Initial`, +1017 /// `Handshake` and all `Data(PathId)` spaces. +1018 /// +1019 /// These are ack-eliciting. Some frames are scheduled per path, e.g. PING, +1020 /// IMMEDIATE_ACK, PATH_CHALLENGE or PATH_RESPONSE. +1021 pub(super) space_specific: bool, +1022 /// Whether there are any other frames to send, these are ack-eliciting. +1023 pub(super) other: bool, +1024} +1025 +1026impl SendableFrames { +1027 /// Returns that no data is available for sending +1028 pub(super) fn empty() -> Self { +1029 Self { +1030 acks: false, +1031 close: false, +1032 space_specific: false, +1033 other: false, +1034 } +1035 } +1036 +1037 /// Whether an ack-eliciting packet will be sent. +1038 pub(super) fn is_ack_eliciting(&self) -> bool { +1039 let Self { +1040 acks: _, +1041 close, +1042 space_specific, +1043 other, +1044 } = *self; +1045 if close { +1046 // No ack-eliciting frames are included with a CONNECTION_CLOSE, only acks. +1047 return false; +1048 } +1049 space_specific || other +1050 } +1051 +1052 /// Whether no data is sendable. +1053 pub(super) fn is_empty(&self) -> bool { +1054 let Self { +1055 acks, +1056 close, +1057 space_specific, +1058 other, +1059 } = *self; +1060 !acks && !close && !space_specific && !other +1061 } +1062} +1063 +1064impl ::std::ops::BitOrAssign for SendableFrames { +1065 fn bitor_assign(&mut self, rhs: Self) { +1066 let Self { +1067 acks, +1068 close, +1069 space_specific, +1070 other, +1071 } = rhs; +1072 +1073 self.acks |= acks; +1074 self.close |= close; +1075 self.space_specific |= space_specific; +1076 self.other |= other; +1077 } +1078} +1079 +1080#[derive(Debug)] +1081pub(super) struct PendingAcks { +1082 /// Whether we should send an ACK immediately, even if that means sending an ACK-only packet +1083 /// +1084 /// When `immediate_ack_required` is false, the normal behavior is to send ACK frames only when +1085 /// there is other data to send, or when the `MaxAckDelay` timer expires. +1086 immediate_ack_required: bool, +1087 /// The number of ack-eliciting packets received since the last ACK frame was sent +1088 /// +1089 /// Once the count _exceeds_ `ack_eliciting_threshold`, an immediate ACK is required +1090 ack_eliciting_since_last_ack_sent: u64, +1091 non_ack_eliciting_since_last_ack_sent: u64, +1092 ack_eliciting_threshold: u64, +1093 /// The reordering threshold, controlling how we respond to out-of-order ack-eliciting packets +1094 /// +1095 /// Different values enable different behavior: +1096 /// +1097 /// * `0`: no special action is taken +1098 /// * `1`: an ACK is immediately sent if it is out-of-order according to RFC 9000 +1099 /// * `>1`: an ACK is immediately sent if it is out-of-order according to the ACK frequency draft +1100 reordering_threshold: u64, +1101 /// The earliest ack-eliciting packet since the last ACK was sent, used to calculate the moment +1102 /// upon which `max_ack_delay` elapses +1103 earliest_ack_eliciting_since_last_ack_sent: Option<Instant>, +1104 /// Packet number ranges for which to still send acknowledgements. +1105 /// +1106 /// These are packet number ranges of ack-eliciting packets the peer has sent and which +1107 /// need to be acknowledged. Packet numbers are only removed from here once the peer has +1108 /// acknowledged the ACKs for them. +1109 ranges: ArrayRangeSet, +1110 /// The largest packet number received and the time it was received +1111 /// +1112 /// Used to calculate ACK delay in [`PendingAcks::ack_delay`]. +1113 largest_packet: Option<(u64, Instant)>, +1114 /// The ack-eliciting packet we have received with the largest packet number +1115 largest_ack_eliciting_packet: Option<u64>, +1116 /// The largest acknowledged packet number sent in an ACK frame +1117 largest_acked: Option<u64>, +1118} +1119 +1120impl PendingAcks { +1121 fn new() -> Self { +1122 Self { +1123 immediate_ack_required: false, +1124 ack_eliciting_since_last_ack_sent: 0, +1125 non_ack_eliciting_since_last_ack_sent: 0, +1126 ack_eliciting_threshold: 1, +1127 reordering_threshold: 1, +1128 earliest_ack_eliciting_since_last_ack_sent: None, +1129 ranges: Default::default(), +1130 largest_packet: Default::default(), +1131 largest_ack_eliciting_packet: Default::default(), +1132 largest_acked: Default::default(), +1133 } +1134 } +1135 +1136 pub(super) fn set_ack_frequency_params(&mut self, frame: &frame::AckFrequency) { +1137 self.ack_eliciting_threshold = frame.ack_eliciting_threshold.into_inner(); +1138 self.reordering_threshold = frame.reordering_threshold.into_inner(); +1139 } +1140 +1141 pub(super) fn set_immediate_ack_required(&mut self) { +1142 self.immediate_ack_required = true; +1143 } +1144 +1145 pub(super) fn on_max_ack_delay_timeout(&mut self) { +1146 self.immediate_ack_required = self.ack_eliciting_since_last_ack_sent > 0; +1147 } +1148 +1149 pub(super) fn max_ack_delay_timeout(&self, max_ack_delay: Duration) -> Option<Instant> { +1150 self.earliest_ack_eliciting_since_last_ack_sent +1151 .map(|earliest_unacked| earliest_unacked + max_ack_delay) +1152 } 1153 -1154 let prev_largest_ack_eliciting = self.largest_ack_eliciting_packet.unwrap_or(0); -1155 -1156 // Track largest ack-eliciting packet -1157 self.largest_ack_eliciting_packet = self -1158 .largest_ack_eliciting_packet -1159 .map(|pn| pn.max(packet_number)) -1160 .or(Some(packet_number)); -1161 -1162 // Handle ack_eliciting_threshold -1163 self.ack_eliciting_since_last_ack_sent += 1; -1164 self.immediate_ack_required |= -1165 self.ack_eliciting_since_last_ack_sent > self.ack_eliciting_threshold; -1166 -1167 // Handle out-of-order packets -1168 self.immediate_ack_required |= -1169 self.is_out_of_order(packet_number, prev_largest_ack_eliciting, dedup); -1170 -1171 // Arm max_ack_delay timer if necessary -1172 if self.earliest_ack_eliciting_since_last_ack_sent.is_none() && !self.can_send() { -1173 self.earliest_ack_eliciting_since_last_ack_sent = Some(now); -1174 return true; -1175 } -1176 -1177 false -1178 } -1179 -1180 fn is_out_of_order( -1181 &self, -1182 packet_number: u64, -1183 prev_largest_ack_eliciting: u64, -1184 dedup: &Dedup, -1185 ) -> bool { -1186 match self.reordering_threshold { -1187 0 => false, -1188 1 => { -1189 // From https://www.rfc-editor.org/rfc/rfc9000#section-13.2.1-7 -1190 packet_number < prev_largest_ack_eliciting -1191 || dedup.missing_in_interval(prev_largest_ack_eliciting, packet_number) -1192 } -1193 _ => { -1194 // From acknowledgement frequency draft, section 6.1: send an ACK immediately if -1195 // doing so would cause the sender to detect a new packet loss -1196 let Some((largest_acked, largest_unacked)) = -1197 self.largest_acked.zip(self.largest_ack_eliciting_packet) -1198 else { -1199 return false; -1200 }; -1201 if self.reordering_threshold > largest_acked { -1202 return false; -1203 } -1204 // The largest packet number that could be declared lost without a new ACK being -1205 // sent -1206 let largest_reported = largest_acked - self.reordering_threshold + 1; -1207 let Some(smallest_missing_unreported) = -1208 dedup.smallest_missing_in_interval(largest_reported, largest_unacked) -1209 else { -1210 return false; -1211 }; -1212 largest_unacked - smallest_missing_unreported >= self.reordering_threshold -1213 } -1214 } -1215 } -1216 -1217 /// Should be called whenever ACKs have been sent -1218 /// -1219 /// This will suppress sending further ACKs until additional ACK eliciting frames arrive -1220 pub(super) fn acks_sent(&mut self) { -1221 // It is possible (though unlikely) that the ACKs we just sent do not cover all the -1222 // ACK-eliciting packets we have received (e.g. if there is not enough room in the packet to -1223 // fit all the ranges). To keep things simple, however, we assume they do. If there are -1224 // indeed some ACKs that weren't covered, the packets might be ACKed later anyway, because -1225 // they are still contained in `self.ranges`. If we somehow fail to send the ACKs at a later -1226 // moment, the peer will assume the packets got lost and will retransmit their frames in a -1227 // new packet, which is suboptimal, because we already received them. Our assumption here is -1228 // that simplicity results in code that is more performant, even in the presence of -1229 // occasional redundant retransmits. -1230 self.immediate_ack_required = false; -1231 self.ack_eliciting_since_last_ack_sent = 0; -1232 self.non_ack_eliciting_since_last_ack_sent = 0; -1233 self.earliest_ack_eliciting_since_last_ack_sent = None; -1234 self.largest_acked = self.largest_ack_eliciting_packet; -1235 } -1236 -1237 /// Insert one packet that needs to be acknowledged -1238 pub(super) fn insert_one(&mut self, packet: u64, now: Instant) { -1239 self.ranges.insert_one(packet); -1240 -1241 if self.largest_packet.is_none_or(|(pn, _)| packet > pn) { -1242 self.largest_packet = Some((packet, now)); -1243 } -1244 -1245 if self.ranges.range_count() > MAX_ACK_BLOCKS { -1246 self.ranges.pop_min(); +1154 /// Whether any ACK frames SHOULD be sent +1155 /// +1156 /// This is used in the top-level [`Connection::space_can_send`], so determines if a +1157 /// packet will be built. It is often possible to construct new ACK ranges to send +1158 /// before this returns `true`. This results in more ACK frames being sent, and +1159 /// processing those at the receiver costs CPU for very little improvements. +1160 /// +1161 /// [`Connection::space_can_send`]: super::Connection::space_can_send +1162 pub(super) fn can_send(&self) -> bool { +1163 self.immediate_ack_required && !self.ranges.is_empty() +1164 } +1165 +1166 /// Returns the delay since the packet with the largest packet number was received +1167 pub(super) fn ack_delay(&self, now: Instant) -> Duration { +1168 self.largest_packet +1169 .map_or_else(Duration::default, |(_, received)| now - received) +1170 } +1171 +1172 /// Handle receipt of a new packet +1173 /// +1174 /// Returns true if the max ack delay timer should be armed +1175 pub(super) fn packet_received( +1176 &mut self, +1177 now: Instant, +1178 packet_number: u64, +1179 ack_eliciting: bool, +1180 dedup: &Dedup, +1181 ) -> bool { +1182 if !ack_eliciting { +1183 self.non_ack_eliciting_since_last_ack_sent += 1; +1184 return false; +1185 } +1186 +1187 let prev_largest_ack_eliciting = self.largest_ack_eliciting_packet.unwrap_or(0); +1188 +1189 // Track largest ack-eliciting packet +1190 self.largest_ack_eliciting_packet = self +1191 .largest_ack_eliciting_packet +1192 .map(|pn| pn.max(packet_number)) +1193 .or(Some(packet_number)); +1194 +1195 // Handle ack_eliciting_threshold +1196 self.ack_eliciting_since_last_ack_sent += 1; +1197 self.immediate_ack_required |= +1198 self.ack_eliciting_since_last_ack_sent > self.ack_eliciting_threshold; +1199 +1200 // Handle out-of-order packets +1201 self.immediate_ack_required |= +1202 self.is_out_of_order(packet_number, prev_largest_ack_eliciting, dedup); +1203 +1204 // Arm max_ack_delay timer if necessary +1205 if self.earliest_ack_eliciting_since_last_ack_sent.is_none() && !self.can_send() { +1206 self.earliest_ack_eliciting_since_last_ack_sent = Some(now); +1207 return true; +1208 } +1209 +1210 false +1211 } +1212 +1213 fn is_out_of_order( +1214 &self, +1215 packet_number: u64, +1216 prev_largest_ack_eliciting: u64, +1217 dedup: &Dedup, +1218 ) -> bool { +1219 match self.reordering_threshold { +1220 0 => false, +1221 1 => { +1222 // From https://www.rfc-editor.org/rfc/rfc9000#section-13.2.1-7 +1223 packet_number < prev_largest_ack_eliciting +1224 || dedup.missing_in_interval(prev_largest_ack_eliciting, packet_number) +1225 } +1226 _ => { +1227 // From acknowledgement frequency draft, section 6.1: send an ACK immediately if +1228 // doing so would cause the sender to detect a new packet loss +1229 let Some((largest_acked, largest_unacked)) = +1230 self.largest_acked.zip(self.largest_ack_eliciting_packet) +1231 else { +1232 return false; +1233 }; +1234 if self.reordering_threshold > largest_acked { +1235 return false; +1236 } +1237 // The largest packet number that could be declared lost without a new ACK being +1238 // sent +1239 let largest_reported = largest_acked - self.reordering_threshold + 1; +1240 let Some(smallest_missing_unreported) = +1241 dedup.smallest_missing_in_interval(largest_reported, largest_unacked) +1242 else { +1243 return false; +1244 }; +1245 largest_unacked - smallest_missing_unreported >= self.reordering_threshold +1246 } 1247 } 1248 } 1249 -1250 /// Remove ACKs of packets numbered at or below `max` from the set of pending ACKs -1251 pub(super) fn subtract_below(&mut self, max: u64) { -1252 self.ranges.remove(0..(max + 1)); -1253 } -1254 -1255 /// Returns the set of currently pending ACK ranges -1256 pub(super) fn ranges(&self) -> &ArrayRangeSet { -1257 &self.ranges -1258 } -1259 -1260 /// Queue an ACK if a significant number of non-ACK-eliciting packets have not yet been -1261 /// acknowledged -1262 /// -1263 /// Should be called immediately before a non-probing packet is composed, when we've already -1264 /// committed to sending a packet regardless. -1265 pub(super) fn maybe_ack_non_eliciting(&mut self) { -1266 // If we're going to send a packet anyway, and we've received a significant number of -1267 // non-ACK-eliciting packets, then include an ACK to help the peer perform timely loss -1268 // detection even if they're not sending any ACK-eliciting packets themselves. Exact -1269 // threshold chosen somewhat arbitrarily. -1270 const LAZY_ACK_THRESHOLD: u64 = 10; -1271 if self.non_ack_eliciting_since_last_ack_sent > LAZY_ACK_THRESHOLD { -1272 self.immediate_ack_required = true; -1273 } -1274 } -1275} -1276 -1277/// Helper for mitigating [optimistic ACK attacks] -1278/// -1279/// A malicious peer could prompt the local application to begin a large data transfer, and then -1280/// send ACKs without first waiting for data to be received. This could defeat congestion control, -1281/// allowing the connection to consume disproportionate resources. We therefore occasionally skip -1282/// packet numbers, and classify any ACK referencing a skipped packet number as a transport error. -1283/// -1284/// Skipped packet numbers occur only in the application data space (where costly transfers might -1285/// take place) and are distributed exponentially to reflect the reduced likelihood and impact of -1286/// bad behavior from a peer that has been well-behaved for an extended period. -1287/// -1288/// ACKs for packet numbers that have not yet been allocated are also a transport error, but an -1289/// attacker with knowledge of the congestion control algorithm in use could time falsified ACKs to -1290/// arrive after the packets they reference are sent. -1291/// -1292/// [optimistic ACK attacks]: https://www.rfc-editor.org/rfc/rfc9000.html#name-optimistic-ack-attack -1293pub(super) struct PacketNumberFilter { -1294 /// Next outgoing packet number to skip -1295 next_skipped_packet_number: u64, -1296 /// Most recently skipped packet number -1297 prev_skipped_packet_number: Option<u64>, -1298 /// Next packet number to skip is randomly selected from 2^n..2^n+1 -1299 exponent: u32, -1300} -1301 -1302impl PacketNumberFilter { -1303 pub(super) fn new(rng: &mut (impl CryptoRng + ?Sized)) -> Self { -1304 // First skipped PN is in 0..64 -1305 let exponent = 6; -1306 Self { -1307 next_skipped_packet_number: rng.random_range(0..2u64.saturating_pow(exponent)), -1308 prev_skipped_packet_number: None, -1309 exponent, -1310 } -1311 } -1312 -1313 #[cfg(test)] -1314 pub(super) fn disabled() -> Self { -1315 Self { -1316 next_skipped_packet_number: u64::MAX, -1317 prev_skipped_packet_number: None, -1318 exponent: u32::MAX, -1319 } -1320 } -1321 -1322 /// Whether to use the provided packet number (false) or to skip it (true) -1323 pub(super) fn skip_pn(&mut self, n: u64, rng: &mut (impl CryptoRng + ?Sized)) -> bool { -1324 if n != self.next_skipped_packet_number { -1325 return false; -1326 } -1327 -1328 trace!("skipping pn {n}"); -1329 // Skip this packet number, and choose the next one to skip -1330 self.prev_skipped_packet_number = Some(self.next_skipped_packet_number); -1331 let next_exponent = self.exponent.saturating_add(1); -1332 self.next_skipped_packet_number = rng -1333 .random_range(2u64.saturating_pow(self.exponent)..2u64.saturating_pow(next_exponent)); -1334 self.exponent = next_exponent; -1335 true -1336 } -1337} -1338 -1339/// Ensures we can always fit all our ACKs in a single minimum-MTU packet with room to spare -1340const MAX_ACK_BLOCKS: usize = 64; -1341 -1342#[cfg(test)] -1343mod test { -1344 use rand::Rng; -1345 use rand::seq::SliceRandom; -1346 -1347 use crate::token::ResetToken; -1348 use crate::{ConnectionIdGenerator, RandomConnectionIdGenerator}; -1349 -1350 use super::*; -1351 -1352 #[test] -1353 fn sanity() { -1354 let mut dedup = Dedup::new(); -1355 assert!(!dedup.insert(0)); -1356 assert_eq!(dedup.next, 1); -1357 assert_eq!(dedup.window, 0b1); -1358 assert!(dedup.insert(0)); -1359 assert_eq!(dedup.next, 1); -1360 assert_eq!(dedup.window, 0b1); -1361 assert!(!dedup.insert(1)); -1362 assert_eq!(dedup.next, 2); -1363 assert_eq!(dedup.window, 0b11); -1364 assert!(!dedup.insert(2)); -1365 assert_eq!(dedup.next, 3); -1366 assert_eq!(dedup.window, 0b111); -1367 assert!(!dedup.insert(4)); -1368 assert_eq!(dedup.next, 5); -1369 assert_eq!(dedup.window, 0b11110); -1370 assert!(!dedup.insert(7)); -1371 assert_eq!(dedup.next, 8); -1372 assert_eq!(dedup.window, 0b1111_0100); -1373 assert!(dedup.insert(4)); -1374 assert!(!dedup.insert(3)); -1375 assert_eq!(dedup.next, 8); -1376 assert_eq!(dedup.window, 0b1111_1100); -1377 assert!(!dedup.insert(6)); -1378 assert_eq!(dedup.next, 8); -1379 assert_eq!(dedup.window, 0b1111_1101); -1380 assert!(!dedup.insert(5)); -1381 assert_eq!(dedup.next, 8); -1382 assert_eq!(dedup.window, 0b1111_1111); -1383 } +1250 /// Should be called whenever ACKs have been sent +1251 /// +1252 /// This will suppress sending further ACKs until additional ACK eliciting frames arrive +1253 pub(super) fn acks_sent(&mut self) { +1254 // It is possible (though unlikely) that the ACKs we just sent do not cover all the +1255 // ACK-eliciting packets we have received (e.g. if there is not enough room in the packet to +1256 // fit all the ranges). To keep things simple, however, we assume they do. If there are +1257 // indeed some ACKs that weren't covered, the packets might be ACKed later anyway, because +1258 // they are still contained in `self.ranges`. If we somehow fail to send the ACKs at a later +1259 // moment, the peer will assume the packets got lost and will retransmit their frames in a +1260 // new packet, which is suboptimal, because we already received them. Our assumption here is +1261 // that simplicity results in code that is more performant, even in the presence of +1262 // occasional redundant retransmits. +1263 self.immediate_ack_required = false; +1264 self.ack_eliciting_since_last_ack_sent = 0; +1265 self.non_ack_eliciting_since_last_ack_sent = 0; +1266 self.earliest_ack_eliciting_since_last_ack_sent = None; +1267 self.largest_acked = self.largest_ack_eliciting_packet; +1268 } +1269 +1270 /// Insert one packet that needs to be acknowledged +1271 pub(super) fn insert_one(&mut self, packet: u64, now: Instant) { +1272 self.ranges.insert_one(packet); +1273 +1274 if self.largest_packet.is_none_or(|(pn, _)| packet > pn) { +1275 self.largest_packet = Some((packet, now)); +1276 } +1277 +1278 if self.ranges.range_count() > MAX_ACK_BLOCKS { +1279 self.ranges.pop_min(); +1280 } +1281 } +1282 +1283 /// Remove ACKs of packets numbered at or below `max` from the set of pending ACKs +1284 pub(super) fn subtract_below(&mut self, max: u64) { +1285 self.ranges.remove(0..(max + 1)); +1286 } +1287 +1288 /// Returns the set of currently pending ACK ranges +1289 pub(super) fn ranges(&self) -> &ArrayRangeSet { +1290 &self.ranges +1291 } +1292 +1293 /// Queue an ACK if a significant number of non-ACK-eliciting packets have not yet been +1294 /// acknowledged +1295 /// +1296 /// Should be called immediately before a non-probing packet is composed, when we've already +1297 /// committed to sending a packet regardless. +1298 pub(super) fn maybe_ack_non_eliciting(&mut self) { +1299 // If we're going to send a packet anyway, and we've received a significant number of +1300 // non-ACK-eliciting packets, then include an ACK to help the peer perform timely loss +1301 // detection even if they're not sending any ACK-eliciting packets themselves. Exact +1302 // threshold chosen somewhat arbitrarily. +1303 const LAZY_ACK_THRESHOLD: u64 = 10; +1304 if self.non_ack_eliciting_since_last_ack_sent > LAZY_ACK_THRESHOLD { +1305 self.immediate_ack_required = true; +1306 } +1307 } +1308} +1309 +1310/// Helper for mitigating [optimistic ACK attacks] +1311/// +1312/// A malicious peer could prompt the local application to begin a large data transfer, and then +1313/// send ACKs without first waiting for data to be received. This could defeat congestion control, +1314/// allowing the connection to consume disproportionate resources. We therefore occasionally skip +1315/// packet numbers, and classify any ACK referencing a skipped packet number as a transport error. +1316/// +1317/// Skipped packet numbers occur only in the application data space (where costly transfers might +1318/// take place) and are distributed exponentially to reflect the reduced likelihood and impact of +1319/// bad behavior from a peer that has been well-behaved for an extended period. +1320/// +1321/// ACKs for packet numbers that have not yet been allocated are also a transport error, but an +1322/// attacker with knowledge of the congestion control algorithm in use could time falsified ACKs to +1323/// arrive after the packets they reference are sent. +1324/// +1325/// [optimistic ACK attacks]: https://www.rfc-editor.org/rfc/rfc9000.html#name-optimistic-ack-attack +1326pub(super) struct PacketNumberFilter { +1327 /// Next outgoing packet number to skip +1328 next_skipped_packet_number: u64, +1329 /// Most recently skipped packet number +1330 prev_skipped_packet_number: Option<u64>, +1331 /// Next packet number to skip is randomly selected from 2^n..2^n+1 +1332 exponent: u32, +1333} +1334 +1335impl PacketNumberFilter { +1336 pub(super) fn new(rng: &mut (impl CryptoRng + ?Sized)) -> Self { +1337 // First skipped PN is in 0..64 +1338 let exponent = 6; +1339 Self { +1340 next_skipped_packet_number: rng.random_range(0..2u64.saturating_pow(exponent)), +1341 prev_skipped_packet_number: None, +1342 exponent, +1343 } +1344 } +1345 +1346 #[cfg(test)] +1347 pub(super) fn disabled() -> Self { +1348 Self { +1349 next_skipped_packet_number: u64::MAX, +1350 prev_skipped_packet_number: None, +1351 exponent: u32::MAX, +1352 } +1353 } +1354 +1355 /// Whether to use the provided packet number (false) or to skip it (true) +1356 pub(super) fn skip_pn(&mut self, n: u64, rng: &mut (impl CryptoRng + ?Sized)) -> bool { +1357 if n != self.next_skipped_packet_number { +1358 return false; +1359 } +1360 +1361 trace!("skipping pn {n}"); +1362 // Skip this packet number, and choose the next one to skip +1363 self.prev_skipped_packet_number = Some(self.next_skipped_packet_number); +1364 let next_exponent = self.exponent.saturating_add(1); +1365 self.next_skipped_packet_number = rng +1366 .random_range(2u64.saturating_pow(self.exponent)..2u64.saturating_pow(next_exponent)); +1367 self.exponent = next_exponent; +1368 true +1369 } +1370} +1371 +1372/// Ensures we can always fit all our ACKs in a single minimum-MTU packet with room to spare +1373const MAX_ACK_BLOCKS: usize = 64; +1374 +1375#[cfg(test)] +1376mod test { +1377 use rand::Rng; +1378 use rand::seq::SliceRandom; +1379 +1380 use crate::token::ResetToken; +1381 use crate::{ConnectionIdGenerator, RandomConnectionIdGenerator}; +1382 +1383 use super::*; 1384 1385 #[test] -1386 fn happypath() { +1386 fn sanity() { 1387 let mut dedup = Dedup::new(); -1388 for i in 0..(2 * WINDOW_SIZE) { -1389 assert!(!dedup.insert(i)); -1390 for j in 0..=i { -1391 assert!(dedup.insert(j)); -1392 } -1393 } -1394 } -1395 -1396 #[test] -1397 fn jump() { -1398 let mut dedup = Dedup::new(); -1399 dedup.insert(2 * WINDOW_SIZE); -1400 assert!(dedup.insert(WINDOW_SIZE)); -1401 assert_eq!(dedup.next, 2 * WINDOW_SIZE + 1); -1402 assert_eq!(dedup.window, 0); -1403 assert!(!dedup.insert(WINDOW_SIZE + 1)); -1404 assert_eq!(dedup.next, 2 * WINDOW_SIZE + 1); -1405 assert_eq!(dedup.window, 1 << (WINDOW_SIZE - 2)); -1406 } -1407 -1408 #[test] -1409 fn dedup_has_missing() { -1410 let mut dedup = Dedup::new(); -1411 -1412 dedup.insert(0); -1413 assert!(!dedup.missing_in_interval(0, 0)); -1414 -1415 dedup.insert(1); -1416 assert!(!dedup.missing_in_interval(0, 1)); +1388 assert!(!dedup.insert(0)); +1389 assert_eq!(dedup.next, 1); +1390 assert_eq!(dedup.window, 0b1); +1391 assert!(dedup.insert(0)); +1392 assert_eq!(dedup.next, 1); +1393 assert_eq!(dedup.window, 0b1); +1394 assert!(!dedup.insert(1)); +1395 assert_eq!(dedup.next, 2); +1396 assert_eq!(dedup.window, 0b11); +1397 assert!(!dedup.insert(2)); +1398 assert_eq!(dedup.next, 3); +1399 assert_eq!(dedup.window, 0b111); +1400 assert!(!dedup.insert(4)); +1401 assert_eq!(dedup.next, 5); +1402 assert_eq!(dedup.window, 0b11110); +1403 assert!(!dedup.insert(7)); +1404 assert_eq!(dedup.next, 8); +1405 assert_eq!(dedup.window, 0b1111_0100); +1406 assert!(dedup.insert(4)); +1407 assert!(!dedup.insert(3)); +1408 assert_eq!(dedup.next, 8); +1409 assert_eq!(dedup.window, 0b1111_1100); +1410 assert!(!dedup.insert(6)); +1411 assert_eq!(dedup.next, 8); +1412 assert_eq!(dedup.window, 0b1111_1101); +1413 assert!(!dedup.insert(5)); +1414 assert_eq!(dedup.next, 8); +1415 assert_eq!(dedup.window, 0b1111_1111); +1416 } 1417 -1418 dedup.insert(3); -1419 assert!(dedup.missing_in_interval(1, 3)); -1420 -1421 dedup.insert(4); -1422 assert!(!dedup.missing_in_interval(3, 4)); -1423 assert!(dedup.missing_in_interval(0, 4)); -1424 -1425 dedup.insert(2); -1426 assert!(!dedup.missing_in_interval(0, 4)); +1418 #[test] +1419 fn happypath() { +1420 let mut dedup = Dedup::new(); +1421 for i in 0..(2 * WINDOW_SIZE) { +1422 assert!(!dedup.insert(i)); +1423 for j in 0..=i { +1424 assert!(dedup.insert(j)); +1425 } +1426 } 1427 } 1428 1429 #[test] -1430 fn dedup_outside_of_window_has_missing() { +1430 fn jump() { 1431 let mut dedup = Dedup::new(); -1432 -1433 for i in 0..140 { -1434 dedup.insert(i); -1435 } -1436 -1437 // 0 and 4 are outside of the window -1438 assert!(!dedup.missing_in_interval(0, 4)); -1439 dedup.insert(160); -1440 assert!(!dedup.missing_in_interval(0, 4)); -1441 assert!(!dedup.missing_in_interval(0, 140)); -1442 assert!(dedup.missing_in_interval(0, 160)); -1443 } +1432 dedup.insert(2 * WINDOW_SIZE); +1433 assert!(dedup.insert(WINDOW_SIZE)); +1434 assert_eq!(dedup.next, 2 * WINDOW_SIZE + 1); +1435 assert_eq!(dedup.window, 0); +1436 assert!(!dedup.insert(WINDOW_SIZE + 1)); +1437 assert_eq!(dedup.next, 2 * WINDOW_SIZE + 1); +1438 assert_eq!(dedup.window, 1 << (WINDOW_SIZE - 2)); +1439 } +1440 +1441 #[test] +1442 fn dedup_has_missing() { +1443 let mut dedup = Dedup::new(); 1444 -1445 #[test] -1446 fn dedup_smallest_missing() { -1447 let mut dedup = Dedup::new(); -1448 -1449 dedup.insert(0); -1450 assert_eq!(dedup.smallest_missing_in_interval(0, 0), None); -1451 -1452 dedup.insert(1); -1453 assert_eq!(dedup.smallest_missing_in_interval(0, 1), None); -1454 -1455 dedup.insert(5); -1456 dedup.insert(7); -1457 assert_eq!(dedup.smallest_missing_in_interval(0, 7), Some(2)); -1458 assert_eq!(dedup.smallest_missing_in_interval(5, 7), Some(6)); -1459 -1460 dedup.insert(2); -1461 assert_eq!(dedup.smallest_missing_in_interval(1, 7), Some(3)); -1462 -1463 dedup.insert(170); -1464 dedup.insert(172); -1465 dedup.insert(300); -1466 assert_eq!(dedup.smallest_missing_in_interval(170, 172), None); -1467 -1468 dedup.insert(500); -1469 assert_eq!(dedup.smallest_missing_in_interval(0, 500), Some(372)); -1470 assert_eq!(dedup.smallest_missing_in_interval(0, 373), Some(372)); -1471 assert_eq!(dedup.smallest_missing_in_interval(0, 372), None); -1472 } -1473 -1474 #[test] -1475 fn pending_acks_first_packet_is_not_considered_reordered() { -1476 let mut acks = PendingAcks::new(); -1477 let mut dedup = Dedup::new(); -1478 dedup.insert(0); -1479 acks.packet_received(Instant::now(), 0, true, &dedup); -1480 assert!(!acks.immediate_ack_required); -1481 } -1482 -1483 #[test] -1484 fn pending_acks_after_immediate_ack_set() { -1485 let mut acks = PendingAcks::new(); -1486 let mut dedup = Dedup::new(); +1445 dedup.insert(0); +1446 assert!(!dedup.missing_in_interval(0, 0)); +1447 +1448 dedup.insert(1); +1449 assert!(!dedup.missing_in_interval(0, 1)); +1450 +1451 dedup.insert(3); +1452 assert!(dedup.missing_in_interval(1, 3)); +1453 +1454 dedup.insert(4); +1455 assert!(!dedup.missing_in_interval(3, 4)); +1456 assert!(dedup.missing_in_interval(0, 4)); +1457 +1458 dedup.insert(2); +1459 assert!(!dedup.missing_in_interval(0, 4)); +1460 } +1461 +1462 #[test] +1463 fn dedup_outside_of_window_has_missing() { +1464 let mut dedup = Dedup::new(); +1465 +1466 for i in 0..140 { +1467 dedup.insert(i); +1468 } +1469 +1470 // 0 and 4 are outside of the window +1471 assert!(!dedup.missing_in_interval(0, 4)); +1472 dedup.insert(160); +1473 assert!(!dedup.missing_in_interval(0, 4)); +1474 assert!(!dedup.missing_in_interval(0, 140)); +1475 assert!(dedup.missing_in_interval(0, 160)); +1476 } +1477 +1478 #[test] +1479 fn dedup_smallest_missing() { +1480 let mut dedup = Dedup::new(); +1481 +1482 dedup.insert(0); +1483 assert_eq!(dedup.smallest_missing_in_interval(0, 0), None); +1484 +1485 dedup.insert(1); +1486 assert_eq!(dedup.smallest_missing_in_interval(0, 1), None); 1487 -1488 // Receive ack-eliciting packet -1489 dedup.insert(0); -1490 let now = Instant::now(); -1491 acks.insert_one(0, now); -1492 acks.packet_received(now, 0, true, &dedup); -1493 -1494 // Sanity check -1495 assert!(!acks.ranges.is_empty()); -1496 assert!(!acks.can_send()); -1497 -1498 // Can send ACK after max_ack_delay exceeded -1499 acks.set_immediate_ack_required(); -1500 assert!(acks.can_send()); -1501 } -1502 -1503 #[test] -1504 fn pending_acks_ack_delay() { -1505 let mut acks = PendingAcks::new(); -1506 let mut dedup = Dedup::new(); -1507 -1508 let t1 = Instant::now(); -1509 let t2 = t1 + Duration::from_millis(2); -1510 let t3 = t2 + Duration::from_millis(5); -1511 assert_eq!(acks.ack_delay(t1), Duration::from_millis(0)); -1512 assert_eq!(acks.ack_delay(t2), Duration::from_millis(0)); -1513 assert_eq!(acks.ack_delay(t3), Duration::from_millis(0)); -1514 -1515 // In-order packet -1516 dedup.insert(0); -1517 acks.insert_one(0, t1); -1518 acks.packet_received(t1, 0, true, &dedup); -1519 assert_eq!(acks.ack_delay(t1), Duration::from_millis(0)); -1520 assert_eq!(acks.ack_delay(t2), Duration::from_millis(2)); -1521 assert_eq!(acks.ack_delay(t3), Duration::from_millis(7)); -1522 -1523 // Out of order (higher than expected) -1524 dedup.insert(3); -1525 acks.insert_one(3, t2); -1526 acks.packet_received(t2, 3, true, &dedup); -1527 assert_eq!(acks.ack_delay(t2), Duration::from_millis(0)); -1528 assert_eq!(acks.ack_delay(t3), Duration::from_millis(5)); -1529 -1530 // Out of order (lower than expected, so previous instant is kept) -1531 dedup.insert(2); -1532 acks.insert_one(2, t3); -1533 acks.packet_received(t3, 2, true, &dedup); -1534 assert_eq!(acks.ack_delay(t3), Duration::from_millis(5)); -1535 } -1536 -1537 #[test] -1538 fn sent_packet_size() { -1539 // The tracking state of sent packets should be minimal, and not grow -1540 // over time. -1541 assert!(std::mem::size_of::<SentPacket>() <= 128); -1542 } -1543 -1544 #[test] -1545 fn pending_new_cids() { -1546 #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))] -1547 use aws_lc_rs::hmac; -1548 #[cfg(feature = "ring")] -1549 use ring::hmac; -1550 -1551 let mut cid_generator = RandomConnectionIdGenerator::new(8); -1552 let mut reset_key = [0; 64]; -1553 rand::rng().fill_bytes(&mut reset_key); -1554 let hmac = hmac::Key::new(hmac::HMAC_SHA256, &reset_key); +1488 dedup.insert(5); +1489 dedup.insert(7); +1490 assert_eq!(dedup.smallest_missing_in_interval(0, 7), Some(2)); +1491 assert_eq!(dedup.smallest_missing_in_interval(5, 7), Some(6)); +1492 +1493 dedup.insert(2); +1494 assert_eq!(dedup.smallest_missing_in_interval(1, 7), Some(3)); +1495 +1496 dedup.insert(170); +1497 dedup.insert(172); +1498 dedup.insert(300); +1499 assert_eq!(dedup.smallest_missing_in_interval(170, 172), None); +1500 +1501 dedup.insert(500); +1502 assert_eq!(dedup.smallest_missing_in_interval(0, 500), Some(372)); +1503 assert_eq!(dedup.smallest_missing_in_interval(0, 373), Some(372)); +1504 assert_eq!(dedup.smallest_missing_in_interval(0, 372), None); +1505 } +1506 +1507 #[test] +1508 fn pending_acks_first_packet_is_not_considered_reordered() { +1509 let mut acks = PendingAcks::new(); +1510 let mut dedup = Dedup::new(); +1511 dedup.insert(0); +1512 acks.packet_received(Instant::now(), 0, true, &dedup); +1513 assert!(!acks.immediate_ack_required); +1514 } +1515 +1516 #[test] +1517 fn pending_acks_after_immediate_ack_set() { +1518 let mut acks = PendingAcks::new(); +1519 let mut dedup = Dedup::new(); +1520 +1521 // Receive ack-eliciting packet +1522 dedup.insert(0); +1523 let now = Instant::now(); +1524 acks.insert_one(0, now); +1525 acks.packet_received(now, 0, true, &dedup); +1526 +1527 // Sanity check +1528 assert!(!acks.ranges.is_empty()); +1529 assert!(!acks.can_send()); +1530 +1531 // Can send ACK after max_ack_delay exceeded +1532 acks.set_immediate_ack_required(); +1533 assert!(acks.can_send()); +1534 } +1535 +1536 #[test] +1537 fn pending_acks_ack_delay() { +1538 let mut acks = PendingAcks::new(); +1539 let mut dedup = Dedup::new(); +1540 +1541 let t1 = Instant::now(); +1542 let t2 = t1 + Duration::from_millis(2); +1543 let t3 = t2 + Duration::from_millis(5); +1544 assert_eq!(acks.ack_delay(t1), Duration::from_millis(0)); +1545 assert_eq!(acks.ack_delay(t2), Duration::from_millis(0)); +1546 assert_eq!(acks.ack_delay(t3), Duration::from_millis(0)); +1547 +1548 // In-order packet +1549 dedup.insert(0); +1550 acks.insert_one(0, t1); +1551 acks.packet_received(t1, 0, true, &dedup); +1552 assert_eq!(acks.ack_delay(t1), Duration::from_millis(0)); +1553 assert_eq!(acks.ack_delay(t2), Duration::from_millis(2)); +1554 assert_eq!(acks.ack_delay(t3), Duration::from_millis(7)); 1555 -1556 let cid_a = cid_generator.generate_cid(); -1557 let a = IssuedCid { -1558 path_id: PathId::ZERO, -1559 sequence: 1, -1560 id: cid_a, -1561 reset_token: ResetToken::new(&hmac, cid_a), -1562 }; -1563 let cid_b = cid_generator.generate_cid(); -1564 let b = IssuedCid { -1565 path_id: PathId::ZERO, -1566 sequence: 2, -1567 id: cid_b, -1568 reset_token: ResetToken::new(&hmac, cid_b), -1569 }; -1570 let cid_c = cid_generator.generate_cid(); -1571 let c = IssuedCid { -1572 path_id: PathId(1), -1573 sequence: 1, -1574 id: cid_c, -1575 reset_token: ResetToken::new(&hmac, cid_c), -1576 }; -1577 -1578 let mut pending_cids = PendingNewCids::default(); -1579 -1580 for _ in 0..9 { -1581 // Push CIDs in a random order -1582 let mut input = vec![a, b, c]; -1583 input.shuffle(&mut rand::rng()); -1584 for cid in input { -1585 pending_cids.push(cid); -1586 } -1587 -1588 // Pop order is always the same -1589 assert_eq!(pending_cids.pop().map(|i| i.id), Some(a.id)); -1590 assert_eq!(pending_cids.pop().map(|i| i.id), Some(b.id)); -1591 assert_eq!(pending_cids.pop().map(|i| i.id), Some(c.id)); -1592 assert!(pending_cids.pop().is_none()); -1593 } -1594 } -1595} \ No newline at end of file +1556 // Out of order (higher than expected) +1557 dedup.insert(3); +1558 acks.insert_one(3, t2); +1559 acks.packet_received(t2, 3, true, &dedup); +1560 assert_eq!(acks.ack_delay(t2), Duration::from_millis(0)); +1561 assert_eq!(acks.ack_delay(t3), Duration::from_millis(5)); +1562 +1563 // Out of order (lower than expected, so previous instant is kept) +1564 dedup.insert(2); +1565 acks.insert_one(2, t3); +1566 acks.packet_received(t3, 2, true, &dedup); +1567 assert_eq!(acks.ack_delay(t3), Duration::from_millis(5)); +1568 } +1569 +1570 #[test] +1571 fn sent_packet_size() { +1572 // The tracking state of sent packets should be minimal, and not grow +1573 // over time. +1574 assert!(std::mem::size_of::<SentPacket>() <= 128); +1575 } +1576 +1577 #[test] +1578 fn pending_new_cids() { +1579 #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))] +1580 use aws_lc_rs::hmac; +1581 #[cfg(feature = "ring")] +1582 use ring::hmac; +1583 +1584 let mut cid_generator = RandomConnectionIdGenerator::new(8); +1585 let mut reset_key = [0; 64]; +1586 rand::rng().fill_bytes(&mut reset_key); +1587 let hmac = hmac::Key::new(hmac::HMAC_SHA256, &reset_key); +1588 +1589 let cid_a = cid_generator.generate_cid(); +1590 let a = IssuedCid { +1591 path_id: PathId::ZERO, +1592 sequence: 1, +1593 id: cid_a, +1594 reset_token: ResetToken::new(&hmac, cid_a), +1595 }; +1596 let cid_b = cid_generator.generate_cid(); +1597 let b = IssuedCid { +1598 path_id: PathId::ZERO, +1599 sequence: 2, +1600 id: cid_b, +1601 reset_token: ResetToken::new(&hmac, cid_b), +1602 }; +1603 let cid_c = cid_generator.generate_cid(); +1604 let c = IssuedCid { +1605 path_id: PathId(1), +1606 sequence: 1, +1607 id: cid_c, +1608 reset_token: ResetToken::new(&hmac, cid_c), +1609 }; +1610 +1611 let mut pending_cids = PendingNewCids::default(); +1612 +1613 for _ in 0..9 { +1614 // Push CIDs in a random order +1615 let mut input = vec![a, b, c]; +1616 input.shuffle(&mut rand::rng()); +1617 for cid in input { +1618 pending_cids.push(cid); +1619 } +1620 +1621 // Pop order is always the same +1622 assert_eq!(pending_cids.pop().map(|i| i.id), Some(a.id)); +1623 assert_eq!(pending_cids.pop().map(|i| i.id), Some(b.id)); +1624 assert_eq!(pending_cids.pop().map(|i| i.id), Some(c.id)); +1625 assert!(pending_cids.pop().is_none()); +1626 } +1627 } +1628} \ No newline at end of file