diff --git a/pr/295/docs/iroh_quinn/enum.ConnectionError.html b/pr/295/docs/iroh_quinn/enum.ConnectionError.html index 9ff772315..d85acc4fc 100644 --- a/pr/295/docs/iroh_quinn/enum.ConnectionError.html +++ b/pr/295/docs/iroh_quinn/enum.ConnectionError.html @@ -1,4 +1,4 @@ -ConnectionError in iroh_quinn - Rust

ConnectionError

Enum ConnectionError 

Source
pub enum ConnectionError {
+ConnectionError in iroh_quinn - 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/295/docs/iroh_quinn/struct.ApplicationClose.html b/pr/295/docs/iroh_quinn/struct.ApplicationClose.html index f87d04551..f57dfa86a 100644 --- a/pr/295/docs/iroh_quinn/struct.ApplicationClose.html +++ b/pr/295/docs/iroh_quinn/struct.ApplicationClose.html @@ -1,11 +1,11 @@ -ApplicationClose in iroh_quinn - Rust

ApplicationClose

Struct ApplicationClose 

Source
pub struct ApplicationClose {
+ApplicationClose in iroh_quinn - Rust

ApplicationClose

Struct ApplicationClose 

Source
pub struct ApplicationClose {
     pub error_code: VarInt,
     pub reason: Bytes,
 }
Expand description

Reason given by an application for closing the connection

Fields§

§error_code: VarInt

Application-specific reason code

§reason: Bytes

Human-readable reason for the close

-

Trait Implementations§

Source§

impl Clone for ApplicationClose

Source§

fn clone(&self) -> ApplicationClose

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 ApplicationClose

Source§

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

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

impl Display for ApplicationClose

Source§

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

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

impl PartialEq for ApplicationClose

Source§

fn eq(&self, other: &ApplicationClose) -> 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 ApplicationClose

Source§

impl StructuralPartialEq for ApplicationClose

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for ApplicationClose

Source§

fn clone(&self) -> ApplicationClose

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 ApplicationClose

Source§

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

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

impl Display for ApplicationClose

Source§

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

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

impl PartialEq for ApplicationClose

Source§

fn eq(&self, other: &ApplicationClose) -> 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 ApplicationClose

Source§

impl StructuralPartialEq for ApplicationClose

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/295/docs/iroh_quinn/struct.ConnectionClose.html b/pr/295/docs/iroh_quinn/struct.ConnectionClose.html index ec3539042..44a74f3ce 100644 --- a/pr/295/docs/iroh_quinn/struct.ConnectionClose.html +++ b/pr/295/docs/iroh_quinn/struct.ConnectionClose.html @@ -1,4 +1,4 @@ -ConnectionClose in iroh_quinn - Rust

ConnectionClose

Struct ConnectionClose 

Source
pub struct ConnectionClose {
+ConnectionClose in iroh_quinn - Rust

ConnectionClose

Struct ConnectionClose 

Source
pub struct ConnectionClose {
     pub error_code: Code,
     pub frame_type: MaybeFrame,
     pub reason: Bytes,
@@ -6,8 +6,8 @@
 

Fields§

§error_code: Code

Class of error as encoded in the specification

§frame_type: MaybeFrame

Type of frame that caused the close

§reason: Bytes

Human-readable reason for the close

-

Trait Implementations§

Source§

impl Clone for ConnectionClose

Source§

fn clone(&self) -> ConnectionClose

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 ConnectionClose

Source§

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

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

impl Display for ConnectionClose

Source§

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

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

impl From<Error> for ConnectionClose

Source§

fn from(x: Error) -> ConnectionClose

Converts to this type from the input type.
Source§

impl PartialEq for ConnectionClose

Source§

fn eq(&self, other: &ConnectionClose) -> 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 ConnectionClose

Source§

impl StructuralPartialEq for ConnectionClose

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for ConnectionClose

Source§

fn clone(&self) -> ConnectionClose

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 ConnectionClose

Source§

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

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

impl Display for ConnectionClose

Source§

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

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

impl From<Error> for ConnectionClose

Source§

fn from(x: Error) -> ConnectionClose

Converts to this type from the input type.
Source§

impl PartialEq for ConnectionClose

Source§

fn eq(&self, other: &ConnectionClose) -> 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 ConnectionClose

Source§

impl StructuralPartialEq for ConnectionClose

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/295/docs/iroh_quinn_proto/coding/trait.Encodable.html b/pr/295/docs/iroh_quinn_proto/coding/trait.Encodable.html index 74ac372b5..3acc05ca8 100644 --- a/pr/295/docs/iroh_quinn_proto/coding/trait.Encodable.html +++ b/pr/295/docs/iroh_quinn_proto/coding/trait.Encodable.html @@ -3,4 +3,4 @@ fn encode<B: BufMut>(&self, buf: &mut B); }

Expand description

Infallible encoding of QUIC primitives.

Required Methods§

Source

fn encode<B: BufMut>(&self, buf: &mut B)

Append the encoding of self to the provided buffer.

-

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementations on Foreign Types§

Source§

impl Encodable for u8

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for u16

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for u32

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for u64

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for Ipv4Addr

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for Ipv6Addr

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Implementors§

\ No newline at end of file +

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementations on Foreign Types§

Source§

impl Encodable for u8

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for u16

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for u32

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for u64

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for Ipv4Addr

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for Ipv6Addr

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Implementors§

\ No newline at end of file diff --git a/pr/295/docs/iroh_quinn_proto/enum.ClosePathError.html b/pr/295/docs/iroh_quinn_proto/enum.ClosePathError.html index 4266d8ea8..65095e8dc 100644 --- a/pr/295/docs/iroh_quinn_proto/enum.ClosePathError.html +++ b/pr/295/docs/iroh_quinn_proto/enum.ClosePathError.html @@ -1,11 +1,11 @@ -ClosePathError in iroh_quinn_proto - Rust

ClosePathError

Enum ClosePathError 

Source
pub enum ClosePathError {
+ClosePathError in iroh_quinn_proto - Rust

ClosePathError

Enum ClosePathError 

Source
pub enum ClosePathError {
     ClosedPath,
     LastOpenPath,
 }
Expand description

Errors triggered when abandoning a path

Variants§

§

ClosedPath

The path is already closed or was never opened

§

LastOpenPath

This is the last path, which can not be abandoned

-

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/295/docs/iroh_quinn_proto/enum.ConnectionError.html b/pr/295/docs/iroh_quinn_proto/enum.ConnectionError.html index 2cdd8b558..8b3f4b576 100644 --- a/pr/295/docs/iroh_quinn_proto/enum.ConnectionError.html +++ b/pr/295/docs/iroh_quinn_proto/enum.ConnectionError.html @@ -1,4 +1,4 @@ -ConnectionError in iroh_quinn_proto - Rust

ConnectionError

Enum ConnectionError 

Source
pub enum ConnectionError {
+ConnectionError in iroh_quinn_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/295/docs/iroh_quinn_proto/enum.Event.html b/pr/295/docs/iroh_quinn_proto/enum.Event.html index a99a63af0..cc6a371d4 100644 --- a/pr/295/docs/iroh_quinn_proto/enum.Event.html +++ b/pr/295/docs/iroh_quinn_proto/enum.Event.html @@ -1,4 +1,4 @@ -Event in iroh_quinn_proto - Rust

Event

Enum Event 

Source
pub enum Event {
+Event in iroh_quinn_proto - Rust

Event

Enum Event 

Source
pub enum Event {
     HandshakeDataReady,
     Connected,
     HandshakeConfirmed,
@@ -22,7 +22,7 @@
 
§

DatagramsUnblocked

One or more application datagrams have been sent after blocking

§

Path(PathEvent)

(Multi)Path events

§

NatTraversal(Event)

Iroh’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/295/docs/iroh_quinn_proto/enum.PathError.html b/pr/295/docs/iroh_quinn_proto/enum.PathError.html index 52e07c2a6..835784200 100644 --- a/pr/295/docs/iroh_quinn_proto/enum.PathError.html +++ b/pr/295/docs/iroh_quinn_proto/enum.PathError.html @@ -1,4 +1,4 @@ -PathError in iroh_quinn_proto - Rust

PathError

Enum PathError 

Source
pub enum PathError {
+PathError in iroh_quinn_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/295/docs/iroh_quinn_proto/enum.PathEvent.html b/pr/295/docs/iroh_quinn_proto/enum.PathEvent.html index 752fd9c69..8729669be 100644 --- a/pr/295/docs/iroh_quinn_proto/enum.PathEvent.html +++ b/pr/295/docs/iroh_quinn_proto/enum.PathEvent.html @@ -47,7 +47,7 @@ changes the status.

Fields

§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, +

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 diff --git a/pr/295/docs/iroh_quinn_proto/enum.SendDatagramError.html b/pr/295/docs/iroh_quinn_proto/enum.SendDatagramError.html index 84ec6fc06..cb460b1ab 100644 --- a/pr/295/docs/iroh_quinn_proto/enum.SendDatagramError.html +++ b/pr/295/docs/iroh_quinn_proto/enum.SendDatagramError.html @@ -1,4 +1,4 @@ -SendDatagramError in iroh_quinn_proto - Rust

SendDatagramError

Enum SendDatagramError 

Source
pub enum SendDatagramError {
+SendDatagramError in iroh_quinn_proto - Rust

SendDatagramError

Enum SendDatagramError 

Source
pub enum SendDatagramError {
     UnsupportedByPeer,
     Disabled,
     TooLarge,
@@ -10,16 +10,16 @@
 

Indicates that the path MTU minus overhead or the limit advertised by the peer has been exceeded.

§

Blocked(Bytes)

Send would block

-

Trait Implementations§

Source§

impl Clone for SendDatagramError

Source§

fn clone(&self) -> SendDatagramError

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 SendDatagramError

Source§

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

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

impl Display for SendDatagramError

Source§

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

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

impl Error for SendDatagramError

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 Hash for SendDatagramError

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where +

Trait Implementations§

Source§

impl Clone for SendDatagramError

Source§

fn clone(&self) -> SendDatagramError

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 SendDatagramError

Source§

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

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

impl Display for SendDatagramError

Source§

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

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

impl Error for SendDatagramError

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 Hash for SendDatagramError

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, - Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for SendDatagramError

Source§

fn cmp(&self, other: &SendDatagramError) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for SendDatagramError

Source§

fn cmp(&self, other: &SendDatagramError) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where - Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for SendDatagramError

Source§

fn eq(&self, other: &SendDatagramError) -> 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 PartialOrd for SendDatagramError

Source§

fn partial_cmp(&self, other: &SendDatagramError) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

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

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

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

Tests less than or equal to (for self and other) and is used by the + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for SendDatagramError

Source§

fn eq(&self, other: &SendDatagramError) -> 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 PartialOrd for SendDatagramError

Source§

fn partial_cmp(&self, other: &SendDatagramError) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

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

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

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

Tests less than or equal to (for self and other) and is used by the <= operator. Read more
1.0.0 · Source§

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

Tests greater than (for self and other) and is used by the > operator. Read more
1.0.0 · Source§

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

Tests greater than or equal to (for self and other) and is used by -the >= operator. Read more
Source§

impl Eq for SendDatagramError

Source§

impl StructuralPartialEq for SendDatagramError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +the >= operator. Read more

Source§

impl Eq for SendDatagramError

Source§

impl StructuralPartialEq for SendDatagramError

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/295/docs/iroh_quinn_proto/struct.ApplicationClose.html b/pr/295/docs/iroh_quinn_proto/struct.ApplicationClose.html index 8ef7f64a4..4686aa3e6 100644 --- a/pr/295/docs/iroh_quinn_proto/struct.ApplicationClose.html +++ b/pr/295/docs/iroh_quinn_proto/struct.ApplicationClose.html @@ -1,11 +1,11 @@ -ApplicationClose in iroh_quinn_proto - Rust

ApplicationClose

Struct ApplicationClose 

Source
pub struct ApplicationClose {
+ApplicationClose in iroh_quinn_proto - Rust

ApplicationClose

Struct ApplicationClose 

Source
pub struct ApplicationClose {
     pub error_code: VarInt,
     pub reason: Bytes,
 }
Expand description

Reason given by an application for closing the connection

Fields§

§error_code: VarInt

Application-specific reason code

§reason: Bytes

Human-readable reason for the close

-

Trait Implementations§

Source§

impl Clone for ApplicationClose

Source§

fn clone(&self) -> ApplicationClose

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 ApplicationClose

Source§

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

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

impl Display for ApplicationClose

Source§

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

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

impl PartialEq for ApplicationClose

Source§

fn eq(&self, other: &ApplicationClose) -> 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 ApplicationClose

Source§

impl StructuralPartialEq for ApplicationClose

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for ApplicationClose

Source§

fn clone(&self) -> ApplicationClose

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 ApplicationClose

Source§

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

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

impl Display for ApplicationClose

Source§

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

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

impl PartialEq for ApplicationClose

Source§

fn eq(&self, other: &ApplicationClose) -> 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 ApplicationClose

Source§

impl StructuralPartialEq for ApplicationClose

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/295/docs/iroh_quinn_proto/struct.Connection.html b/pr/295/docs/iroh_quinn_proto/struct.Connection.html index c1f70a438..c6284846f 100644 --- a/pr/295/docs/iroh_quinn_proto/struct.Connection.html +++ b/pr/295/docs/iroh_quinn_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
  • @@ -107,7 +107,7 @@ for a path that was never opened locally.

    ) -> 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, @@ -121,60 +121,60 @@ for a path that was never opened locally.

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 message respectively.

-
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.

A ConnectionLost event is emitted with details when the connection becomes closed.

-
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, @@ -185,36 +185,36 @@ 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 local_address_changed(&mut self)

Handle a change in the local address, i.e. an active migration

-
Source

pub fn current_mtu(&self) -> u16

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

Source

pub fn local_address_changed(&mut self)

Handle a change in the local address, i.e. an active migration

+
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

@@ -224,7 +224,7 @@ initiated, the previous one is cancelled, and paths that have not been opened ar

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/295/docs/iroh_quinn_proto/struct.ConnectionClose.html b/pr/295/docs/iroh_quinn_proto/struct.ConnectionClose.html index 364cae822..a698c9b87 100644 --- a/pr/295/docs/iroh_quinn_proto/struct.ConnectionClose.html +++ b/pr/295/docs/iroh_quinn_proto/struct.ConnectionClose.html @@ -1,4 +1,4 @@ -ConnectionClose in iroh_quinn_proto - Rust

ConnectionClose

Struct ConnectionClose 

Source
pub struct ConnectionClose {
+ConnectionClose in iroh_quinn_proto - Rust

ConnectionClose

Struct ConnectionClose 

Source
pub struct ConnectionClose {
     pub error_code: TransportErrorCode,
     pub frame_type: MaybeFrame,
     pub reason: Bytes,
@@ -6,8 +6,8 @@
 

Fields§

§error_code: TransportErrorCode

Class of error as encoded in the specification

§frame_type: MaybeFrame

Type of frame that caused the close

§reason: Bytes

Human-readable reason for the close

-

Trait Implementations§

Source§

impl Clone for ConnectionClose

Source§

fn clone(&self) -> ConnectionClose

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 ConnectionClose

Source§

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

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

impl Display for ConnectionClose

Source§

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

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

impl From<Error> for ConnectionClose

Source§

fn from(x: TransportError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ConnectionClose

Source§

fn eq(&self, other: &ConnectionClose) -> 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 ConnectionClose

Source§

impl StructuralPartialEq for ConnectionClose

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for ConnectionClose

Source§

fn clone(&self) -> ConnectionClose

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 ConnectionClose

Source§

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

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

impl Display for ConnectionClose

Source§

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

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

impl From<Error> for ConnectionClose

Source§

fn from(x: TransportError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ConnectionClose

Source§

fn eq(&self, other: &ConnectionClose) -> 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 ConnectionClose

Source§

impl StructuralPartialEq for ConnectionClose

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/295/docs/iroh_quinn_proto/struct.Datagram.html b/pr/295/docs/iroh_quinn_proto/struct.Datagram.html index 0a08d9acc..ab2a3812b 100644 --- a/pr/295/docs/iroh_quinn_proto/struct.Datagram.html +++ b/pr/295/docs/iroh_quinn_proto/struct.Datagram.html @@ -1,8 +1,8 @@ -Datagram in iroh_quinn_proto - Rust

Datagram

Struct Datagram 

Source
pub struct Datagram {
+Datagram in iroh_quinn_proto - Rust

Datagram

Struct Datagram 

Source
pub struct Datagram {
     pub data: Bytes,
 }
Expand description

An unreliable datagram

Fields§

§data: Bytes

Payload

-

Trait Implementations§

Source§

impl Clone for Datagram

Source§

fn clone(&self) -> Datagram

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 Datagram

Source§

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

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

impl Encodable for Datagram

Source§

fn encode<B: BufMut>(&self, out: &mut B)

Append the encoding of self to the provided buffer.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for Datagram

Source§

fn clone(&self) -> Datagram

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 Datagram

Source§

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

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

impl Encodable for Datagram

Source§

fn encode<B: BufMut>(&self, out: &mut B)

Append the encoding of self to the provided buffer.

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/295/docs/iroh_quinn_proto/struct.TransportError.html b/pr/295/docs/iroh_quinn_proto/struct.TransportError.html index 6ddcdc8c1..8cd62b851 100644 --- a/pr/295/docs/iroh_quinn_proto/struct.TransportError.html +++ b/pr/295/docs/iroh_quinn_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/295/docs/src/iroh_quinn_proto/connection/datagrams.rs.html b/pr/295/docs/src/iroh_quinn_proto/connection/datagrams.rs.html index 3b252e392..8fa8eda04 100644 --- a/pr/295/docs/src/iroh_quinn_proto/connection/datagrams.rs.html +++ b/pr/295/docs/src/iroh_quinn_proto/connection/datagrams.rs.html @@ -167,53 +167,49 @@ 167 /// 168 /// Returns whether a frame was written. At most `max_size` bytes will be written, including 169 /// framing. -170 pub(super) fn write<'a, 'b>( -171 &mut self, -172 builder: &mut PacketBuilder, -173 stats: &mut FrameStats, -174 ) -> bool { -175 let datagram = match self.outgoing.pop_front() { -176 Some(x) => x, -177 None => return false, -178 }; -179 -180 if builder.frame_space_remaining() < datagram.size(true) { -181 // Future work: we could be more clever about cramming small datagrams into -182 // mostly-full packets when a larger one is queued first -183 self.outgoing.push_front(datagram); -184 return false; -185 } -186 -187 trace!(len = datagram.data.len(), "DATAGRAM"); -188 -189 self.outgoing_total -= datagram.data.len(); -190 builder.encode(datagram, stats); -191 true -192 } -193 -194 pub(super) fn recv(&mut self) -> Option<Bytes> { -195 let x = self.incoming.pop_front()?.data; -196 self.recv_buffered -= x.len(); -197 Some(x) -198 } -199} -200 -201/// Errors that can arise when sending a datagram -202#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -203pub enum SendDatagramError { -204 /// The peer does not support receiving datagram frames -205 #[error("datagrams not supported by peer")] -206 UnsupportedByPeer, -207 /// Datagram support is disabled locally -208 #[error("datagram support disabled")] -209 Disabled, -210 /// The datagram is larger than the connection can currently accommodate -211 /// -212 /// Indicates that the path MTU minus overhead or the limit advertised by the peer has been -213 /// exceeded. -214 #[error("datagram too large")] -215 TooLarge, -216 /// Send would block -217 #[error("datagram send blocked")] -218 Blocked(Bytes), -219}

\ No newline at end of file +170 pub(super) fn write<'a, 'b>(&mut self, buf: &mut PacketBuilder, stat: &mut FrameStats) -> bool { +171 let datagram = match self.outgoing.pop_front() { +172 Some(x) => x, +173 None => return false, +174 }; +175 +176 if buf.frame_space_remaining() < datagram.size(true) { +177 // Future work: we could be more clever about cramming small datagrams into +178 // mostly-full packets when a larger one is queued first +179 self.outgoing.push_front(datagram); +180 return false; +181 } +182 +183 trace!(len = datagram.data.len(), "DATAGRAM"); +184 +185 self.outgoing_total -= datagram.data.len(); +186 buf.encode(datagram, stat); +187 true +188 } +189 +190 pub(super) fn recv(&mut self) -> Option<Bytes> { +191 let x = self.incoming.pop_front()?.data; +192 self.recv_buffered -= x.len(); +193 Some(x) +194 } +195} +196 +197/// Errors that can arise when sending a datagram +198#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +199pub enum SendDatagramError { +200 /// The peer does not support receiving datagram frames +201 #[error("datagrams not supported by peer")] +202 UnsupportedByPeer, +203 /// Datagram support is disabled locally +204 #[error("datagram support disabled")] +205 Disabled, +206 /// The datagram is larger than the connection can currently accommodate +207 /// +208 /// Indicates that the path MTU minus overhead or the limit advertised by the peer has been +209 /// exceeded. +210 #[error("datagram too large")] +211 TooLarge, +212 /// Send would block +213 #[error("datagram send blocked")] +214 Blocked(Bytes), +215} \ No newline at end of file diff --git a/pr/295/docs/src/iroh_quinn_proto/connection/mod.rs.html b/pr/295/docs/src/iroh_quinn_proto/connection/mod.rs.html index 97e68c946..7cfd1401a 100644 --- a/pr/295/docs/src/iroh_quinn_proto/connection/mod.rs.html +++ b/pr/295/docs/src/iroh_quinn_proto/connection/mod.rs.html @@ -1292,5469 +1292,5453 @@ 1292 let stats = &mut self.stats.frame_tx; 1293 if frame::ConnectionClose::SIZE_BOUND < builder.frame_space_remaining() { 1294 let max_frame_size = builder.frame_space_remaining(); -1295 match self.state.as_type() { +1295 let close: Close = match self.state.as_type() { 1296 StateType::Closed => { 1297 let reason: Close = 1298 self.state.as_closed().expect("checked").clone().into(); 1299 if space_id == SpaceId::Data || reason.is_transport_layer() { -1300 builder.encode(reason.encoder(max_frame_size), stats) +1300 reason 1301 } else { -1302 let frame: frame::Close = -1303 TransportError::APPLICATION_ERROR("").into(); -1304 builder.encode(frame.encoder(max_frame_size), stats); -1305 } -1306 } -1307 StateType::Draining => { -1308 let frame: frame::Close = TransportError::NO_ERROR("").into(); -1309 builder.encode(frame.encoder(max_frame_size), stats); -1310 } -1311 _ => unreachable!( -1312 "tried to make a close packet when the connection wasn't closed" -1313 ), -1314 }; -1315 } -1316 builder.finish_and_track(now, self, path_id, pad_datagram); -1317 if space_id == self.highest_space { -1318 // Don't send another close packet. Even with multipath we only send -1319 // CONNECTION_CLOSE on a single path since we expect our paths to work. -1320 self.close = false; -1321 // `CONNECTION_CLOSE` is the final packet -1322 break; -1323 } else { -1324 // Send a close frame in every possible space for robustness, per -1325 // RFC9000 "Immediate Close during the Handshake". Don't bother trying -1326 // to send anything else. -1327 space_id = space_id.next(); -1328 continue; -1329 } -1330 } -1331 -1332 // Send an off-path PATH_RESPONSE. Prioritized over on-path data to ensure that -1333 // path validation can occur while the link is saturated. -1334 if space_id == SpaceId::Data && builder.buf.num_datagrams() == 1 { -1335 let path = self.path_data_mut(path_id); -1336 if let Some((token, network_path)) = -1337 path.path_responses.pop_off_path(path.network_path) -1338 { -1339 // TODO(flub): We need to use the right CID! We shouldn't use the same -1340 // CID as the current active one for the path. Though see also -1341 // https://github.com/quinn-rs/quinn/issues/2184 -1342 let response = frame::PathResponse(token); -1343 trace!(%response, "(off-path)"); -1344 builder.encode(response, &mut self.stats.frame_tx); -1345 builder.finish_and_track(now, self, path_id, PadDatagram::ToMinMtu); -1346 self.stats.udp_tx.on_sent(1, transmit.len()); -1347 return Some(Transmit { -1348 destination: network_path.remote, -1349 size: transmit.len(), -1350 ecn: None, -1351 segment_size: None, -1352 src_ip: network_path.local_ip, -1353 }); -1354 } -1355 } +1302 TransportError::APPLICATION_ERROR("").into() +1303 } +1304 } +1305 StateType::Draining => TransportError::NO_ERROR("").into(), +1306 _ => unreachable!( +1307 "tried to make a close packet when the connection wasn't closed" +1308 ), +1309 }; +1310 builder.encode(close.encoder(max_frame_size), stats); +1311 } +1312 builder.finish_and_track(now, self, path_id, pad_datagram); +1313 if space_id == self.highest_space { +1314 // Don't send another close packet. Even with multipath we only send +1315 // CONNECTION_CLOSE on a single path since we expect our paths to work. +1316 self.close = false; +1317 // `CONNECTION_CLOSE` is the final packet +1318 break; +1319 } else { +1320 // Send a close frame in every possible space for robustness, per +1321 // RFC9000 "Immediate Close during the Handshake". Don't bother trying +1322 // to send anything else. +1323 space_id = space_id.next(); +1324 continue; +1325 } +1326 } +1327 +1328 // Send an off-path PATH_RESPONSE. Prioritized over on-path data to ensure that +1329 // path validation can occur while the link is saturated. +1330 if space_id == SpaceId::Data && builder.buf.num_datagrams() == 1 { +1331 let path = self.path_data_mut(path_id); +1332 if let Some((token, network_path)) = +1333 path.path_responses.pop_off_path(path.network_path) +1334 { +1335 // TODO(flub): We need to use the right CID! We shouldn't use the same +1336 // CID as the current active one for the path. Though see also +1337 // https://github.com/quinn-rs/quinn/issues/2184 +1338 let response = frame::PathResponse(token); +1339 trace!(%response, "(off-path)"); +1340 builder.encode(response, &mut self.stats.frame_tx); +1341 builder.finish_and_track(now, self, path_id, PadDatagram::ToMinMtu); +1342 self.stats.udp_tx.on_sent(1, transmit.len()); +1343 return Some(Transmit { +1344 destination: network_path.remote, +1345 size: transmit.len(), +1346 ecn: None, +1347 segment_size: None, +1348 src_ip: network_path.local_ip, +1349 }); +1350 } +1351 } +1352 +1353 let path_exclusive_only = +1354 have_available_path && self.path_data(path_id).local_status() == PathStatus::Backup; +1355 self.populate_packet(now, space_id, path_id, path_exclusive_only, &mut builder); 1356 -1357 let path_exclusive_only = -1358 have_available_path && self.path_data(path_id).local_status() == PathStatus::Backup; -1359 self.populate_packet(now, space_id, path_id, path_exclusive_only, &mut builder); -1360 -1361 // ACK-only packets should only be sent when explicitly allowed. If we write them due to -1362 // any other reason, there is a bug which leads to one component announcing write -1363 // readiness while not writing any data. This degrades performance. The condition is -1364 // only checked if the full MTU is available and when potentially large fixed-size -1365 // frames aren't queued, so that lack of space in the datagram isn't the reason for just -1366 // writing ACKs. -1367 debug_assert!( -1368 !(builder.sent_frames().is_ack_only(&self.streams) -1369 && !can_send.acks -1370 && can_send.other -1371 && builder.buf.segment_size() -1372 == self.path_data(path_id).current_mtu() as usize -1373 && self.datagrams.outgoing.is_empty()), -1374 "SendableFrames was {can_send:?}, but only ACKs have been written" -1375 ); -1376 if builder.sent_frames().requires_padding { -1377 pad_datagram |= PadDatagram::ToMinMtu; -1378 } -1379 -1380 for (path_id, _pn) in builder.sent_frames().largest_acked.iter() { -1381 self.spaces[space_id] -1382 .for_path(*path_id) -1383 .pending_acks -1384 .acks_sent(); -1385 self.timers.stop( -1386 Timer::PerPath(*path_id, PathTimer::MaxAckDelay), -1387 self.qlog.with_time(now), -1388 ); -1389 } -1390 -1391 // Now we need to finish the packet. Before we do so we need to know if we will -1392 // be coalescing the next packet into this one, or will be ending the datagram -1393 // as well. Because if this is the last packet in the datagram more padding -1394 // might be needed because of the packet type, or to fill the GSO segment size. -1395 -1396 // Are we allowed to coalesce AND is there enough space for another *packet* in -1397 // this datagram AND is there another packet to send in this or the next space? -1398 if coalesce -1399 && builder -1400 .buf -1401 .datagram_remaining_mut() -1402 .saturating_sub(builder.predict_packet_end()) -1403 > MIN_PACKET_SPACE -1404 && self -1405 .next_send_space(space_id, path_id, builder.buf, close) -1406 .is_some() -1407 { -1408 // We can append/coalesce the next packet into the current -1409 // datagram. Finish the current packet without adding extra padding. -1410 builder.finish_and_track(now, self, path_id, PadDatagram::No); -1411 } else { -1412 // We need a new datagram for the next packet. Finish the current -1413 // packet with padding. -1414 if builder.buf.num_datagrams() > 1 && matches!(pad_datagram, PadDatagram::No) { -1415 // If too many padding bytes would be required to continue the -1416 // GSO batch after this packet, end the GSO batch here. Ensures -1417 // that fixed-size frames with heterogeneous sizes -1418 // (e.g. application datagrams) won't inadvertently waste large -1419 // amounts of bandwidth. The exact threshold is a bit arbitrary -1420 // and might benefit from further tuning, though there's no -1421 // universally optimal value. -1422 const MAX_PADDING: usize = 32; -1423 if builder.buf.datagram_remaining_mut() -1424 > builder.predict_packet_end() + MAX_PADDING -1425 { -1426 trace!( -1427 "GSO truncated by demand for {} padding bytes", -1428 builder.buf.datagram_remaining_mut() - builder.predict_packet_end() -1429 ); -1430 builder.finish_and_track(now, self, path_id, PadDatagram::No); -1431 break; -1432 } -1433 -1434 // Pad the current datagram to GSO segment size so it can be -1435 // included in the GSO batch. -1436 builder.finish_and_track(now, self, path_id, PadDatagram::ToSegmentSize); -1437 } else { -1438 builder.finish_and_track(now, self, path_id, pad_datagram); -1439 } -1440 if transmit.num_datagrams() == 1 { -1441 transmit.clip_datagram_size(); -1442 } -1443 } -1444 } -1445 -1446 if let Some(last_packet_number) = last_packet_number { -1447 // Note that when sending in multiple packet spaces the last packet number will -1448 // be the one from the highest packet space. -1449 self.path_data_mut(path_id).congestion.on_sent( -1450 now, -1451 transmit.len() as u64, -1452 last_packet_number, -1453 ); -1454 } -1455 -1456 self.qlog.emit_recovery_metrics( -1457 path_id, -1458 &mut self.paths.get_mut(&path_id).unwrap().data, -1459 now, -1460 ); -1461 -1462 self.app_limited = transmit.is_empty() && !congestion_blocked; -1463 -1464 // Send MTU probe if necessary -1465 if transmit.is_empty() && self.state.is_established() { -1466 // MTU probing happens only in Data space. -1467 let space_id = SpaceId::Data; -1468 path_id = *self.paths.first_key_value().expect("one path must exist").0; -1469 let probe_data = loop { -1470 // We MTU probe all paths for which all of the following is true: -1471 // - We have an active destination CID for the path. -1472 // - The remote address *and* path are validated. -1473 // - The path is not abandoned. -1474 // - The MTU Discovery subsystem wants to probe the path. -1475 let active_cid = self.rem_cids.get(&path_id).map(CidQueue::active); -1476 let eligible = self.path_data(path_id).validated -1477 && !self.path_data(path_id).is_validating_path() -1478 && !self.abandoned_paths.contains(&path_id); -1479 let probe_size = eligible -1480 .then(|| { -1481 let next_pn = self.spaces[space_id].for_path(path_id).peek_tx_number(); -1482 self.path_data_mut(path_id).mtud.poll_transmit(now, next_pn) -1483 }) -1484 .flatten(); -1485 match (active_cid, probe_size) { -1486 (Some(active_cid), Some(probe_size)) => { -1487 // Let's send an MTUD probe! -1488 break Some((active_cid, probe_size)); -1489 } -1490 _ => { -1491 // Find the next path to check if it needs an MTUD probe. -1492 match self.paths.keys().find(|&&next| next > path_id) { -1493 Some(next) => { -1494 path_id = *next; -1495 continue; -1496 } -1497 None => break None, -1498 } -1499 } -1500 } -1501 }; +1357 // ACK-only packets should only be sent when explicitly allowed. If we write them due to +1358 // any other reason, there is a bug which leads to one component announcing write +1359 // readiness while not writing any data. This degrades performance. The condition is +1360 // only checked if the full MTU is available and when potentially large fixed-size +1361 // frames aren't queued, so that lack of space in the datagram isn't the reason for just +1362 // writing ACKs. +1363 debug_assert!( +1364 !(builder.sent_frames().is_ack_only(&self.streams) +1365 && !can_send.acks +1366 && can_send.other +1367 && builder.buf.segment_size() +1368 == self.path_data(path_id).current_mtu() as usize +1369 && self.datagrams.outgoing.is_empty()), +1370 "SendableFrames was {can_send:?}, but only ACKs have been written" +1371 ); +1372 if builder.sent_frames().requires_padding { +1373 pad_datagram |= PadDatagram::ToMinMtu; +1374 } +1375 +1376 for (path_id, _pn) in builder.sent_frames().largest_acked.iter() { +1377 self.spaces[space_id] +1378 .for_path(*path_id) +1379 .pending_acks +1380 .acks_sent(); +1381 self.timers.stop( +1382 Timer::PerPath(*path_id, PathTimer::MaxAckDelay), +1383 self.qlog.with_time(now), +1384 ); +1385 } +1386 +1387 // Now we need to finish the packet. Before we do so we need to know if we will +1388 // be coalescing the next packet into this one, or will be ending the datagram +1389 // as well. Because if this is the last packet in the datagram more padding +1390 // might be needed because of the packet type, or to fill the GSO segment size. +1391 +1392 // Are we allowed to coalesce AND is there enough space for another *packet* in +1393 // this datagram AND is there another packet to send in this or the next space? +1394 if coalesce +1395 && builder +1396 .buf +1397 .datagram_remaining_mut() +1398 .saturating_sub(builder.predict_packet_end()) +1399 > MIN_PACKET_SPACE +1400 && self +1401 .next_send_space(space_id, path_id, builder.buf, close) +1402 .is_some() +1403 { +1404 // We can append/coalesce the next packet into the current +1405 // datagram. Finish the current packet without adding extra padding. +1406 builder.finish_and_track(now, self, path_id, PadDatagram::No); +1407 } else { +1408 // We need a new datagram for the next packet. Finish the current +1409 // packet with padding. +1410 if builder.buf.num_datagrams() > 1 && matches!(pad_datagram, PadDatagram::No) { +1411 // If too many padding bytes would be required to continue the +1412 // GSO batch after this packet, end the GSO batch here. Ensures +1413 // that fixed-size frames with heterogeneous sizes +1414 // (e.g. application datagrams) won't inadvertently waste large +1415 // amounts of bandwidth. The exact threshold is a bit arbitrary +1416 // and might benefit from further tuning, though there's no +1417 // universally optimal value. +1418 const MAX_PADDING: usize = 32; +1419 if builder.buf.datagram_remaining_mut() +1420 > builder.predict_packet_end() + MAX_PADDING +1421 { +1422 trace!( +1423 "GSO truncated by demand for {} padding bytes", +1424 builder.buf.datagram_remaining_mut() - builder.predict_packet_end() +1425 ); +1426 builder.finish_and_track(now, self, path_id, PadDatagram::No); +1427 break; +1428 } +1429 +1430 // Pad the current datagram to GSO segment size so it can be +1431 // included in the GSO batch. +1432 builder.finish_and_track(now, self, path_id, PadDatagram::ToSegmentSize); +1433 } else { +1434 builder.finish_and_track(now, self, path_id, pad_datagram); +1435 } +1436 if transmit.num_datagrams() == 1 { +1437 transmit.clip_datagram_size(); +1438 } +1439 } +1440 } +1441 +1442 if let Some(last_packet_number) = last_packet_number { +1443 // Note that when sending in multiple packet spaces the last packet number will +1444 // be the one from the highest packet space. +1445 self.path_data_mut(path_id).congestion.on_sent( +1446 now, +1447 transmit.len() as u64, +1448 last_packet_number, +1449 ); +1450 } +1451 +1452 self.qlog.emit_recovery_metrics( +1453 path_id, +1454 &mut self.paths.get_mut(&path_id).unwrap().data, +1455 now, +1456 ); +1457 +1458 self.app_limited = transmit.is_empty() && !congestion_blocked; +1459 +1460 // Send MTU probe if necessary +1461 if transmit.is_empty() && self.state.is_established() { +1462 // MTU probing happens only in Data space. +1463 let space_id = SpaceId::Data; +1464 path_id = *self.paths.first_key_value().expect("one path must exist").0; +1465 let probe_data = loop { +1466 // We MTU probe all paths for which all of the following is true: +1467 // - We have an active destination CID for the path. +1468 // - The remote address *and* path are validated. +1469 // - The path is not abandoned. +1470 // - The MTU Discovery subsystem wants to probe the path. +1471 let active_cid = self.rem_cids.get(&path_id).map(CidQueue::active); +1472 let eligible = self.path_data(path_id).validated +1473 && !self.path_data(path_id).is_validating_path() +1474 && !self.abandoned_paths.contains(&path_id); +1475 let probe_size = eligible +1476 .then(|| { +1477 let next_pn = self.spaces[space_id].for_path(path_id).peek_tx_number(); +1478 self.path_data_mut(path_id).mtud.poll_transmit(now, next_pn) +1479 }) +1480 .flatten(); +1481 match (active_cid, probe_size) { +1482 (Some(active_cid), Some(probe_size)) => { +1483 // Let's send an MTUD probe! +1484 break Some((active_cid, probe_size)); +1485 } +1486 _ => { +1487 // Find the next path to check if it needs an MTUD probe. +1488 match self.paths.keys().find(|&&next| next > path_id) { +1489 Some(next) => { +1490 path_id = *next; +1491 continue; +1492 } +1493 None => break None, +1494 } +1495 } +1496 } +1497 }; +1498 if let Some((active_cid, probe_size)) = probe_data { +1499 // We are definitely sending a DPLPMTUD probe. +1500 debug_assert_eq!(transmit.num_datagrams(), 0); +1501 transmit.start_new_datagram_with_size(probe_size as usize); 1502 -1503 if let Some((active_cid, probe_size)) = probe_data { -1504 // We are definitely sending a DPLPMTUD probe. -1505 debug_assert_eq!(transmit.num_datagrams(), 0); -1506 transmit.start_new_datagram_with_size(probe_size as usize); -1507 -1508 let mut builder = PacketBuilder::new( -1509 now, -1510 space_id, -1511 path_id, -1512 active_cid, -1513 &mut transmit, -1514 true, -1515 self, -1516 )?; +1503 let mut builder = PacketBuilder::new( +1504 now, +1505 space_id, +1506 path_id, +1507 active_cid, +1508 &mut transmit, +1509 true, +1510 self, +1511 )?; +1512 +1513 // We implement MTU probes as ping packets padded up to the probe size +1514 trace!(?probe_size, "writing MTUD probe"); +1515 trace!("PING"); +1516 builder.encode(frame::Ping, &mut self.stats.frame_tx); 1517 -1518 // We implement MTU probes as ping packets padded up to the probe size -1519 trace!(?probe_size, "writing MTUD probe"); -1520 trace!("PING"); -1521 builder.encode(frame::Ping, &mut self.stats.frame_tx); -1522 -1523 // If supported by the peer, we want no delays to the probe's ACK -1524 if self.peer_supports_ack_frequency() { -1525 trace!("IMMEDIATE_ACK"); -1526 builder.encode(frame::ImmediateAck, &mut self.stats.frame_tx); -1527 } -1528 -1529 builder.finish_and_track(now, self, path_id, PadDatagram::ToSize(probe_size)); -1530 -1531 self.path_stats -1532 .entry(path_id) -1533 .or_default() -1534 .sent_plpmtud_probes += 1; -1535 } -1536 } -1537 -1538 if transmit.is_empty() { -1539 return None; -1540 } -1541 -1542 let network_path = self.path_data(path_id).network_path; -1543 trace!( -1544 segment_size = transmit.segment_size(), -1545 last_datagram_len = transmit.len() % transmit.segment_size(), -1546 %network_path, -1547 "sending {} bytes in {} datagrams", -1548 transmit.len(), -1549 transmit.num_datagrams() -1550 ); -1551 self.path_data_mut(path_id) -1552 .inc_total_sent(transmit.len() as u64); -1553 -1554 self.stats -1555 .udp_tx -1556 .on_sent(transmit.num_datagrams() as u64, transmit.len()); -1557 -1558 Some(Transmit { -1559 destination: network_path.remote, -1560 size: transmit.len(), -1561 ecn: if self.path_data(path_id).sending_ecn { -1562 Some(EcnCodepoint::Ect0) -1563 } else { -1564 None -1565 }, -1566 segment_size: match transmit.num_datagrams() { -1567 1 => None, -1568 _ => Some(transmit.segment_size()), -1569 }, -1570 src_ip: network_path.local_ip, -1571 }) -1572 } -1573 -1574 /// Returns the [`SpaceId`] of the next packet space which has data to send -1575 /// -1576 /// This takes into account the space available to frames in the next datagram. -1577 // TODO(flub): This duplication is not nice. -1578 fn next_send_space( -1579 &mut self, -1580 current_space_id: SpaceId, -1581 path_id: PathId, -1582 buf: &TransmitBuf<'_>, -1583 close: bool, -1584 ) -> Option<SpaceId> { -1585 // Number of bytes available for frames if this is a 1-RTT packet. We're guaranteed -1586 // to be able to send an individual frame at least this large in the next 1-RTT -1587 // packet. This could be generalized to support every space, but it's only needed to -1588 // handle large fixed-size frames, which only exist in 1-RTT (application -1589 // datagrams). We don't account for coalesced packets potentially occupying space -1590 // because frames can always spill into the next datagram. -1591 let mut space_id = current_space_id; -1592 loop { -1593 let can_send = self.space_can_send(space_id, path_id, buf.segment_size(), close); -1594 if !can_send.is_empty() || (close && self.spaces[space_id].crypto.is_some()) { -1595 return Some(space_id); +1518 // If supported by the peer, we want no delays to the probe's ACK +1519 if self.peer_supports_ack_frequency() { +1520 trace!("IMMEDIATE_ACK"); +1521 builder.encode(frame::ImmediateAck, &mut self.stats.frame_tx); +1522 } +1523 +1524 builder.finish_and_track(now, self, path_id, PadDatagram::ToSize(probe_size)); +1525 +1526 self.path_stats +1527 .entry(path_id) +1528 .or_default() +1529 .sent_plpmtud_probes += 1; +1530 } +1531 } +1532 +1533 if transmit.is_empty() { +1534 return None; +1535 } +1536 +1537 let network_path = self.path_data(path_id).network_path; +1538 trace!( +1539 segment_size = transmit.segment_size(), +1540 last_datagram_len = transmit.len() % transmit.segment_size(), +1541 %network_path, +1542 "sending {} bytes in {} datagrams", +1543 transmit.len(), +1544 transmit.num_datagrams() +1545 ); +1546 self.path_data_mut(path_id) +1547 .inc_total_sent(transmit.len() as u64); +1548 +1549 self.stats +1550 .udp_tx +1551 .on_sent(transmit.num_datagrams() as u64, transmit.len()); +1552 +1553 Some(Transmit { +1554 destination: network_path.remote, +1555 size: transmit.len(), +1556 ecn: if self.path_data(path_id).sending_ecn { +1557 Some(EcnCodepoint::Ect0) +1558 } else { +1559 None +1560 }, +1561 segment_size: match transmit.num_datagrams() { +1562 1 => None, +1563 _ => Some(transmit.segment_size()), +1564 }, +1565 src_ip: network_path.local_ip, +1566 }) +1567 } +1568 +1569 /// Returns the [`SpaceId`] of the next packet space which has data to send +1570 /// +1571 /// This takes into account the space available to frames in the next datagram. +1572 // TODO(flub): This duplication is not nice. +1573 fn next_send_space( +1574 &mut self, +1575 current_space_id: SpaceId, +1576 path_id: PathId, +1577 buf: &TransmitBuf<'_>, +1578 close: bool, +1579 ) -> Option<SpaceId> { +1580 // Number of bytes available for frames if this is a 1-RTT packet. We're guaranteed +1581 // to be able to send an individual frame at least this large in the next 1-RTT +1582 // packet. This could be generalized to support every space, but it's only needed to +1583 // handle large fixed-size frames, which only exist in 1-RTT (application +1584 // datagrams). We don't account for coalesced packets potentially occupying space +1585 // because frames can always spill into the next datagram. +1586 let mut space_id = current_space_id; +1587 loop { +1588 let can_send = self.space_can_send(space_id, path_id, buf.segment_size(), close); +1589 if !can_send.is_empty() || (close && self.spaces[space_id].crypto.is_some()) { +1590 return Some(space_id); +1591 } +1592 space_id = match space_id { +1593 SpaceId::Initial => SpaceId::Handshake, +1594 SpaceId::Handshake => SpaceId::Data, +1595 SpaceId::Data => break, 1596 } -1597 space_id = match space_id { -1598 SpaceId::Initial => SpaceId::Handshake, -1599 SpaceId::Handshake => SpaceId::Data, -1600 SpaceId::Data => break, -1601 } -1602 } -1603 None -1604 } -1605 -1606 /// Checks if creating a new datagram would be blocked by congestion control -1607 fn path_congestion_check( -1608 &mut self, -1609 space_id: SpaceId, -1610 path_id: PathId, -1611 transmit: &TransmitBuf<'_>, -1612 can_send: &SendableFrames, -1613 now: Instant, -1614 ) -> PathBlocked { -1615 // Anti-amplification is only based on `total_sent`, which gets updated after -1616 // the transmit is sent. Therefore we pass the amount of bytes for datagrams -1617 // that are already created, as well as 1 byte for starting another datagram. If -1618 // there is any anti-amplification budget left, we always allow a full MTU to be -1619 // sent (see https://github.com/quinn-rs/quinn/issues/1082). -1620 if self.side().is_server() -1621 && self -1622 .path_data(path_id) -1623 .anti_amplification_blocked(transmit.len() as u64 + 1) -1624 { -1625 trace!(?space_id, %path_id, "blocked by anti-amplification"); -1626 return PathBlocked::AntiAmplification; -1627 } +1597 } +1598 None +1599 } +1600 +1601 /// Checks if creating a new datagram would be blocked by congestion control +1602 fn path_congestion_check( +1603 &mut self, +1604 space_id: SpaceId, +1605 path_id: PathId, +1606 transmit: &TransmitBuf<'_>, +1607 can_send: &SendableFrames, +1608 now: Instant, +1609 ) -> PathBlocked { +1610 // Anti-amplification is only based on `total_sent`, which gets updated after +1611 // the transmit is sent. Therefore we pass the amount of bytes for datagrams +1612 // that are already created, as well as 1 byte for starting another datagram. If +1613 // there is any anti-amplification budget left, we always allow a full MTU to be +1614 // sent (see https://github.com/quinn-rs/quinn/issues/1082). +1615 if self.side().is_server() +1616 && self +1617 .path_data(path_id) +1618 .anti_amplification_blocked(transmit.len() as u64 + 1) +1619 { +1620 trace!(?space_id, %path_id, "blocked by anti-amplification"); +1621 return PathBlocked::AntiAmplification; +1622 } +1623 +1624 // Congestion control check. +1625 // Tail loss probes must not be blocked by congestion, or a deadlock could arise. +1626 let bytes_to_send = transmit.segment_size() as u64; +1627 let need_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0; 1628 -1629 // Congestion control check. -1630 // Tail loss probes must not be blocked by congestion, or a deadlock could arise. -1631 let bytes_to_send = transmit.segment_size() as u64; -1632 let need_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0; -1633 -1634 if can_send.other && !need_loss_probe && !can_send.close { -1635 let path = self.path_data(path_id); -1636 if path.in_flight.bytes + bytes_to_send >= path.congestion.window() { -1637 trace!(?space_id, %path_id, "blocked by congestion control"); -1638 return PathBlocked::Congestion; -1639 } -1640 } -1641 -1642 // Pacing check. -1643 if let Some(delay) = self.path_data_mut(path_id).pacing_delay(bytes_to_send, now) { -1644 self.timers.set( -1645 Timer::PerPath(path_id, PathTimer::Pacing), -1646 delay, -1647 self.qlog.with_time(now), -1648 ); -1649 // Loss probes and CONNECTION_CLOSE should be subject to pacing, even though -1650 // they are not congestion controlled. -1651 trace!(?space_id, %path_id, "blocked by pacing"); -1652 return PathBlocked::Pacing; -1653 } -1654 -1655 PathBlocked::No -1656 } -1657 -1658 /// Send PATH_CHALLENGE for a previous path if necessary -1659 /// -1660 /// QUIC-TRANSPORT section 9.3.3 -1661 /// <https://www.rfc-editor.org/rfc/rfc9000.html#name-off-path-packet-forwarding> -1662 fn send_prev_path_challenge( -1663 &mut self, -1664 now: Instant, -1665 buf: &mut TransmitBuf<'_>, -1666 path_id: PathId, -1667 ) -> Option<Transmit> { -1668 let (prev_cid, prev_path) = self.paths.get_mut(&path_id)?.prev.as_mut()?; -1669 // TODO (matheus23): We could use !prev_path.is_validating() here instead to -1670 // (possibly) also re-send challenges when they get lost. -1671 if !prev_path.send_new_challenge { -1672 return None; -1673 }; -1674 prev_path.send_new_challenge = false; -1675 let network_path = prev_path.network_path; -1676 let token = self.rng.random(); -1677 let info = paths::SentChallengeInfo { -1678 sent_instant: now, -1679 network_path, -1680 }; -1681 prev_path.challenges_sent.insert(token, info); -1682 debug_assert_eq!( -1683 self.highest_space, -1684 SpaceId::Data, -1685 "PATH_CHALLENGE queued without 1-RTT keys" -1686 ); -1687 buf.start_new_datagram_with_size(MIN_INITIAL_SIZE as usize); -1688 -1689 // Use the previous CID to avoid linking the new path with the previous path. We -1690 // don't bother accounting for possible retirement of that prev_cid because this is -1691 // sent once, immediately after migration, when the CID is known to be valid. Even -1692 // if a post-migration packet caused the CID to be retired, it's fair to pretend -1693 // this is sent first. -1694 debug_assert_eq!(buf.datagram_start_offset(), 0); -1695 let mut builder = -1696 PacketBuilder::new(now, SpaceId::Data, path_id, *prev_cid, buf, false, self)?; -1697 let challenge = frame::PathChallenge(token); -1698 trace!(%challenge, "validating previous path"); -1699 builder.encode(challenge, &mut self.stats.frame_tx); -1700 -1701 // An endpoint MUST expand datagrams that contain a PATH_CHALLENGE frame -1702 // to at least the smallest allowed maximum datagram size of 1200 bytes, -1703 // unless the anti-amplification limit for the path does not permit -1704 // sending a datagram of this size -1705 builder.pad_to(MIN_INITIAL_SIZE); -1706 -1707 builder.finish(self, now); -1708 self.stats.udp_tx.on_sent(1, buf.len()); -1709 -1710 Some(Transmit { -1711 destination: network_path.remote, -1712 size: buf.len(), -1713 ecn: None, -1714 segment_size: None, -1715 src_ip: network_path.local_ip, -1716 }) -1717 } -1718 -1719 /// Indicate what types of frames are ready to send for the given space -1720 /// -1721 /// *packet_size* is the number of bytes available to build the next packet. *close* -1722 /// *indicates whether a CONNECTION_CLOSE frame needs to be sent. -1723 fn space_can_send( -1724 &mut self, -1725 space_id: SpaceId, -1726 path_id: PathId, -1727 packet_size: usize, -1728 close: bool, -1729 ) -> SendableFrames { -1730 let pn = self.spaces[SpaceId::Data] -1731 .for_path(path_id) -1732 .peek_tx_number(); -1733 let frame_space_1rtt = packet_size.saturating_sub(self.predict_1rtt_overhead(pn, path_id)); -1734 if self.spaces[space_id].crypto.is_none() -1735 && (space_id != SpaceId::Data -1736 || self.zero_rtt_crypto.is_none() -1737 || self.side.is_server()) -1738 { -1739 // No keys available for this space -1740 return SendableFrames::empty(); -1741 } -1742 let mut can_send = self.spaces[space_id].can_send(path_id, &self.streams); -1743 if space_id == SpaceId::Data { -1744 can_send |= self.can_send_1rtt(path_id, frame_space_1rtt); -1745 } +1629 if can_send.other && !need_loss_probe && !can_send.close { +1630 let path = self.path_data(path_id); +1631 if path.in_flight.bytes + bytes_to_send >= path.congestion.window() { +1632 trace!(?space_id, %path_id, "blocked by congestion control"); +1633 return PathBlocked::Congestion; +1634 } +1635 } +1636 +1637 // Pacing check. +1638 if let Some(delay) = self.path_data_mut(path_id).pacing_delay(bytes_to_send, now) { +1639 self.timers.set( +1640 Timer::PerPath(path_id, PathTimer::Pacing), +1641 delay, +1642 self.qlog.with_time(now), +1643 ); +1644 // Loss probes and CONNECTION_CLOSE should be subject to pacing, even though +1645 // they are not congestion controlled. +1646 trace!(?space_id, %path_id, "blocked by pacing"); +1647 return PathBlocked::Pacing; +1648 } +1649 +1650 PathBlocked::No +1651 } +1652 +1653 /// Send PATH_CHALLENGE for a previous path if necessary +1654 /// +1655 /// QUIC-TRANSPORT section 9.3.3 +1656 /// <https://www.rfc-editor.org/rfc/rfc9000.html#name-off-path-packet-forwarding> +1657 fn send_prev_path_challenge( +1658 &mut self, +1659 now: Instant, +1660 buf: &mut TransmitBuf<'_>, +1661 path_id: PathId, +1662 ) -> Option<Transmit> { +1663 let (prev_cid, prev_path) = self.paths.get_mut(&path_id)?.prev.as_mut()?; +1664 // TODO (matheus23): We could use !prev_path.is_validating() here instead to +1665 // (possibly) also re-send challenges when they get lost. +1666 if !prev_path.send_new_challenge { +1667 return None; +1668 }; +1669 prev_path.send_new_challenge = false; +1670 let network_path = prev_path.network_path; +1671 let token = self.rng.random(); +1672 let info = paths::SentChallengeInfo { +1673 sent_instant: now, +1674 network_path, +1675 }; +1676 prev_path.challenges_sent.insert(token, info); +1677 debug_assert_eq!( +1678 self.highest_space, +1679 SpaceId::Data, +1680 "PATH_CHALLENGE queued without 1-RTT keys" +1681 ); +1682 buf.start_new_datagram_with_size(MIN_INITIAL_SIZE as usize); +1683 +1684 // Use the previous CID to avoid linking the new path with the previous path. We +1685 // don't bother accounting for possible retirement of that prev_cid because this is +1686 // sent once, immediately after migration, when the CID is known to be valid. Even +1687 // if a post-migration packet caused the CID to be retired, it's fair to pretend +1688 // this is sent first. +1689 debug_assert_eq!(buf.datagram_start_offset(), 0); +1690 let mut builder = +1691 PacketBuilder::new(now, SpaceId::Data, path_id, *prev_cid, buf, false, self)?; +1692 let challenge = frame::PathChallenge(token); +1693 trace!(%challenge, "validating previous path"); +1694 builder.encode(challenge, &mut self.stats.frame_tx); +1695 +1696 // An endpoint MUST expand datagrams that contain a PATH_CHALLENGE frame +1697 // to at least the smallest allowed maximum datagram size of 1200 bytes, +1698 // unless the anti-amplification limit for the path does not permit +1699 // sending a datagram of this size +1700 builder.pad_to(MIN_INITIAL_SIZE); +1701 +1702 builder.finish(self, now); +1703 self.stats.udp_tx.on_sent(1, buf.len()); +1704 +1705 Some(Transmit { +1706 destination: network_path.remote, +1707 size: buf.len(), +1708 ecn: None, +1709 segment_size: None, +1710 src_ip: network_path.local_ip, +1711 }) +1712 } +1713 +1714 /// Indicate what types of frames are ready to send for the given space +1715 /// +1716 /// *packet_size* is the number of bytes available to build the next packet. *close* +1717 /// *indicates whether a CONNECTION_CLOSE frame needs to be sent. +1718 fn space_can_send( +1719 &mut self, +1720 space_id: SpaceId, +1721 path_id: PathId, +1722 packet_size: usize, +1723 close: bool, +1724 ) -> SendableFrames { +1725 let pn = self.spaces[SpaceId::Data] +1726 .for_path(path_id) +1727 .peek_tx_number(); +1728 let frame_space_1rtt = packet_size.saturating_sub(self.predict_1rtt_overhead(pn, path_id)); +1729 if self.spaces[space_id].crypto.is_none() +1730 && (space_id != SpaceId::Data +1731 || self.zero_rtt_crypto.is_none() +1732 || self.side.is_server()) +1733 { +1734 // No keys available for this space +1735 return SendableFrames::empty(); +1736 } +1737 let mut can_send = self.spaces[space_id].can_send(path_id, &self.streams); +1738 if space_id == SpaceId::Data { +1739 can_send |= self.can_send_1rtt(path_id, frame_space_1rtt); +1740 } +1741 +1742 can_send.close = close && self.spaces[space_id].crypto.is_some(); +1743 +1744 can_send +1745 } 1746 -1747 can_send.close = close && self.spaces[space_id].crypto.is_some(); -1748 -1749 can_send -1750 } -1751 -1752 /// Process `ConnectionEvent`s generated by the associated `Endpoint` -1753 /// -1754 /// Will execute protocol logic upon receipt of a connection event, in turn preparing signals -1755 /// (including application `Event`s, `EndpointEvent`s and outgoing datagrams) that should be -1756 /// extracted through the relevant methods. -1757 pub fn handle_event(&mut self, event: ConnectionEvent) { -1758 use ConnectionEventInner::*; -1759 match event.0 { -1760 Datagram(DatagramConnectionEvent { -1761 now, -1762 network_path, -1763 path_id, -1764 ecn, -1765 first_decode, -1766 remaining, -1767 }) => { -1768 let span = trace_span!("pkt", %path_id); -1769 let _guard = span.enter(); +1747 /// Process `ConnectionEvent`s generated by the associated `Endpoint` +1748 /// +1749 /// Will execute protocol logic upon receipt of a connection event, in turn preparing signals +1750 /// (including application `Event`s, `EndpointEvent`s and outgoing datagrams) that should be +1751 /// extracted through the relevant methods. +1752 pub fn handle_event(&mut self, event: ConnectionEvent) { +1753 use ConnectionEventInner::*; +1754 match event.0 { +1755 Datagram(DatagramConnectionEvent { +1756 now, +1757 network_path, +1758 path_id, +1759 ecn, +1760 first_decode, +1761 remaining, +1762 }) => { +1763 let span = trace_span!("pkt", %path_id); +1764 let _guard = span.enter(); +1765 +1766 if self.update_network_path_or_discard(network_path, path_id) { +1767 // A return value of true indicates we should discard this packet. +1768 return; +1769 } 1770 -1771 if self.update_network_path_or_discard(network_path, path_id) { -1772 // A return value of true indicates we should discard this packet. -1773 return; -1774 } -1775 -1776 let was_anti_amplification_blocked = self -1777 .path(path_id) -1778 .map(|path| path.anti_amplification_blocked(1)) -1779 .unwrap_or(true); // if we don't know about this path it's eagerly considered as unvalidated -1780 // TODO(@divma): revisit this -1781 -1782 self.stats.udp_rx.datagrams += 1; -1783 self.stats.udp_rx.bytes += first_decode.len() as u64; -1784 let data_len = first_decode.len(); -1785 -1786 self.handle_decode(now, network_path, path_id, ecn, first_decode); -1787 // The current `path` might have changed inside `handle_decode` since the packet -1788 // could have triggered a migration. The packet might also belong to an unknown -1789 // path and have been rejected. Make sure the data received is accounted for the -1790 // most recent path by accessing `path` after `handle_decode`. -1791 if let Some(path) = self.path_mut(path_id) { -1792 path.inc_total_recvd(data_len as u64); +1771 let was_anti_amplification_blocked = self +1772 .path(path_id) +1773 .map(|path| path.anti_amplification_blocked(1)) +1774 .unwrap_or(true); // if we don't know about this path it's eagerly considered as unvalidated +1775 // TODO(@divma): revisit this +1776 +1777 self.stats.udp_rx.datagrams += 1; +1778 self.stats.udp_rx.bytes += first_decode.len() as u64; +1779 let data_len = first_decode.len(); +1780 +1781 self.handle_decode(now, network_path, path_id, ecn, first_decode); +1782 // The current `path` might have changed inside `handle_decode` since the packet +1783 // could have triggered a migration. The packet might also belong to an unknown +1784 // path and have been rejected. Make sure the data received is accounted for the +1785 // most recent path by accessing `path` after `handle_decode`. +1786 if let Some(path) = self.path_mut(path_id) { +1787 path.inc_total_recvd(data_len as u64); +1788 } +1789 +1790 if let Some(data) = remaining { +1791 self.stats.udp_rx.bytes += data.len() as u64; +1792 self.handle_coalesced(now, network_path, path_id, ecn, data); 1793 } 1794 -1795 if let Some(data) = remaining { -1796 self.stats.udp_rx.bytes += data.len() as u64; -1797 self.handle_coalesced(now, network_path, path_id, ecn, data); +1795 if let Some(path) = self.paths.get_mut(&path_id) { +1796 self.qlog +1797 .emit_recovery_metrics(path_id, &mut path.data, now); 1798 } 1799 -1800 if let Some(path) = self.paths.get_mut(&path_id) { -1801 self.qlog -1802 .emit_recovery_metrics(path_id, &mut path.data, now); -1803 } -1804 -1805 if was_anti_amplification_blocked { -1806 // A prior attempt to set the loss detection timer may have failed due to -1807 // anti-amplification, so ensure it's set now. Prevents a handshake deadlock if -1808 // the server's first flight is lost. -1809 self.set_loss_detection_timer(now, path_id); -1810 } -1811 } -1812 NewIdentifiers(ids, now, cid_len, cid_lifetime) => { -1813 let path_id = ids.first().map(|issued| issued.path_id).unwrap_or_default(); -1814 debug_assert!(ids.iter().all(|issued| issued.path_id == path_id)); -1815 let cid_state = self -1816 .local_cid_state -1817 .entry(path_id) -1818 .or_insert_with(|| CidState::new(cid_len, cid_lifetime, now, 0)); -1819 cid_state.new_cids(&ids, now); -1820 -1821 ids.into_iter().rev().for_each(|frame| { -1822 self.spaces[SpaceId::Data].pending.new_cids.push(frame); -1823 }); -1824 // Always update Timer::PushNewCid -1825 self.reset_cid_retirement(now); -1826 } -1827 } -1828 } -1829 -1830 /// Updates the network path for `path_id`. -1831 /// -1832 /// Returns true if a packet coming in for this `path_id` over given `network_path` should be discarded. -1833 /// Returns false if the path was updated and the packet doesn't need to be discarded. -1834 fn update_network_path_or_discard(&mut self, network_path: FourTuple, path_id: PathId) -> bool { -1835 let remote_may_migrate = self.side.remote_may_migrate(&self.state); -1836 let local_ip_may_migrate = self.side.is_client(); -1837 // If this packet could initiate a migration and we're a client or a server that -1838 // forbids migration, drop the datagram. This could be relaxed to heuristically -1839 // permit NAT-rebinding-like migration. -1840 if let Some(known_path) = self.path_mut(path_id) { -1841 if network_path.remote != known_path.network_path.remote && !remote_may_migrate { -1842 trace!( -1843 %path_id, -1844 %network_path, -1845 %known_path.network_path, -1846 "discarding packet from unrecognized peer" -1847 ); -1848 return true; -1849 } -1850 -1851 if known_path.network_path.local_ip.is_some() -1852 && network_path.local_ip.is_some() -1853 && known_path.network_path.local_ip != network_path.local_ip -1854 && !local_ip_may_migrate -1855 { -1856 trace!( -1857 %path_id, -1858 %network_path, -1859 %known_path.network_path, -1860 "discarding packet sent to incorrect interface" -1861 ); -1862 return true; -1863 } -1864 // If the datagram indicates that we've changed our local IP, we update it. -1865 // This is alluded to in Section 5.2 of the Multipath RFC draft 18: -1866 // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-18.html#name-using-multiple-paths-on-the -1867 // > Client receives the packet, recognizes a path migration, updates the source address of path 2 to 192.0.2.1. -1868 if let Some(local_ip) = network_path.local_ip { -1869 if known_path -1870 .network_path -1871 .local_ip -1872 .is_some_and(|ip| ip != local_ip) -1873 { -1874 debug!( -1875 %path_id, -1876 %network_path, -1877 %known_path.network_path, -1878 "path's local address seemingly migrated" -1879 ); -1880 } -1881 // We update the address without path validation on the client side. -1882 // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-18.html#section-5.1 -1883 // > Servers observing a 4-tuple change will perform path validation (see Section 9 of [QUIC-TRANSPORT]). -1884 // This sounds like it's *only* the server endpoints that do this. -1885 // TODO(matheus23): We should still consider doing a proper migration on the client side in the future. -1886 // For now, this preserves the behavior of this code pre 4-tuple tracking. -1887 known_path.network_path.local_ip = Some(local_ip); -1888 } -1889 } -1890 false -1891 } -1892 -1893 /// Process timer expirations -1894 /// -1895 /// Executes protocol logic, potentially preparing signals (including application `Event`s, -1896 /// `EndpointEvent`s and outgoing datagrams) that should be extracted through the relevant -1897 /// methods. -1898 /// -1899 /// It is most efficient to call this immediately after the system clock reaches the latest -1900 /// `Instant` that was output by `poll_timeout`; however spurious extra calls will simply -1901 /// no-op and therefore are safe. -1902 pub fn handle_timeout(&mut self, now: Instant) { -1903 while let Some((timer, _time)) = self.timers.expire_before(now, &self.qlog) { -1904 // TODO(@divma): remove `at` when the unicorn is born -1905 trace!(?timer, at=?now, "timeout"); -1906 match timer { -1907 Timer::Conn(timer) => match timer { -1908 ConnTimer::Close => { -1909 self.state.move_to_drained(None); -1910 self.endpoint_events.push_back(EndpointEventInner::Drained); -1911 } -1912 ConnTimer::Idle => { -1913 self.kill(ConnectionError::TimedOut); -1914 } -1915 ConnTimer::KeepAlive => { -1916 trace!("sending keep-alive"); -1917 self.ping(); -1918 } -1919 ConnTimer::KeyDiscard => { -1920 self.zero_rtt_crypto = None; -1921 self.prev_crypto = None; -1922 } -1923 ConnTimer::PushNewCid => { -1924 while let Some((path_id, when)) = self.next_cid_retirement() { -1925 if when > now { -1926 break; -1927 } -1928 match self.local_cid_state.get_mut(&path_id) { -1929 None => error!(%path_id, "No local CID state for path"), -1930 Some(cid_state) => { -1931 // Update `retire_prior_to` field in NEW_CONNECTION_ID frame -1932 let num_new_cid = cid_state.on_cid_timeout().into(); -1933 if !self.state.is_closed() { -1934 trace!( -1935 "push a new CID to peer RETIRE_PRIOR_TO field {}", -1936 cid_state.retire_prior_to() -1937 ); -1938 self.endpoint_events.push_back( -1939 EndpointEventInner::NeedIdentifiers( -1940 path_id, -1941 now, -1942 num_new_cid, -1943 ), -1944 ); -1945 } -1946 } -1947 } -1948 } -1949 } -1950 }, -1951 // TODO: add path_id as span somehow -1952 Timer::PerPath(path_id, timer) => { -1953 let span = trace_span!("per-path timer fired", %path_id, ?timer); -1954 let _guard = span.enter(); -1955 match timer { -1956 PathTimer::PathIdle => { -1957 self.close_path(now, path_id, TransportErrorCode::NO_ERROR.into()) -1958 .ok(); +1800 if was_anti_amplification_blocked { +1801 // A prior attempt to set the loss detection timer may have failed due to +1802 // anti-amplification, so ensure it's set now. Prevents a handshake deadlock if +1803 // the server's first flight is lost. +1804 self.set_loss_detection_timer(now, path_id); +1805 } +1806 } +1807 NewIdentifiers(ids, now, cid_len, cid_lifetime) => { +1808 let path_id = ids.first().map(|issued| issued.path_id).unwrap_or_default(); +1809 debug_assert!(ids.iter().all(|issued| issued.path_id == path_id)); +1810 let cid_state = self +1811 .local_cid_state +1812 .entry(path_id) +1813 .or_insert_with(|| CidState::new(cid_len, cid_lifetime, now, 0)); +1814 cid_state.new_cids(&ids, now); +1815 +1816 ids.into_iter().rev().for_each(|frame| { +1817 self.spaces[SpaceId::Data].pending.new_cids.push(frame); +1818 }); +1819 // Always update Timer::PushNewCid +1820 self.reset_cid_retirement(now); +1821 } +1822 } +1823 } +1824 +1825 /// Updates the network path for `path_id`. +1826 /// +1827 /// Returns true if a packet coming in for this `path_id` over given `network_path` should be discarded. +1828 /// Returns false if the path was updated and the packet doesn't need to be discarded. +1829 fn update_network_path_or_discard(&mut self, network_path: FourTuple, path_id: PathId) -> bool { +1830 let remote_may_migrate = self.side.remote_may_migrate(&self.state); +1831 let local_ip_may_migrate = self.side.is_client(); +1832 // If this packet could initiate a migration and we're a client or a server that +1833 // forbids migration, drop the datagram. This could be relaxed to heuristically +1834 // permit NAT-rebinding-like migration. +1835 if let Some(known_path) = self.path_mut(path_id) { +1836 if network_path.remote != known_path.network_path.remote && !remote_may_migrate { +1837 trace!( +1838 %path_id, +1839 %network_path, +1840 %known_path.network_path, +1841 "discarding packet from unrecognized peer" +1842 ); +1843 return true; +1844 } +1845 +1846 if known_path.network_path.local_ip.is_some() +1847 && network_path.local_ip.is_some() +1848 && known_path.network_path.local_ip != network_path.local_ip +1849 && !local_ip_may_migrate +1850 { +1851 trace!( +1852 %path_id, +1853 %network_path, +1854 %known_path.network_path, +1855 "discarding packet sent to incorrect interface" +1856 ); +1857 return true; +1858 } +1859 // If the datagram indicates that we've changed our local IP, we update it. +1860 // This is alluded to in Section 5.2 of the Multipath RFC draft 18: +1861 // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-18.html#name-using-multiple-paths-on-the +1862 // > Client receives the packet, recognizes a path migration, updates the source address of path 2 to 192.0.2.1. +1863 if let Some(local_ip) = network_path.local_ip { +1864 if known_path +1865 .network_path +1866 .local_ip +1867 .is_some_and(|ip| ip != local_ip) +1868 { +1869 debug!( +1870 %path_id, +1871 %network_path, +1872 %known_path.network_path, +1873 "path's local address seemingly migrated" +1874 ); +1875 } +1876 // We update the address without path validation on the client side. +1877 // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-18.html#section-5.1 +1878 // > Servers observing a 4-tuple change will perform path validation (see Section 9 of [QUIC-TRANSPORT]). +1879 // This sounds like it's *only* the server endpoints that do this. +1880 // TODO(matheus23): We should still consider doing a proper migration on the client side in the future. +1881 // For now, this preserves the behavior of this code pre 4-tuple tracking. +1882 known_path.network_path.local_ip = Some(local_ip); +1883 } +1884 } +1885 false +1886 } +1887 +1888 /// Process timer expirations +1889 /// +1890 /// Executes protocol logic, potentially preparing signals (including application `Event`s, +1891 /// `EndpointEvent`s and outgoing datagrams) that should be extracted through the relevant +1892 /// methods. +1893 /// +1894 /// It is most efficient to call this immediately after the system clock reaches the latest +1895 /// `Instant` that was output by `poll_timeout`; however spurious extra calls will simply +1896 /// no-op and therefore are safe. +1897 pub fn handle_timeout(&mut self, now: Instant) { +1898 while let Some((timer, _time)) = self.timers.expire_before(now, &self.qlog) { +1899 // TODO(@divma): remove `at` when the unicorn is born +1900 trace!(?timer, at=?now, "timeout"); +1901 match timer { +1902 Timer::Conn(timer) => match timer { +1903 ConnTimer::Close => { +1904 self.state.move_to_drained(None); +1905 self.endpoint_events.push_back(EndpointEventInner::Drained); +1906 } +1907 ConnTimer::Idle => { +1908 self.kill(ConnectionError::TimedOut); +1909 } +1910 ConnTimer::KeepAlive => { +1911 trace!("sending keep-alive"); +1912 self.ping(); +1913 } +1914 ConnTimer::KeyDiscard => { +1915 self.zero_rtt_crypto = None; +1916 self.prev_crypto = None; +1917 } +1918 ConnTimer::PushNewCid => { +1919 while let Some((path_id, when)) = self.next_cid_retirement() { +1920 if when > now { +1921 break; +1922 } +1923 match self.local_cid_state.get_mut(&path_id) { +1924 None => error!(%path_id, "No local CID state for path"), +1925 Some(cid_state) => { +1926 // Update `retire_prior_to` field in NEW_CONNECTION_ID frame +1927 let num_new_cid = cid_state.on_cid_timeout().into(); +1928 if !self.state.is_closed() { +1929 trace!( +1930 "push a new CID to peer RETIRE_PRIOR_TO field {}", +1931 cid_state.retire_prior_to() +1932 ); +1933 self.endpoint_events.push_back( +1934 EndpointEventInner::NeedIdentifiers( +1935 path_id, +1936 now, +1937 num_new_cid, +1938 ), +1939 ); +1940 } +1941 } +1942 } +1943 } +1944 } +1945 }, +1946 // TODO: add path_id as span somehow +1947 Timer::PerPath(path_id, timer) => { +1948 let span = trace_span!("per-path timer fired", %path_id, ?timer); +1949 let _guard = span.enter(); +1950 match timer { +1951 PathTimer::PathIdle => { +1952 self.close_path(now, path_id, TransportErrorCode::NO_ERROR.into()) +1953 .ok(); +1954 } +1955 +1956 PathTimer::PathKeepAlive => { +1957 trace!("sending keep-alive on path"); +1958 self.ping_path(path_id).ok(); 1959 } -1960 -1961 PathTimer::PathKeepAlive => { -1962 trace!("sending keep-alive on path"); -1963 self.ping_path(path_id).ok(); -1964 } -1965 PathTimer::LossDetection => { -1966 self.on_loss_detection_timeout(now, path_id); -1967 self.qlog.emit_recovery_metrics( -1968 path_id, -1969 &mut self.paths.get_mut(&path_id).unwrap().data, -1970 now, -1971 ); -1972 } -1973 PathTimer::PathValidation => { -1974 let Some(path) = self.paths.get_mut(&path_id) else { -1975 continue; -1976 }; -1977 self.timers.stop( -1978 Timer::PerPath(path_id, PathTimer::PathChallengeLost), -1979 self.qlog.with_time(now), -1980 ); -1981 debug!("path validation failed"); -1982 if let Some((_, prev)) = path.prev.take() { -1983 path.data = prev; -1984 } -1985 path.data.challenges_sent.clear(); -1986 path.data.send_new_challenge = false; -1987 } -1988 PathTimer::PathChallengeLost => { -1989 let Some(path) = self.paths.get_mut(&path_id) else { -1990 continue; -1991 }; -1992 trace!("path challenge deemed lost"); -1993 path.data.send_new_challenge = true; -1994 } -1995 PathTimer::PathOpen => { -1996 let Some(path) = self.paths.get_mut(&path_id) else { -1997 continue; -1998 }; -1999 path.data.challenges_sent.clear(); -2000 path.data.send_new_challenge = false; -2001 self.timers.stop( -2002 Timer::PerPath(path_id, PathTimer::PathChallengeLost), -2003 self.qlog.with_time(now), -2004 ); -2005 debug!("new path validation failed"); -2006 if let Err(err) = self.close_path( -2007 now, -2008 path_id, -2009 TransportErrorCode::PATH_UNSTABLE_OR_POOR.into(), -2010 ) { -2011 warn!(?err, "failed closing path"); -2012 } -2013 -2014 self.events.push_back(Event::Path(PathEvent::LocallyClosed { -2015 id: path_id, -2016 error: PathError::ValidationFailed, -2017 })); -2018 } -2019 PathTimer::Pacing => trace!("pacing timer expired"), -2020 PathTimer::MaxAckDelay => { -2021 trace!("max ack delay reached"); -2022 // This timer is only armed in the Data space -2023 self.spaces[SpaceId::Data] -2024 .for_path(path_id) -2025 .pending_acks -2026 .on_max_ack_delay_timeout() -2027 } -2028 PathTimer::DiscardPath => { -2029 // The path was abandoned and 3*PTO has expired since. Clean up all -2030 // remaining state and install stateless reset token. -2031 self.timers.stop_per_path(path_id, self.qlog.with_time(now)); -2032 if let Some(loc_cid_state) = self.local_cid_state.remove(&path_id) { -2033 let (min_seq, max_seq) = loc_cid_state.active_seq(); -2034 for seq in min_seq..=max_seq { -2035 self.endpoint_events.push_back( -2036 EndpointEventInner::RetireConnectionId( -2037 now, path_id, seq, false, -2038 ), -2039 ); -2040 } -2041 } -2042 self.discard_path(path_id, now); -2043 } -2044 } -2045 } -2046 } -2047 } -2048 } -2049 -2050 /// Close a connection immediately +1960 PathTimer::LossDetection => { +1961 self.on_loss_detection_timeout(now, path_id); +1962 self.qlog.emit_recovery_metrics( +1963 path_id, +1964 &mut self.paths.get_mut(&path_id).unwrap().data, +1965 now, +1966 ); +1967 } +1968 PathTimer::PathValidation => { +1969 let Some(path) = self.paths.get_mut(&path_id) else { +1970 continue; +1971 }; +1972 self.timers.stop( +1973 Timer::PerPath(path_id, PathTimer::PathChallengeLost), +1974 self.qlog.with_time(now), +1975 ); +1976 debug!("path validation failed"); +1977 if let Some((_, prev)) = path.prev.take() { +1978 path.data = prev; +1979 } +1980 path.data.challenges_sent.clear(); +1981 path.data.send_new_challenge = false; +1982 } +1983 PathTimer::PathChallengeLost => { +1984 let Some(path) = self.paths.get_mut(&path_id) else { +1985 continue; +1986 }; +1987 trace!("path challenge deemed lost"); +1988 path.data.send_new_challenge = true; +1989 } +1990 PathTimer::PathOpen => { +1991 let Some(path) = self.paths.get_mut(&path_id) else { +1992 continue; +1993 }; +1994 path.data.challenges_sent.clear(); +1995 path.data.send_new_challenge = false; +1996 self.timers.stop( +1997 Timer::PerPath(path_id, PathTimer::PathChallengeLost), +1998 self.qlog.with_time(now), +1999 ); +2000 debug!("new path validation failed"); +2001 if let Err(err) = self.close_path( +2002 now, +2003 path_id, +2004 TransportErrorCode::PATH_UNSTABLE_OR_POOR.into(), +2005 ) { +2006 warn!(?err, "failed closing path"); +2007 } +2008 +2009 self.events.push_back(Event::Path(PathEvent::LocallyClosed { +2010 id: path_id, +2011 error: PathError::ValidationFailed, +2012 })); +2013 } +2014 PathTimer::Pacing => trace!("pacing timer expired"), +2015 PathTimer::MaxAckDelay => { +2016 trace!("max ack delay reached"); +2017 // This timer is only armed in the Data space +2018 self.spaces[SpaceId::Data] +2019 .for_path(path_id) +2020 .pending_acks +2021 .on_max_ack_delay_timeout() +2022 } +2023 PathTimer::DiscardPath => { +2024 // The path was abandoned and 3*PTO has expired since. Clean up all +2025 // remaining state and install stateless reset token. +2026 self.timers.stop_per_path(path_id, self.qlog.with_time(now)); +2027 if let Some(loc_cid_state) = self.local_cid_state.remove(&path_id) { +2028 let (min_seq, max_seq) = loc_cid_state.active_seq(); +2029 for seq in min_seq..=max_seq { +2030 self.endpoint_events.push_back( +2031 EndpointEventInner::RetireConnectionId( +2032 now, path_id, seq, false, +2033 ), +2034 ); +2035 } +2036 } +2037 self.discard_path(path_id, now); +2038 } +2039 } +2040 } +2041 } +2042 } +2043 } +2044 +2045 /// Close a connection immediately +2046 /// +2047 /// This does not ensure delivery of outstanding data. It is the application's responsibility to +2048 /// call this only when all important communications have been completed, e.g. by calling +2049 /// [`SendStream::finish`] on outstanding streams and waiting for the corresponding +2050 /// [`StreamEvent::Finished`] event. 2051 /// -2052 /// This does not ensure delivery of outstanding data. It is the application's responsibility to -2053 /// call this only when all important communications have been completed, e.g. by calling -2054 /// [`SendStream::finish`] on outstanding streams and waiting for the corresponding -2055 /// [`StreamEvent::Finished`] event. -2056 /// -2057 /// If [`Streams::send_streams`] returns 0, all outstanding stream data has been -2058 /// delivered. There may still be data from the peer that has not been received. -2059 /// -2060 /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished -2061 pub fn close(&mut self, now: Instant, error_code: VarInt, reason: Bytes) { -2062 self.close_inner( -2063 now, -2064 Close::Application(frame::ApplicationClose { error_code, reason }), -2065 ) -2066 } -2067 -2068 fn close_inner(&mut self, now: Instant, reason: Close) { -2069 let was_closed = self.state.is_closed(); -2070 if !was_closed { -2071 self.close_common(); -2072 self.set_close_timer(now); -2073 self.close = true; -2074 self.state.move_to_closed_local(reason); -2075 } +2052 /// If [`Streams::send_streams`] returns 0, all outstanding stream data has been +2053 /// delivered. There may still be data from the peer that has not been received. +2054 /// +2055 /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished +2056 pub fn close(&mut self, now: Instant, error_code: VarInt, reason: Bytes) { +2057 self.close_inner( +2058 now, +2059 Close::Application(frame::ApplicationClose { error_code, reason }), +2060 ) +2061 } +2062 +2063 fn close_inner(&mut self, now: Instant, reason: Close) { +2064 let was_closed = self.state.is_closed(); +2065 if !was_closed { +2066 self.close_common(); +2067 self.set_close_timer(now); +2068 self.close = true; +2069 self.state.move_to_closed_local(reason); +2070 } +2071 } +2072 +2073 /// Control datagrams +2074 pub fn datagrams(&mut self) -> Datagrams<'_> { +2075 Datagrams { conn: self } 2076 } 2077 -2078 /// Control datagrams -2079 pub fn datagrams(&mut self) -> Datagrams<'_> { -2080 Datagrams { conn: self } +2078 /// Returns connection statistics +2079 pub fn stats(&mut self) -> ConnectionStats { +2080 self.stats.clone() 2081 } 2082 -2083 /// Returns connection statistics -2084 pub fn stats(&mut self) -> ConnectionStats { -2085 self.stats.clone() -2086 } -2087 -2088 /// Returns path statistics -2089 pub fn path_stats(&mut self, path_id: PathId) -> Option<PathStats> { -2090 let path = self.paths.get(&path_id)?; -2091 let stats = self.path_stats.entry(path_id).or_default(); -2092 stats.rtt = path.data.rtt.get(); -2093 stats.cwnd = path.data.congestion.window(); -2094 stats.current_mtu = path.data.mtud.current_mtu(); -2095 Some(*stats) -2096 } -2097 -2098 /// Ping the remote endpoint -2099 /// -2100 /// Causes an ACK-eliciting packet to be transmitted on the connection. -2101 pub fn ping(&mut self) { -2102 // TODO(flub): This is very brute-force: it pings *all* the paths. Instead it would -2103 // be nice if we could only send a single packet for this. -2104 for path_data in self.spaces[self.highest_space].number_spaces.values_mut() { -2105 path_data.ping_pending = true; -2106 } -2107 } -2108 -2109 /// Ping the remote endpoint over a specific path -2110 /// -2111 /// Causes an ACK-eliciting packet to be transmitted on the path. -2112 pub fn ping_path(&mut self, path: PathId) -> Result<(), ClosedPath> { -2113 let path_data = self.spaces[self.highest_space] -2114 .number_spaces -2115 .get_mut(&path) -2116 .ok_or(ClosedPath { _private: () })?; -2117 path_data.ping_pending = true; -2118 Ok(()) -2119 } -2120 -2121 /// Update traffic keys spontaneously -2122 /// -2123 /// This can be useful for testing key updates, as they otherwise only happen infrequently. -2124 pub fn force_key_update(&mut self) { -2125 if !self.state.is_established() { -2126 debug!("ignoring forced key update in illegal state"); -2127 return; -2128 } -2129 if self.prev_crypto.is_some() { -2130 // We already just updated, or are currently updating, the keys. Concurrent key updates -2131 // are illegal. -2132 debug!("ignoring redundant forced key update"); -2133 return; -2134 } -2135 self.update_keys(None, false); -2136 } -2137 -2138 // Compatibility wrapper for quinn < 0.11.7. Remove for 0.12. -2139 #[doc(hidden)] -2140 #[deprecated] -2141 pub fn initiate_key_update(&mut self) { -2142 self.force_key_update(); +2083 /// Returns path statistics +2084 pub fn path_stats(&mut self, path_id: PathId) -> Option<PathStats> { +2085 let path = self.paths.get(&path_id)?; +2086 let stats = self.path_stats.entry(path_id).or_default(); +2087 stats.rtt = path.data.rtt.get(); +2088 stats.cwnd = path.data.congestion.window(); +2089 stats.current_mtu = path.data.mtud.current_mtu(); +2090 Some(*stats) +2091 } +2092 +2093 /// Ping the remote endpoint +2094 /// +2095 /// Causes an ACK-eliciting packet to be transmitted on the connection. +2096 pub fn ping(&mut self) { +2097 // TODO(flub): This is very brute-force: it pings *all* the paths. Instead it would +2098 // be nice if we could only send a single packet for this. +2099 for path_data in self.spaces[self.highest_space].number_spaces.values_mut() { +2100 path_data.ping_pending = true; +2101 } +2102 } +2103 +2104 /// Ping the remote endpoint over a specific path +2105 /// +2106 /// Causes an ACK-eliciting packet to be transmitted on the path. +2107 pub fn ping_path(&mut self, path: PathId) -> Result<(), ClosedPath> { +2108 let path_data = self.spaces[self.highest_space] +2109 .number_spaces +2110 .get_mut(&path) +2111 .ok_or(ClosedPath { _private: () })?; +2112 path_data.ping_pending = true; +2113 Ok(()) +2114 } +2115 +2116 /// Update traffic keys spontaneously +2117 /// +2118 /// This can be useful for testing key updates, as they otherwise only happen infrequently. +2119 pub fn force_key_update(&mut self) { +2120 if !self.state.is_established() { +2121 debug!("ignoring forced key update in illegal state"); +2122 return; +2123 } +2124 if self.prev_crypto.is_some() { +2125 // We already just updated, or are currently updating, the keys. Concurrent key updates +2126 // are illegal. +2127 debug!("ignoring redundant forced key update"); +2128 return; +2129 } +2130 self.update_keys(None, false); +2131 } +2132 +2133 // Compatibility wrapper for quinn < 0.11.7. Remove for 0.12. +2134 #[doc(hidden)] +2135 #[deprecated] +2136 pub fn initiate_key_update(&mut self) { +2137 self.force_key_update(); +2138 } +2139 +2140 /// Get a session reference +2141 pub fn crypto_session(&self) -> &dyn crypto::Session { +2142 &*self.crypto 2143 } 2144 -2145 /// Get a session reference -2146 pub fn crypto_session(&self) -> &dyn crypto::Session { -2147 &*self.crypto -2148 } -2149 -2150 /// Whether the connection is in the process of being established -2151 /// -2152 /// If this returns `false`, the connection may be either established or closed, signaled by the -2153 /// emission of a `Connected` or `ConnectionLost` message respectively. -2154 pub fn is_handshaking(&self) -> bool { -2155 self.state.is_handshake() -2156 } -2157 -2158 /// Whether the connection is closed -2159 /// -2160 /// Closed connections cannot transport any further data. A connection becomes closed when -2161 /// either peer application intentionally closes it, or when either transport layer detects an -2162 /// error such as a time-out or certificate validation failure. -2163 /// -2164 /// A `ConnectionLost` event is emitted with details when the connection becomes closed. -2165 pub fn is_closed(&self) -> bool { -2166 self.state.is_closed() -2167 } -2168 -2169 /// Whether there is no longer any need to keep the connection around -2170 /// -2171 /// Closed connections become drained after a brief timeout to absorb any remaining in-flight -2172 /// packets from the peer. All drained connections have been closed. -2173 pub fn is_drained(&self) -> bool { -2174 self.state.is_drained() -2175 } -2176 -2177 /// For clients, if the peer accepted the 0-RTT data packets -2178 /// -2179 /// The value is meaningless until after the handshake completes. -2180 pub fn accepted_0rtt(&self) -> bool { -2181 self.accepted_0rtt +2145 /// Whether the connection is in the process of being established +2146 /// +2147 /// If this returns `false`, the connection may be either established or closed, signaled by the +2148 /// emission of a `Connected` or `ConnectionLost` message respectively. +2149 pub fn is_handshaking(&self) -> bool { +2150 self.state.is_handshake() +2151 } +2152 +2153 /// Whether the connection is closed +2154 /// +2155 /// Closed connections cannot transport any further data. A connection becomes closed when +2156 /// either peer application intentionally closes it, or when either transport layer detects an +2157 /// error such as a time-out or certificate validation failure. +2158 /// +2159 /// A `ConnectionLost` event is emitted with details when the connection becomes closed. +2160 pub fn is_closed(&self) -> bool { +2161 self.state.is_closed() +2162 } +2163 +2164 /// Whether there is no longer any need to keep the connection around +2165 /// +2166 /// Closed connections become drained after a brief timeout to absorb any remaining in-flight +2167 /// packets from the peer. All drained connections have been closed. +2168 pub fn is_drained(&self) -> bool { +2169 self.state.is_drained() +2170 } +2171 +2172 /// For clients, if the peer accepted the 0-RTT data packets +2173 /// +2174 /// The value is meaningless until after the handshake completes. +2175 pub fn accepted_0rtt(&self) -> bool { +2176 self.accepted_0rtt +2177 } +2178 +2179 /// Whether 0-RTT is/was possible during the handshake +2180 pub fn has_0rtt(&self) -> bool { +2181 self.zero_rtt_enabled 2182 } 2183 -2184 /// Whether 0-RTT is/was possible during the handshake -2185 pub fn has_0rtt(&self) -> bool { -2186 self.zero_rtt_enabled +2184 /// Whether there are any pending retransmits +2185 pub fn has_pending_retransmits(&self) -> bool { +2186 !self.spaces[SpaceId::Data].pending.is_empty(&self.streams) 2187 } 2188 -2189 /// Whether there are any pending retransmits -2190 pub fn has_pending_retransmits(&self) -> bool { -2191 !self.spaces[SpaceId::Data].pending.is_empty(&self.streams) +2189 /// Look up whether we're the client or server of this Connection +2190 pub fn side(&self) -> Side { +2191 self.side.side() 2192 } 2193 -2194 /// Look up whether we're the client or server of this Connection -2195 pub fn side(&self) -> Side { -2196 self.side.side() -2197 } -2198 -2199 /// Get the address observed by the remote over the given path -2200 pub fn path_observed_address(&self, path_id: PathId) -> Result<Option<SocketAddr>, ClosedPath> { -2201 self.path(path_id) -2202 .map(|path_data| { -2203 path_data -2204 .last_observed_addr_report -2205 .as_ref() -2206 .map(|observed| observed.socket_addr()) -2207 }) -2208 .ok_or(ClosedPath { _private: () }) +2194 /// Get the address observed by the remote over the given path +2195 pub fn path_observed_address(&self, path_id: PathId) -> Result<Option<SocketAddr>, ClosedPath> { +2196 self.path(path_id) +2197 .map(|path_data| { +2198 path_data +2199 .last_observed_addr_report +2200 .as_ref() +2201 .map(|observed| observed.socket_addr()) +2202 }) +2203 .ok_or(ClosedPath { _private: () }) +2204 } +2205 +2206 /// Current best estimate of this connection's latency (round-trip-time) +2207 pub fn rtt(&self, path_id: PathId) -> Option<Duration> { +2208 self.path(path_id).map(|d| d.rtt.get()) 2209 } 2210 -2211 /// Current best estimate of this connection's latency (round-trip-time) -2212 pub fn rtt(&self, path_id: PathId) -> Option<Duration> { -2213 self.path(path_id).map(|d| d.rtt.get()) +2211 /// Current state of this connection's congestion controller, for debugging purposes +2212 pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller> { +2213 self.path(path_id).map(|d| d.congestion.as_ref()) 2214 } 2215 -2216 /// Current state of this connection's congestion controller, for debugging purposes -2217 pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller> { -2218 self.path(path_id).map(|d| d.congestion.as_ref()) -2219 } -2220 -2221 /// Modify the number of remotely initiated streams that may be concurrently open -2222 /// -2223 /// No streams may be opened by the peer unless fewer than `count` are already open. Large -2224 /// `count`s increase both minimum and worst-case memory consumption. -2225 pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt) { -2226 self.streams.set_max_concurrent(dir, count); -2227 // If the limit was reduced, then a flow control update previously deemed insignificant may -2228 // now be significant. -2229 let pending = &mut self.spaces[SpaceId::Data].pending; -2230 self.streams.queue_max_stream_id(pending); -2231 } -2232 -2233 /// Modify the number of open paths allowed when multipath is enabled +2216 /// Modify the number of remotely initiated streams that may be concurrently open +2217 /// +2218 /// No streams may be opened by the peer unless fewer than `count` are already open. Large +2219 /// `count`s increase both minimum and worst-case memory consumption. +2220 pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt) { +2221 self.streams.set_max_concurrent(dir, count); +2222 // If the limit was reduced, then a flow control update previously deemed insignificant may +2223 // now be significant. +2224 let pending = &mut self.spaces[SpaceId::Data].pending; +2225 self.streams.queue_max_stream_id(pending); +2226 } +2227 +2228 /// Modify the number of open paths allowed when multipath is enabled +2229 /// +2230 /// When reducing the number of concurrent paths this will only affect delaying sending +2231 /// new MAX_PATH_ID frames until fewer than this number of paths are possible. To +2232 /// actively reduce paths they must be closed using [`Connection::close_path`], which +2233 /// can also be used to close not-yet-opened paths. 2234 /// -2235 /// When reducing the number of concurrent paths this will only affect delaying sending -2236 /// new MAX_PATH_ID frames until fewer than this number of paths are possible. To -2237 /// actively reduce paths they must be closed using [`Connection::close_path`], which -2238 /// can also be used to close not-yet-opened paths. -2239 /// -2240 /// If multipath is not negotiated (see the [`TransportConfig`]) this can not enable -2241 /// multipath and will fail. -2242 pub fn set_max_concurrent_paths( -2243 &mut self, -2244 now: Instant, -2245 count: NonZeroU32, -2246 ) -> Result<(), MultipathNotNegotiated> { -2247 if !self.is_multipath_negotiated() { -2248 return Err(MultipathNotNegotiated { _private: () }); -2249 } -2250 self.max_concurrent_paths = count; -2251 -2252 let in_use_count = self -2253 .local_max_path_id -2254 .next() -2255 .saturating_sub(self.abandoned_paths.len() as u32) -2256 .as_u32(); -2257 let extra_needed = count.get().saturating_sub(in_use_count); -2258 let new_max_path_id = self.local_max_path_id.saturating_add(extra_needed); +2235 /// If multipath is not negotiated (see the [`TransportConfig`]) this can not enable +2236 /// multipath and will fail. +2237 pub fn set_max_concurrent_paths( +2238 &mut self, +2239 now: Instant, +2240 count: NonZeroU32, +2241 ) -> Result<(), MultipathNotNegotiated> { +2242 if !self.is_multipath_negotiated() { +2243 return Err(MultipathNotNegotiated { _private: () }); +2244 } +2245 self.max_concurrent_paths = count; +2246 +2247 let in_use_count = self +2248 .local_max_path_id +2249 .next() +2250 .saturating_sub(self.abandoned_paths.len() as u32) +2251 .as_u32(); +2252 let extra_needed = count.get().saturating_sub(in_use_count); +2253 let new_max_path_id = self.local_max_path_id.saturating_add(extra_needed); +2254 +2255 self.set_max_path_id(now, new_max_path_id); +2256 +2257 Ok(()) +2258 } 2259 -2260 self.set_max_path_id(now, new_max_path_id); -2261 -2262 Ok(()) -2263 } -2264 -2265 /// If needed, issues a new MAX_PATH_ID frame and new CIDs for any newly allowed paths -2266 fn set_max_path_id(&mut self, now: Instant, max_path_id: PathId) { -2267 if max_path_id <= self.local_max_path_id { -2268 return; -2269 } -2270 -2271 self.local_max_path_id = max_path_id; -2272 self.spaces[SpaceId::Data].pending.max_path_id = true; -2273 -2274 self.issue_first_path_cids(now); -2275 } -2276 -2277 /// Current number of remotely initiated streams that may be concurrently open -2278 /// -2279 /// If the target for this limit is reduced using [`set_max_concurrent_streams`](Self::set_max_concurrent_streams), -2280 /// it will not change immediately, even if fewer streams are open. Instead, it will -2281 /// decrement by one for each time a remotely initiated stream of matching directionality is closed. -2282 pub fn max_concurrent_streams(&self, dir: Dir) -> u64 { -2283 self.streams.max_concurrent(dir) +2260 /// If needed, issues a new MAX_PATH_ID frame and new CIDs for any newly allowed paths +2261 fn set_max_path_id(&mut self, now: Instant, max_path_id: PathId) { +2262 if max_path_id <= self.local_max_path_id { +2263 return; +2264 } +2265 +2266 self.local_max_path_id = max_path_id; +2267 self.spaces[SpaceId::Data].pending.max_path_id = true; +2268 +2269 self.issue_first_path_cids(now); +2270 } +2271 +2272 /// Current number of remotely initiated streams that may be concurrently open +2273 /// +2274 /// If the target for this limit is reduced using [`set_max_concurrent_streams`](Self::set_max_concurrent_streams), +2275 /// it will not change immediately, even if fewer streams are open. Instead, it will +2276 /// decrement by one for each time a remotely initiated stream of matching directionality is closed. +2277 pub fn max_concurrent_streams(&self, dir: Dir) -> u64 { +2278 self.streams.max_concurrent(dir) +2279 } +2280 +2281 /// See [`TransportConfig::send_window()`] +2282 pub fn set_send_window(&mut self, send_window: u64) { +2283 self.streams.set_send_window(send_window); 2284 } 2285 -2286 /// See [`TransportConfig::send_window()`] -2287 pub fn set_send_window(&mut self, send_window: u64) { -2288 self.streams.set_send_window(send_window); -2289 } -2290 -2291 /// See [`TransportConfig::receive_window()`] -2292 pub fn set_receive_window(&mut self, receive_window: VarInt) { -2293 if self.streams.set_receive_window(receive_window) { -2294 self.spaces[SpaceId::Data].pending.max_data = true; -2295 } -2296 } -2297 -2298 /// Whether the Multipath for QUIC extension is enabled. -2299 /// -2300 /// Multipath is only enabled after the handshake is completed and if it was enabled by both -2301 /// peers. -2302 pub fn is_multipath_negotiated(&self) -> bool { -2303 !self.is_handshaking() -2304 && self.config.max_concurrent_multipath_paths.is_some() -2305 && self.peer_params.initial_max_path_id.is_some() -2306 } -2307 -2308 fn on_ack_received( -2309 &mut self, -2310 now: Instant, -2311 space: SpaceId, -2312 ack: frame::Ack, -2313 ) -> Result<(), TransportError> { -2314 // All ACKs are referencing path 0 -2315 let path = PathId::ZERO; -2316 self.inner_on_ack_received(now, space, path, ack) -2317 } -2318 -2319 fn on_path_ack_received( -2320 &mut self, -2321 now: Instant, -2322 space: SpaceId, -2323 path_ack: frame::PathAck, -2324 ) -> Result<(), TransportError> { -2325 let (ack, path) = path_ack.into_ack(); -2326 self.inner_on_ack_received(now, space, path, ack) -2327 } -2328 -2329 /// Handles an ACK frame acknowledging packets sent on *path*. -2330 fn inner_on_ack_received( -2331 &mut self, -2332 now: Instant, -2333 space: SpaceId, -2334 path: PathId, -2335 ack: frame::Ack, -2336 ) -> Result<(), TransportError> { -2337 if self.abandoned_paths.contains(&path) { -2338 // See also https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.4.3-3 -2339 // > PATH_ACK frames received with an abandoned path ID are silently ignored, as specified in Section 4. -2340 trace!("silently ignoring PATH_ACK on abandoned path"); -2341 return Ok(()); -2342 } -2343 if ack.largest >= self.spaces[space].for_path(path).next_packet_number { -2344 return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked")); -2345 } -2346 let new_largest = { -2347 let space = &mut self.spaces[space].for_path(path); -2348 if space.largest_acked_packet.is_none_or(|pn| ack.largest > pn) { -2349 space.largest_acked_packet = Some(ack.largest); -2350 if let Some(info) = space.sent_packets.get(ack.largest) { -2351 // This should always succeed, but a misbehaving peer might ACK a packet we -2352 // haven't sent. At worst, that will result in us spuriously reducing the -2353 // congestion window. -2354 space.largest_acked_packet_sent = info.time_sent; -2355 } -2356 true -2357 } else { -2358 false -2359 } -2360 }; -2361 -2362 if self.detect_spurious_loss(&ack, space, path) { -2363 self.path_data_mut(path) -2364 .congestion -2365 .on_spurious_congestion_event(); -2366 } -2367 -2368 // Avoid DoS from unreasonably huge ack ranges by filtering out just the new acks. -2369 let mut newly_acked = ArrayRangeSet::new(); -2370 for range in ack.iter() { -2371 self.spaces[space].for_path(path).check_ack(range.clone())?; -2372 for (pn, _) in self.spaces[space] -2373 .for_path(path) -2374 .sent_packets -2375 .iter_range(range) -2376 { -2377 newly_acked.insert_one(pn); -2378 } -2379 } -2380 -2381 if newly_acked.is_empty() { -2382 return Ok(()); -2383 } -2384 -2385 let mut ack_eliciting_acked = false; -2386 for packet in newly_acked.elts() { -2387 if let Some(info) = self.spaces[space].for_path(path).take(packet) { -2388 for (acked_path_id, acked_pn) in info.largest_acked.iter() { -2389 // Assume ACKs for all packets below the largest acknowledged in -2390 // `packet` have been received. This can cause the peer to spuriously -2391 // retransmit if some of our earlier ACKs were lost, but allows for -2392 // simpler state tracking. See discussion at -2393 // https://www.rfc-editor.org/rfc/rfc9000.html#name-limiting-ranges-by-tracking -2394 if let Some(pns) = self.spaces[space].path_space_mut(*acked_path_id) { -2395 pns.pending_acks.subtract_below(*acked_pn); -2396 } -2397 } -2398 ack_eliciting_acked |= info.ack_eliciting; -2399 -2400 // Notify MTU discovery that a packet was acked, because it might be an MTU probe -2401 let path_data = self.path_data_mut(path); -2402 let mtu_updated = path_data.mtud.on_acked(space, packet, info.size); -2403 if mtu_updated { -2404 path_data -2405 .congestion -2406 .on_mtu_update(path_data.mtud.current_mtu()); -2407 } -2408 -2409 // Notify ack frequency that a packet was acked, because it might contain an ACK_FREQUENCY frame -2410 self.ack_frequency.on_acked(path, packet); -2411 -2412 self.on_packet_acked(now, path, info); -2413 } -2414 } +2286 /// See [`TransportConfig::receive_window()`] +2287 pub fn set_receive_window(&mut self, receive_window: VarInt) { +2288 if self.streams.set_receive_window(receive_window) { +2289 self.spaces[SpaceId::Data].pending.max_data = true; +2290 } +2291 } +2292 +2293 /// Whether the Multipath for QUIC extension is enabled. +2294 /// +2295 /// Multipath is only enabled after the handshake is completed and if it was enabled by both +2296 /// peers. +2297 pub fn is_multipath_negotiated(&self) -> bool { +2298 !self.is_handshaking() +2299 && self.config.max_concurrent_multipath_paths.is_some() +2300 && self.peer_params.initial_max_path_id.is_some() +2301 } +2302 +2303 fn on_ack_received( +2304 &mut self, +2305 now: Instant, +2306 space: SpaceId, +2307 ack: frame::Ack, +2308 ) -> Result<(), TransportError> { +2309 // All ACKs are referencing path 0 +2310 let path = PathId::ZERO; +2311 self.inner_on_ack_received(now, space, path, ack) +2312 } +2313 +2314 fn on_path_ack_received( +2315 &mut self, +2316 now: Instant, +2317 space: SpaceId, +2318 path_ack: frame::PathAck, +2319 ) -> Result<(), TransportError> { +2320 let (ack, path) = path_ack.into_ack(); +2321 self.inner_on_ack_received(now, space, path, ack) +2322 } +2323 +2324 /// Handles an ACK frame acknowledging packets sent on *path*. +2325 fn inner_on_ack_received( +2326 &mut self, +2327 now: Instant, +2328 space: SpaceId, +2329 path: PathId, +2330 ack: frame::Ack, +2331 ) -> Result<(), TransportError> { +2332 if self.abandoned_paths.contains(&path) { +2333 // See also https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.4.3-3 +2334 // > PATH_ACK frames received with an abandoned path ID are silently ignored, as specified in Section 4. +2335 trace!("silently ignoring PATH_ACK on abandoned path"); +2336 return Ok(()); +2337 } +2338 if ack.largest >= self.spaces[space].for_path(path).next_packet_number { +2339 return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked")); +2340 } +2341 let new_largest = { +2342 let space = &mut self.spaces[space].for_path(path); +2343 if space.largest_acked_packet.is_none_or(|pn| ack.largest > pn) { +2344 space.largest_acked_packet = Some(ack.largest); +2345 if let Some(info) = space.sent_packets.get(ack.largest) { +2346 // This should always succeed, but a misbehaving peer might ACK a packet we +2347 // haven't sent. At worst, that will result in us spuriously reducing the +2348 // congestion window. +2349 space.largest_acked_packet_sent = info.time_sent; +2350 } +2351 true +2352 } else { +2353 false +2354 } +2355 }; +2356 +2357 if self.detect_spurious_loss(&ack, space, path) { +2358 self.path_data_mut(path) +2359 .congestion +2360 .on_spurious_congestion_event(); +2361 } +2362 +2363 // Avoid DoS from unreasonably huge ack ranges by filtering out just the new acks. +2364 let mut newly_acked = ArrayRangeSet::new(); +2365 for range in ack.iter() { +2366 self.spaces[space].for_path(path).check_ack(range.clone())?; +2367 for (pn, _) in self.spaces[space] +2368 .for_path(path) +2369 .sent_packets +2370 .iter_range(range) +2371 { +2372 newly_acked.insert_one(pn); +2373 } +2374 } +2375 +2376 if newly_acked.is_empty() { +2377 return Ok(()); +2378 } +2379 +2380 let mut ack_eliciting_acked = false; +2381 for packet in newly_acked.elts() { +2382 if let Some(info) = self.spaces[space].for_path(path).take(packet) { +2383 for (acked_path_id, acked_pn) in info.largest_acked.iter() { +2384 // Assume ACKs for all packets below the largest acknowledged in +2385 // `packet` have been received. This can cause the peer to spuriously +2386 // retransmit if some of our earlier ACKs were lost, but allows for +2387 // simpler state tracking. See discussion at +2388 // https://www.rfc-editor.org/rfc/rfc9000.html#name-limiting-ranges-by-tracking +2389 if let Some(pns) = self.spaces[space].path_space_mut(*acked_path_id) { +2390 pns.pending_acks.subtract_below(*acked_pn); +2391 } +2392 } +2393 ack_eliciting_acked |= info.ack_eliciting; +2394 +2395 // Notify MTU discovery that a packet was acked, because it might be an MTU probe +2396 let path_data = self.path_data_mut(path); +2397 let mtu_updated = path_data.mtud.on_acked(space, packet, info.size); +2398 if mtu_updated { +2399 path_data +2400 .congestion +2401 .on_mtu_update(path_data.mtud.current_mtu()); +2402 } +2403 +2404 // Notify ack frequency that a packet was acked, because it might contain an ACK_FREQUENCY frame +2405 self.ack_frequency.on_acked(path, packet); +2406 +2407 self.on_packet_acked(now, path, info); +2408 } +2409 } +2410 +2411 let largest_ackd = self.spaces[space].for_path(path).largest_acked_packet; +2412 let app_limited = self.app_limited; +2413 let path_data = self.path_data_mut(path); +2414 let in_flight = path_data.in_flight.bytes; 2415 -2416 let largest_ackd = self.spaces[space].for_path(path).largest_acked_packet; -2417 let app_limited = self.app_limited; -2418 let path_data = self.path_data_mut(path); -2419 let in_flight = path_data.in_flight.bytes; -2420 -2421 path_data -2422 .congestion -2423 .on_end_acks(now, in_flight, app_limited, largest_ackd); -2424 -2425 if new_largest && ack_eliciting_acked { -2426 let ack_delay = if space != SpaceId::Data { -2427 Duration::from_micros(0) -2428 } else { -2429 cmp::min( -2430 self.ack_frequency.peer_max_ack_delay, -2431 Duration::from_micros(ack.delay << self.peer_params.ack_delay_exponent.0), -2432 ) -2433 }; -2434 let rtt = now.saturating_duration_since( -2435 self.spaces[space].for_path(path).largest_acked_packet_sent, -2436 ); -2437 -2438 let next_pn = self.spaces[space].for_path(path).next_packet_number; -2439 let path_data = self.path_data_mut(path); -2440 // TODO(@divma): should be a method of path, should be contained in a single place -2441 path_data.rtt.update(ack_delay, rtt); -2442 if path_data.first_packet_after_rtt_sample.is_none() { -2443 path_data.first_packet_after_rtt_sample = Some((space, next_pn)); -2444 } -2445 } -2446 -2447 // Must be called before crypto/pto_count are clobbered -2448 self.detect_lost_packets(now, space, path, true); -2449 -2450 if self.peer_completed_address_validation(path) { -2451 self.path_data_mut(path).pto_count = 0; -2452 } -2453 -2454 // Explicit congestion notification -2455 // TODO(@divma): this code is a good example of logic that should be contained in a single -2456 // place but it's split between the path data and the packet number space data, we should -2457 // find a way to make this work without two lookups -2458 if self.path_data(path).sending_ecn { -2459 if let Some(ecn) = ack.ecn { -2460 // We only examine ECN counters from ACKs that we are certain we received in transmit -2461 // order, allowing us to compute an increase in ECN counts to compare against the number -2462 // of newly acked packets that remains well-defined in the presence of arbitrary packet -2463 // reordering. -2464 if new_largest { -2465 let sent = self.spaces[space].for_path(path).largest_acked_packet_sent; -2466 self.process_ecn(now, space, path, newly_acked.len() as u64, ecn, sent); -2467 } -2468 } else { -2469 // We always start out sending ECN, so any ack that doesn't acknowledge it disables it. -2470 debug!("ECN not acknowledged by peer"); -2471 self.path_data_mut(path).sending_ecn = false; -2472 } -2473 } -2474 -2475 self.set_loss_detection_timer(now, path); -2476 Ok(()) -2477 } -2478 -2479 fn detect_spurious_loss(&mut self, ack: &frame::Ack, space: SpaceId, path: PathId) -> bool { -2480 let lost_packets = &mut self.spaces[space].for_path(path).lost_packets; -2481 -2482 if lost_packets.is_empty() { -2483 return false; -2484 } -2485 -2486 for range in ack.iter() { -2487 let spurious_losses: Vec<u64> = lost_packets -2488 .iter_range(range.clone()) -2489 .map(|(pn, _info)| pn) -2490 .collect(); +2416 path_data +2417 .congestion +2418 .on_end_acks(now, in_flight, app_limited, largest_ackd); +2419 +2420 if new_largest && ack_eliciting_acked { +2421 let ack_delay = if space != SpaceId::Data { +2422 Duration::from_micros(0) +2423 } else { +2424 cmp::min( +2425 self.ack_frequency.peer_max_ack_delay, +2426 Duration::from_micros(ack.delay << self.peer_params.ack_delay_exponent.0), +2427 ) +2428 }; +2429 let rtt = now.saturating_duration_since( +2430 self.spaces[space].for_path(path).largest_acked_packet_sent, +2431 ); +2432 +2433 let next_pn = self.spaces[space].for_path(path).next_packet_number; +2434 let path_data = self.path_data_mut(path); +2435 // TODO(@divma): should be a method of path, should be contained in a single place +2436 path_data.rtt.update(ack_delay, rtt); +2437 if path_data.first_packet_after_rtt_sample.is_none() { +2438 path_data.first_packet_after_rtt_sample = Some((space, next_pn)); +2439 } +2440 } +2441 +2442 // Must be called before crypto/pto_count are clobbered +2443 self.detect_lost_packets(now, space, path, true); +2444 +2445 if self.peer_completed_address_validation(path) { +2446 self.path_data_mut(path).pto_count = 0; +2447 } +2448 +2449 // Explicit congestion notification +2450 // TODO(@divma): this code is a good example of logic that should be contained in a single +2451 // place but it's split between the path data and the packet number space data, we should +2452 // find a way to make this work without two lookups +2453 if self.path_data(path).sending_ecn { +2454 if let Some(ecn) = ack.ecn { +2455 // We only examine ECN counters from ACKs that we are certain we received in transmit +2456 // order, allowing us to compute an increase in ECN counts to compare against the number +2457 // of newly acked packets that remains well-defined in the presence of arbitrary packet +2458 // reordering. +2459 if new_largest { +2460 let sent = self.spaces[space].for_path(path).largest_acked_packet_sent; +2461 self.process_ecn(now, space, path, newly_acked.len() as u64, ecn, sent); +2462 } +2463 } else { +2464 // We always start out sending ECN, so any ack that doesn't acknowledge it disables it. +2465 debug!("ECN not acknowledged by peer"); +2466 self.path_data_mut(path).sending_ecn = false; +2467 } +2468 } +2469 +2470 self.set_loss_detection_timer(now, path); +2471 Ok(()) +2472 } +2473 +2474 fn detect_spurious_loss(&mut self, ack: &frame::Ack, space: SpaceId, path: PathId) -> bool { +2475 let lost_packets = &mut self.spaces[space].for_path(path).lost_packets; +2476 +2477 if lost_packets.is_empty() { +2478 return false; +2479 } +2480 +2481 for range in ack.iter() { +2482 let spurious_losses: Vec<u64> = lost_packets +2483 .iter_range(range.clone()) +2484 .map(|(pn, _info)| pn) +2485 .collect(); +2486 +2487 for pn in spurious_losses { +2488 lost_packets.remove(pn); +2489 } +2490 } 2491 -2492 for pn in spurious_losses { -2493 lost_packets.remove(pn); -2494 } -2495 } -2496 -2497 // If this ACK frame acknowledged all deemed lost packets, -2498 // then we have raised a spurious congestion event in the past. -2499 // We cannot conclude when there are remaining packets, -2500 // but future ACK frames might indicate a spurious loss detection. -2501 lost_packets.is_empty() -2502 } -2503 -2504 /// Drain lost packets that we reasonably think will never arrive -2505 /// -2506 /// The current criterion is copied from `msquic`: -2507 /// discard packets that were sent earlier than 2 probe timeouts ago. -2508 fn drain_lost_packets(&mut self, now: Instant, space: SpaceId, path: PathId) { -2509 let two_pto = 2 * self.path_data(path).rtt.pto_base(); -2510 -2511 let lost_packets = &mut self.spaces[space].for_path(path).lost_packets; -2512 lost_packets.retain(|_pn, info| now.saturating_duration_since(info.time_sent) <= two_pto); -2513 } -2514 -2515 /// Process a new ECN block from an in-order ACK -2516 fn process_ecn( -2517 &mut self, -2518 now: Instant, -2519 space: SpaceId, -2520 path: PathId, -2521 newly_acked: u64, -2522 ecn: frame::EcnCounts, -2523 largest_sent_time: Instant, -2524 ) { -2525 match self.spaces[space] -2526 .for_path(path) -2527 .detect_ecn(newly_acked, ecn) -2528 { -2529 Err(e) => { -2530 debug!("halting ECN due to verification failure: {}", e); -2531 -2532 self.path_data_mut(path).sending_ecn = false; -2533 // Wipe out the existing value because it might be garbage and could interfere with -2534 // future attempts to use ECN on new paths. -2535 self.spaces[space].for_path(path).ecn_feedback = frame::EcnCounts::ZERO; -2536 } -2537 Ok(false) => {} -2538 Ok(true) => { -2539 self.path_stats.entry(path).or_default().congestion_events += 1; -2540 self.path_data_mut(path).congestion.on_congestion_event( -2541 now, -2542 largest_sent_time, -2543 false, -2544 true, -2545 0, -2546 ); -2547 } -2548 } -2549 } -2550 -2551 // Not timing-aware, so it's safe to call this for inferred acks, such as arise from -2552 // high-latency handshakes -2553 fn on_packet_acked(&mut self, now: Instant, path_id: PathId, info: SentPacket) { -2554 self.paths -2555 .get_mut(&path_id) -2556 .expect("known path") -2557 .remove_in_flight(&info); -2558 let app_limited = self.app_limited; -2559 let path = self.path_data_mut(path_id); -2560 if info.ack_eliciting && !path.is_validating_path() { -2561 // Only pass ACKs to the congestion controller if we are not validating the current -2562 // path, so as to ignore any ACKs from older paths still coming in. -2563 let rtt = path.rtt; -2564 path.congestion -2565 .on_ack(now, info.time_sent, info.size.into(), app_limited, &rtt); -2566 } -2567 -2568 // Update state for confirmed delivery of frames -2569 if let Some(retransmits) = info.retransmits.get() { -2570 for (id, _) in retransmits.reset_stream.iter() { -2571 self.streams.reset_acked(*id); -2572 } -2573 } +2492 // If this ACK frame acknowledged all deemed lost packets, +2493 // then we have raised a spurious congestion event in the past. +2494 // We cannot conclude when there are remaining packets, +2495 // but future ACK frames might indicate a spurious loss detection. +2496 lost_packets.is_empty() +2497 } +2498 +2499 /// Drain lost packets that we reasonably think will never arrive +2500 /// +2501 /// The current criterion is copied from `msquic`: +2502 /// discard packets that were sent earlier than 2 probe timeouts ago. +2503 fn drain_lost_packets(&mut self, now: Instant, space: SpaceId, path: PathId) { +2504 let two_pto = 2 * self.path_data(path).rtt.pto_base(); +2505 +2506 let lost_packets = &mut self.spaces[space].for_path(path).lost_packets; +2507 lost_packets.retain(|_pn, info| now.saturating_duration_since(info.time_sent) <= two_pto); +2508 } +2509 +2510 /// Process a new ECN block from an in-order ACK +2511 fn process_ecn( +2512 &mut self, +2513 now: Instant, +2514 space: SpaceId, +2515 path: PathId, +2516 newly_acked: u64, +2517 ecn: frame::EcnCounts, +2518 largest_sent_time: Instant, +2519 ) { +2520 match self.spaces[space] +2521 .for_path(path) +2522 .detect_ecn(newly_acked, ecn) +2523 { +2524 Err(e) => { +2525 debug!("halting ECN due to verification failure: {}", e); +2526 +2527 self.path_data_mut(path).sending_ecn = false; +2528 // Wipe out the existing value because it might be garbage and could interfere with +2529 // future attempts to use ECN on new paths. +2530 self.spaces[space].for_path(path).ecn_feedback = frame::EcnCounts::ZERO; +2531 } +2532 Ok(false) => {} +2533 Ok(true) => { +2534 self.path_stats.entry(path).or_default().congestion_events += 1; +2535 self.path_data_mut(path).congestion.on_congestion_event( +2536 now, +2537 largest_sent_time, +2538 false, +2539 true, +2540 0, +2541 ); +2542 } +2543 } +2544 } +2545 +2546 // Not timing-aware, so it's safe to call this for inferred acks, such as arise from +2547 // high-latency handshakes +2548 fn on_packet_acked(&mut self, now: Instant, path_id: PathId, info: SentPacket) { +2549 self.paths +2550 .get_mut(&path_id) +2551 .expect("known path") +2552 .remove_in_flight(&info); +2553 let app_limited = self.app_limited; +2554 let path = self.path_data_mut(path_id); +2555 if info.ack_eliciting && !path.is_validating_path() { +2556 // Only pass ACKs to the congestion controller if we are not validating the current +2557 // path, so as to ignore any ACKs from older paths still coming in. +2558 let rtt = path.rtt; +2559 path.congestion +2560 .on_ack(now, info.time_sent, info.size.into(), app_limited, &rtt); +2561 } +2562 +2563 // Update state for confirmed delivery of frames +2564 if let Some(retransmits) = info.retransmits.get() { +2565 for (id, _) in retransmits.reset_stream.iter() { +2566 self.streams.reset_acked(*id); +2567 } +2568 } +2569 +2570 for frame in info.stream_frames { +2571 self.streams.received_ack_of(frame); +2572 } +2573 } 2574 -2575 for frame in info.stream_frames { -2576 self.streams.received_ack_of(frame); -2577 } -2578 } -2579 -2580 fn set_key_discard_timer(&mut self, now: Instant, space: SpaceId) { -2581 let start = if self.zero_rtt_crypto.is_some() { -2582 now -2583 } else { -2584 self.prev_crypto -2585 .as_ref() -2586 .expect("no previous keys") -2587 .end_packet -2588 .as_ref() -2589 .expect("update not acknowledged yet") -2590 .1 -2591 }; -2592 -2593 // QUIC-MULTIPATH § 2.5 Key Phase Update Process: use largest PTO off all paths. -2594 self.timers.set( -2595 Timer::Conn(ConnTimer::KeyDiscard), -2596 start + self.pto_max_path(space, false) * 3, -2597 self.qlog.with_time(now), -2598 ); -2599 } -2600 -2601 /// Handle a [`PathTimer::LossDetection`] timeout. -2602 /// -2603 /// This timer expires for two reasons: -2604 /// - An ACK-eliciting packet we sent should be considered lost. -2605 /// - The PTO may have expired and a tail-loss probe needs to be scheduled. -2606 /// -2607 /// The former needs us to schedule re-transmission of the lost data. -2608 /// -2609 /// The latter means we have not received an ACK for an ack-eliciting packet we sent -2610 /// within the PTO time-window. We need to schedule a tail-loss probe, an ack-eliciting -2611 /// packet, to try and elicit new acknowledgements. These new acknowledgements will -2612 /// indicate whether the previously sent packets were lost or not. -2613 fn on_loss_detection_timeout(&mut self, now: Instant, path_id: PathId) { -2614 if let Some((_, pn_space)) = self.loss_time_and_space(path_id) { -2615 // Time threshold loss Detection -2616 self.detect_lost_packets(now, pn_space, path_id, false); -2617 self.set_loss_detection_timer(now, path_id); -2618 return; -2619 } -2620 -2621 let (_, space) = match self.pto_time_and_space(now, path_id) { -2622 Some(x) => x, -2623 None => { -2624 error!(%path_id, "PTO expired while unset"); -2625 return; -2626 } -2627 }; -2628 trace!( -2629 in_flight = self.path_data(path_id).in_flight.bytes, -2630 count = self.path_data(path_id).pto_count, -2631 ?space, -2632 %path_id, -2633 "PTO fired" -2634 ); -2635 -2636 let count = match self.path_data(path_id).in_flight.ack_eliciting { -2637 // A PTO when we're not expecting any ACKs must be due to handshake anti-amplification -2638 // deadlock preventions -2639 0 => { -2640 debug_assert!(!self.peer_completed_address_validation(path_id)); -2641 1 -2642 } -2643 // Conventional loss probe -2644 _ => 2, -2645 }; -2646 let pns = self.spaces[space].for_path(path_id); -2647 pns.loss_probes = pns.loss_probes.saturating_add(count); -2648 let path_data = self.path_data_mut(path_id); -2649 path_data.pto_count = path_data.pto_count.saturating_add(1); -2650 self.set_loss_detection_timer(now, path_id); -2651 } -2652 -2653 /// Detect any lost packets -2654 /// -2655 /// There are two cases in which we detects lost packets: +2575 fn set_key_discard_timer(&mut self, now: Instant, space: SpaceId) { +2576 let start = if self.zero_rtt_crypto.is_some() { +2577 now +2578 } else { +2579 self.prev_crypto +2580 .as_ref() +2581 .expect("no previous keys") +2582 .end_packet +2583 .as_ref() +2584 .expect("update not acknowledged yet") +2585 .1 +2586 }; +2587 +2588 // QUIC-MULTIPATH § 2.5 Key Phase Update Process: use largest PTO off all paths. +2589 self.timers.set( +2590 Timer::Conn(ConnTimer::KeyDiscard), +2591 start + self.pto_max_path(space, false) * 3, +2592 self.qlog.with_time(now), +2593 ); +2594 } +2595 +2596 /// Handle a [`PathTimer::LossDetection`] timeout. +2597 /// +2598 /// This timer expires for two reasons: +2599 /// - An ACK-eliciting packet we sent should be considered lost. +2600 /// - The PTO may have expired and a tail-loss probe needs to be scheduled. +2601 /// +2602 /// The former needs us to schedule re-transmission of the lost data. +2603 /// +2604 /// The latter means we have not received an ACK for an ack-eliciting packet we sent +2605 /// within the PTO time-window. We need to schedule a tail-loss probe, an ack-eliciting +2606 /// packet, to try and elicit new acknowledgements. These new acknowledgements will +2607 /// indicate whether the previously sent packets were lost or not. +2608 fn on_loss_detection_timeout(&mut self, now: Instant, path_id: PathId) { +2609 if let Some((_, pn_space)) = self.loss_time_and_space(path_id) { +2610 // Time threshold loss Detection +2611 self.detect_lost_packets(now, pn_space, path_id, false); +2612 self.set_loss_detection_timer(now, path_id); +2613 return; +2614 } +2615 +2616 let (_, space) = match self.pto_time_and_space(now, path_id) { +2617 Some(x) => x, +2618 None => { +2619 error!(%path_id, "PTO expired while unset"); +2620 return; +2621 } +2622 }; +2623 trace!( +2624 in_flight = self.path_data(path_id).in_flight.bytes, +2625 count = self.path_data(path_id).pto_count, +2626 ?space, +2627 %path_id, +2628 "PTO fired" +2629 ); +2630 +2631 let count = match self.path_data(path_id).in_flight.ack_eliciting { +2632 // A PTO when we're not expecting any ACKs must be due to handshake anti-amplification +2633 // deadlock preventions +2634 0 => { +2635 debug_assert!(!self.peer_completed_address_validation(path_id)); +2636 1 +2637 } +2638 // Conventional loss probe +2639 _ => 2, +2640 }; +2641 let pns = self.spaces[space].for_path(path_id); +2642 pns.loss_probes = pns.loss_probes.saturating_add(count); +2643 let path_data = self.path_data_mut(path_id); +2644 path_data.pto_count = path_data.pto_count.saturating_add(1); +2645 self.set_loss_detection_timer(now, path_id); +2646 } +2647 +2648 /// Detect any lost packets +2649 /// +2650 /// There are two cases in which we detects lost packets: +2651 /// +2652 /// - We received an ACK packet. +2653 /// - The [`PathTimer::LossDetection`] timer expired. So there is an un-acknowledged packet +2654 /// that was followed by an acknowledged packet. The loss timer for this +2655 /// un-acknowledged packet expired and we need to detect that packet as lost. 2656 /// -2657 /// - We received an ACK packet. -2658 /// - The [`PathTimer::LossDetection`] timer expired. So there is an un-acknowledged packet -2659 /// that was followed by an acknowledged packet. The loss timer for this -2660 /// un-acknowledged packet expired and we need to detect that packet as lost. -2661 /// -2662 /// Packets are lost if they are both (See RFC9002 §6.1): -2663 /// -2664 /// - Unacknowledged, in flight and sent prior to an acknowledged packet. -2665 /// - Old enough by either: -2666 /// - Having a packet number [`TransportConfig::packet_threshold`] lower then the last -2667 /// acknowledged packet. -2668 /// - Being sent [`TransportConfig::time_threshold`] * RTT in the past. -2669 fn detect_lost_packets( -2670 &mut self, -2671 now: Instant, -2672 pn_space: SpaceId, -2673 path_id: PathId, -2674 due_to_ack: bool, -2675 ) { -2676 let mut lost_packets = Vec::<u64>::new(); -2677 let mut lost_mtu_probe = None; -2678 let mut in_persistent_congestion = false; -2679 let mut size_of_lost_packets = 0u64; -2680 self.spaces[pn_space].for_path(path_id).loss_time = None; -2681 -2682 // Find all the lost packets, populating all variables initialised above. -2683 -2684 let path = self.path_data(path_id); -2685 let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe(); -2686 let loss_delay = path -2687 .rtt -2688 .conservative() -2689 .mul_f32(self.config.time_threshold) -2690 .max(TIMER_GRANULARITY); -2691 let first_packet_after_rtt_sample = path.first_packet_after_rtt_sample; -2692 -2693 let largest_acked_packet = self.spaces[pn_space] -2694 .for_path(path_id) -2695 .largest_acked_packet -2696 .expect("detect_lost_packets only to be called if path received at least one ACK"); -2697 let packet_threshold = self.config.packet_threshold as u64; -2698 -2699 // InPersistentCongestion: Determine if all packets in the time period before the newest -2700 // lost packet, including the edges, are marked lost. PTO computation must always -2701 // include max ACK delay, i.e. operate as if in Data space (see RFC9001 §7.6.1). -2702 let congestion_period = self -2703 .pto(SpaceId::Data, path_id) -2704 .saturating_mul(self.config.persistent_congestion_threshold); -2705 let mut persistent_congestion_start: Option<Instant> = None; -2706 let mut prev_packet = None; -2707 let space = self.spaces[pn_space].for_path(path_id); -2708 -2709 for (packet, info) in space.sent_packets.iter_range(0..largest_acked_packet) { -2710 if prev_packet != Some(packet.wrapping_sub(1)) { -2711 // An intervening packet was acknowledged -2712 persistent_congestion_start = None; -2713 } -2714 -2715 // Packets sent before now - loss_delay are deemed lost. -2716 // However, we avoid subtraction as it can panic and there's no -2717 // saturating equivalent of this subtraction operation with a Duration. -2718 let packet_too_old = now.saturating_duration_since(info.time_sent) >= loss_delay; -2719 if packet_too_old || largest_acked_packet >= packet + packet_threshold { -2720 // The packet should be declared lost. -2721 if Some(packet) == in_flight_mtu_probe { -2722 // Lost MTU probes are not included in `lost_packets`, because they -2723 // should not trigger a congestion control response -2724 lost_mtu_probe = in_flight_mtu_probe; -2725 } else { -2726 lost_packets.push(packet); -2727 size_of_lost_packets += info.size as u64; -2728 if info.ack_eliciting && due_to_ack { -2729 match persistent_congestion_start { -2730 // Two ACK-eliciting packets lost more than -2731 // congestion_period apart, with no ACKed packets in between -2732 Some(start) if info.time_sent - start > congestion_period => { -2733 in_persistent_congestion = true; -2734 } -2735 // Persistent congestion must start after the first RTT sample -2736 None if first_packet_after_rtt_sample -2737 .is_some_and(|x| x < (pn_space, packet)) => -2738 { -2739 persistent_congestion_start = Some(info.time_sent); -2740 } -2741 _ => {} -2742 } -2743 } -2744 } -2745 } else { -2746 // The packet should not yet be declared lost. -2747 if space.loss_time.is_none() { -2748 // Since we iterate in order the lowest packet number's loss time will -2749 // always be the earliest. -2750 space.loss_time = Some(info.time_sent + loss_delay); -2751 } -2752 persistent_congestion_start = None; -2753 } -2754 -2755 prev_packet = Some(packet); -2756 } -2757 -2758 self.handle_lost_packets( -2759 pn_space, -2760 path_id, -2761 now, -2762 lost_packets, -2763 lost_mtu_probe, -2764 loss_delay, -2765 in_persistent_congestion, -2766 size_of_lost_packets, -2767 ); -2768 } -2769 -2770 /// Drops the path state, declaring any remaining in-flight packets as lost -2771 fn discard_path(&mut self, path_id: PathId, now: Instant) { -2772 trace!(%path_id, "dropping path state"); -2773 let path = self.path_data(path_id); -2774 let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe(); -2775 -2776 let mut size_of_lost_packets = 0u64; // add to path_stats.lost_bytes; -2777 let lost_pns: Vec<_> = self.spaces[SpaceId::Data] -2778 .for_path(path_id) -2779 .sent_packets -2780 .iter() -2781 .filter(|(pn, _info)| Some(*pn) != in_flight_mtu_probe) -2782 .map(|(pn, info)| { -2783 size_of_lost_packets += info.size as u64; -2784 pn -2785 }) -2786 .collect(); -2787 -2788 if !lost_pns.is_empty() { -2789 trace!( -2790 %path_id, -2791 count = lost_pns.len(), -2792 lost_bytes = size_of_lost_packets, -2793 "packets lost on path abandon" -2794 ); -2795 self.handle_lost_packets( -2796 SpaceId::Data, -2797 path_id, -2798 now, -2799 lost_pns, -2800 in_flight_mtu_probe, -2801 Duration::ZERO, -2802 false, -2803 size_of_lost_packets, -2804 ); -2805 } -2806 self.paths.remove(&path_id); -2807 self.spaces[SpaceId::Data].number_spaces.remove(&path_id); -2808 -2809 let path_stats = self.path_stats.remove(&path_id).unwrap_or_default(); -2810 self.events.push_back( -2811 PathEvent::Abandoned { -2812 id: path_id, -2813 path_stats, -2814 } -2815 .into(), -2816 ); -2817 } -2818 -2819 fn handle_lost_packets( -2820 &mut self, -2821 pn_space: SpaceId, -2822 path_id: PathId, -2823 now: Instant, -2824 lost_packets: Vec<u64>, -2825 lost_mtu_probe: Option<u64>, -2826 loss_delay: Duration, -2827 in_persistent_congestion: bool, -2828 size_of_lost_packets: u64, -2829 ) { -2830 debug_assert!( -2831 { -2832 let mut sorted = lost_packets.clone(); -2833 sorted.sort(); -2834 sorted == lost_packets -2835 }, -2836 "lost_packets must be sorted" -2837 ); -2838 -2839 self.drain_lost_packets(now, pn_space, path_id); -2840 -2841 // OnPacketsLost -2842 if let Some(largest_lost) = lost_packets.last().cloned() { -2843 let old_bytes_in_flight = self.path_data_mut(path_id).in_flight.bytes; -2844 let largest_lost_sent = self.spaces[pn_space] -2845 .for_path(path_id) -2846 .sent_packets -2847 .get(largest_lost) -2848 .unwrap() -2849 .time_sent; -2850 let path_stats = self.path_stats.entry(path_id).or_default(); -2851 path_stats.lost_packets += lost_packets.len() as u64; -2852 path_stats.lost_bytes += size_of_lost_packets; -2853 trace!( -2854 %path_id, -2855 count = lost_packets.len(), -2856 lost_bytes = size_of_lost_packets, -2857 "packets lost", -2858 ); -2859 -2860 for &packet in &lost_packets { -2861 let Some(info) = self.spaces[pn_space].for_path(path_id).take(packet) else { -2862 continue; -2863 }; -2864 self.qlog -2865 .emit_packet_lost(packet, &info, loss_delay, pn_space, now); -2866 self.paths -2867 .get_mut(&path_id) -2868 .unwrap() -2869 .remove_in_flight(&info); -2870 -2871 for frame in info.stream_frames { -2872 self.streams.retransmit(frame); -2873 } -2874 self.spaces[pn_space].pending |= info.retransmits; -2875 self.path_data_mut(path_id) -2876 .mtud -2877 .on_non_probe_lost(packet, info.size); -2878 -2879 self.spaces[pn_space].for_path(path_id).lost_packets.insert( -2880 packet, -2881 LostPacket { -2882 time_sent: info.time_sent, -2883 }, -2884 ); -2885 } -2886 -2887 let path = self.path_data_mut(path_id); -2888 if path.mtud.black_hole_detected(now) { -2889 path.congestion.on_mtu_update(path.mtud.current_mtu()); -2890 if let Some(max_datagram_size) = self.datagrams().max_size() { -2891 self.datagrams.drop_oversized(max_datagram_size); -2892 } -2893 self.path_stats -2894 .entry(path_id) -2895 .or_default() -2896 .black_holes_detected += 1; -2897 } -2898 -2899 // Don't apply congestion penalty for lost ack-only packets -2900 let lost_ack_eliciting = -2901 old_bytes_in_flight != self.path_data_mut(path_id).in_flight.bytes; -2902 -2903 if lost_ack_eliciting { -2904 self.path_stats -2905 .entry(path_id) -2906 .or_default() -2907 .congestion_events += 1; -2908 self.path_data_mut(path_id).congestion.on_congestion_event( -2909 now, -2910 largest_lost_sent, -2911 in_persistent_congestion, -2912 false, -2913 size_of_lost_packets, -2914 ); -2915 } -2916 } -2917 -2918 // Handle a lost MTU probe -2919 if let Some(packet) = lost_mtu_probe { -2920 let info = self.spaces[SpaceId::Data] -2921 .for_path(path_id) -2922 .take(packet) -2923 .unwrap(); // safe: lost_mtu_probe is omitted from lost_packets, and -2924 // therefore must not have been removed yet -2925 self.paths -2926 .get_mut(&path_id) -2927 .unwrap() -2928 .remove_in_flight(&info); -2929 self.path_data_mut(path_id).mtud.on_probe_lost(); -2930 self.path_stats -2931 .entry(path_id) -2932 .or_default() -2933 .lost_plpmtud_probes += 1; -2934 } -2935 } -2936 -2937 /// Returns the earliest time packets should be declared lost for all spaces on a path. -2938 /// -2939 /// If a path has an acknowledged packet with any prior un-acknowledged packets, the -2940 /// earliest un-acknowledged packet can be declared lost after a timeout has elapsed. -2941 /// The time returned is when this packet should be declared lost. -2942 fn loss_time_and_space(&self, path_id: PathId) -> Option<(Instant, SpaceId)> { -2943 SpaceId::iter() -2944 .filter_map(|id| { -2945 self.spaces[id] -2946 .number_spaces -2947 .get(&path_id) -2948 .and_then(|pns| pns.loss_time) -2949 .map(|time| (time, id)) -2950 }) -2951 .min_by_key(|&(time, _)| time) -2952 } -2953 -2954 /// Returns the earliest next PTO should fire for all spaces on a path. -2955 fn pto_time_and_space(&mut self, now: Instant, path_id: PathId) -> Option<(Instant, SpaceId)> { -2956 let path = self.path(path_id)?; -2957 let pto_count = path.pto_count; -2958 let backoff = 2u32.pow(pto_count.min(MAX_BACKOFF_EXPONENT)); -2959 let mut duration = path.rtt.pto_base() * backoff; -2960 -2961 if path_id == PathId::ZERO -2962 && path.in_flight.ack_eliciting == 0 -2963 && !self.peer_completed_address_validation(PathId::ZERO) -2964 { -2965 // Address Validation during Connection Establishment: -2966 // https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1. To prevent a -2967 // deadlock if an Initial or Handshake packet from the server is lost and the -2968 // server can not send more due to its anti-amplification limit the client must -2969 // send another packet on PTO. -2970 let space = match self.highest_space { -2971 SpaceId::Handshake => SpaceId::Handshake, -2972 _ => SpaceId::Initial, -2973 }; -2974 -2975 return Some((now + duration, space)); -2976 } -2977 -2978 let mut result = None; -2979 for space in SpaceId::iter() { -2980 let Some(pns) = self.spaces[space].number_spaces.get(&path_id) else { -2981 continue; -2982 }; -2983 -2984 if !pns.has_in_flight() { -2985 continue; -2986 } -2987 if space == SpaceId::Data { -2988 // Skip ApplicationData until handshake completes. -2989 if self.is_handshaking() { -2990 return result; -2991 } -2992 // Include max_ack_delay and backoff for ApplicationData. -2993 duration += self.ack_frequency.max_ack_delay_for_pto() * backoff; -2994 } -2995 let Some(last_ack_eliciting) = pns.time_of_last_ack_eliciting_packet else { -2996 continue; -2997 }; -2998 let pto = last_ack_eliciting + duration; -2999 if result.is_none_or(|(earliest_pto, _)| pto < earliest_pto) { -3000 if path.anti_amplification_blocked(1) { -3001 // Nothing would be able to be sent. -3002 continue; -3003 } -3004 if path.in_flight.ack_eliciting == 0 { -3005 // Nothing ack-eliciting, no PTO to arm/fire. -3006 continue; -3007 } -3008 result = Some((pto, space)); -3009 } -3010 } -3011 result -3012 } -3013 -3014 fn peer_completed_address_validation(&self, path: PathId) -> bool { -3015 // TODO(flub): This logic needs updating for multipath -3016 if self.side.is_server() || self.state.is_closed() { -3017 return true; -3018 } -3019 // The server is guaranteed to have validated our address if any of our handshake or 1-RTT -3020 // packets are acknowledged or we've seen HANDSHAKE_DONE and discarded handshake keys. -3021 self.spaces[SpaceId::Handshake] -3022 .path_space(PathId::ZERO) -3023 .and_then(|pns| pns.largest_acked_packet) -3024 .is_some() -3025 || self.spaces[SpaceId::Data] -3026 .path_space(path) -3027 .and_then(|pns| pns.largest_acked_packet) -3028 .is_some() -3029 || (self.spaces[SpaceId::Data].crypto.is_some() -3030 && self.spaces[SpaceId::Handshake].crypto.is_none()) -3031 } -3032 -3033 /// Resets the the [`PathTimer::LossDetection`] timer to the next instant it may be needed -3034 /// -3035 /// The timer must fire if either: -3036 /// - An ack-eliciting packet we sent needs to be declared lost. -3037 /// - A tail-loss probe needs to be sent. -3038 /// -3039 /// See [`Connection::on_loss_detection_timeout`] for details. -3040 fn set_loss_detection_timer(&mut self, now: Instant, path_id: PathId) { -3041 if self.state.is_closed() { -3042 // No loss detection takes place on closed connections, and `close_common` already -3043 // stopped time timer. Ensure we don't restart it inadvertently, e.g. in response to a -3044 // reordered packet being handled by state-insensitive code. -3045 return; -3046 } -3047 -3048 if let Some((loss_time, _)) = self.loss_time_and_space(path_id) { -3049 // Time threshold loss detection. -3050 self.timers.set( -3051 Timer::PerPath(path_id, PathTimer::LossDetection), -3052 loss_time, -3053 self.qlog.with_time(now), -3054 ); -3055 return; -3056 } -3057 -3058 // Determine which PN space to arm PTO for. -3059 // Calculate PTO duration -3060 if let Some((timeout, _)) = self.pto_time_and_space(now, path_id) { -3061 self.timers.set( -3062 Timer::PerPath(path_id, PathTimer::LossDetection), -3063 timeout, +2657 /// Packets are lost if they are both (See RFC9002 §6.1): +2658 /// +2659 /// - Unacknowledged, in flight and sent prior to an acknowledged packet. +2660 /// - Old enough by either: +2661 /// - Having a packet number [`TransportConfig::packet_threshold`] lower then the last +2662 /// acknowledged packet. +2663 /// - Being sent [`TransportConfig::time_threshold`] * RTT in the past. +2664 fn detect_lost_packets( +2665 &mut self, +2666 now: Instant, +2667 pn_space: SpaceId, +2668 path_id: PathId, +2669 due_to_ack: bool, +2670 ) { +2671 let mut lost_packets = Vec::<u64>::new(); +2672 let mut lost_mtu_probe = None; +2673 let mut in_persistent_congestion = false; +2674 let mut size_of_lost_packets = 0u64; +2675 self.spaces[pn_space].for_path(path_id).loss_time = None; +2676 +2677 // Find all the lost packets, populating all variables initialised above. +2678 +2679 let path = self.path_data(path_id); +2680 let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe(); +2681 let loss_delay = path +2682 .rtt +2683 .conservative() +2684 .mul_f32(self.config.time_threshold) +2685 .max(TIMER_GRANULARITY); +2686 let first_packet_after_rtt_sample = path.first_packet_after_rtt_sample; +2687 +2688 let largest_acked_packet = self.spaces[pn_space] +2689 .for_path(path_id) +2690 .largest_acked_packet +2691 .expect("detect_lost_packets only to be called if path received at least one ACK"); +2692 let packet_threshold = self.config.packet_threshold as u64; +2693 +2694 // InPersistentCongestion: Determine if all packets in the time period before the newest +2695 // lost packet, including the edges, are marked lost. PTO computation must always +2696 // include max ACK delay, i.e. operate as if in Data space (see RFC9001 §7.6.1). +2697 let congestion_period = self +2698 .pto(SpaceId::Data, path_id) +2699 .saturating_mul(self.config.persistent_congestion_threshold); +2700 let mut persistent_congestion_start: Option<Instant> = None; +2701 let mut prev_packet = None; +2702 let space = self.spaces[pn_space].for_path(path_id); +2703 +2704 for (packet, info) in space.sent_packets.iter_range(0..largest_acked_packet) { +2705 if prev_packet != Some(packet.wrapping_sub(1)) { +2706 // An intervening packet was acknowledged +2707 persistent_congestion_start = None; +2708 } +2709 +2710 // Packets sent before now - loss_delay are deemed lost. +2711 // However, we avoid subtraction as it can panic and there's no +2712 // saturating equivalent of this subtraction operation with a Duration. +2713 let packet_too_old = now.saturating_duration_since(info.time_sent) >= loss_delay; +2714 if packet_too_old || largest_acked_packet >= packet + packet_threshold { +2715 // The packet should be declared lost. +2716 if Some(packet) == in_flight_mtu_probe { +2717 // Lost MTU probes are not included in `lost_packets`, because they +2718 // should not trigger a congestion control response +2719 lost_mtu_probe = in_flight_mtu_probe; +2720 } else { +2721 lost_packets.push(packet); +2722 size_of_lost_packets += info.size as u64; +2723 if info.ack_eliciting && due_to_ack { +2724 match persistent_congestion_start { +2725 // Two ACK-eliciting packets lost more than +2726 // congestion_period apart, with no ACKed packets in between +2727 Some(start) if info.time_sent - start > congestion_period => { +2728 in_persistent_congestion = true; +2729 } +2730 // Persistent congestion must start after the first RTT sample +2731 None if first_packet_after_rtt_sample +2732 .is_some_and(|x| x < (pn_space, packet)) => +2733 { +2734 persistent_congestion_start = Some(info.time_sent); +2735 } +2736 _ => {} +2737 } +2738 } +2739 } +2740 } else { +2741 // The packet should not yet be declared lost. +2742 if space.loss_time.is_none() { +2743 // Since we iterate in order the lowest packet number's loss time will +2744 // always be the earliest. +2745 space.loss_time = Some(info.time_sent + loss_delay); +2746 } +2747 persistent_congestion_start = None; +2748 } +2749 +2750 prev_packet = Some(packet); +2751 } +2752 +2753 self.handle_lost_packets( +2754 pn_space, +2755 path_id, +2756 now, +2757 lost_packets, +2758 lost_mtu_probe, +2759 loss_delay, +2760 in_persistent_congestion, +2761 size_of_lost_packets, +2762 ); +2763 } +2764 +2765 /// Drops the path state, declaring any remaining in-flight packets as lost +2766 fn discard_path(&mut self, path_id: PathId, now: Instant) { +2767 trace!(%path_id, "dropping path state"); +2768 let path = self.path_data(path_id); +2769 let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe(); +2770 +2771 let mut size_of_lost_packets = 0u64; // add to path_stats.lost_bytes; +2772 let lost_pns: Vec<_> = self.spaces[SpaceId::Data] +2773 .for_path(path_id) +2774 .sent_packets +2775 .iter() +2776 .filter(|(pn, _info)| Some(*pn) != in_flight_mtu_probe) +2777 .map(|(pn, info)| { +2778 size_of_lost_packets += info.size as u64; +2779 pn +2780 }) +2781 .collect(); +2782 +2783 if !lost_pns.is_empty() { +2784 trace!( +2785 %path_id, +2786 count = lost_pns.len(), +2787 lost_bytes = size_of_lost_packets, +2788 "packets lost on path abandon" +2789 ); +2790 self.handle_lost_packets( +2791 SpaceId::Data, +2792 path_id, +2793 now, +2794 lost_pns, +2795 in_flight_mtu_probe, +2796 Duration::ZERO, +2797 false, +2798 size_of_lost_packets, +2799 ); +2800 } +2801 self.paths.remove(&path_id); +2802 self.spaces[SpaceId::Data].number_spaces.remove(&path_id); +2803 +2804 let path_stats = self.path_stats.remove(&path_id).unwrap_or_default(); +2805 self.events.push_back( +2806 PathEvent::Abandoned { +2807 id: path_id, +2808 path_stats, +2809 } +2810 .into(), +2811 ); +2812 } +2813 +2814 fn handle_lost_packets( +2815 &mut self, +2816 pn_space: SpaceId, +2817 path_id: PathId, +2818 now: Instant, +2819 lost_packets: Vec<u64>, +2820 lost_mtu_probe: Option<u64>, +2821 loss_delay: Duration, +2822 in_persistent_congestion: bool, +2823 size_of_lost_packets: u64, +2824 ) { +2825 debug_assert!( +2826 { +2827 let mut sorted = lost_packets.clone(); +2828 sorted.sort(); +2829 sorted == lost_packets +2830 }, +2831 "lost_packets must be sorted" +2832 ); +2833 +2834 self.drain_lost_packets(now, pn_space, path_id); +2835 +2836 // OnPacketsLost +2837 if let Some(largest_lost) = lost_packets.last().cloned() { +2838 let old_bytes_in_flight = self.path_data_mut(path_id).in_flight.bytes; +2839 let largest_lost_sent = self.spaces[pn_space] +2840 .for_path(path_id) +2841 .sent_packets +2842 .get(largest_lost) +2843 .unwrap() +2844 .time_sent; +2845 let path_stats = self.path_stats.entry(path_id).or_default(); +2846 path_stats.lost_packets += lost_packets.len() as u64; +2847 path_stats.lost_bytes += size_of_lost_packets; +2848 trace!( +2849 %path_id, +2850 count = lost_packets.len(), +2851 lost_bytes = size_of_lost_packets, +2852 "packets lost", +2853 ); +2854 +2855 for &packet in &lost_packets { +2856 let Some(info) = self.spaces[pn_space].for_path(path_id).take(packet) else { +2857 continue; +2858 }; +2859 self.qlog +2860 .emit_packet_lost(packet, &info, loss_delay, pn_space, now); +2861 self.paths +2862 .get_mut(&path_id) +2863 .unwrap() +2864 .remove_in_flight(&info); +2865 +2866 for frame in info.stream_frames { +2867 self.streams.retransmit(frame); +2868 } +2869 self.spaces[pn_space].pending |= info.retransmits; +2870 self.path_data_mut(path_id) +2871 .mtud +2872 .on_non_probe_lost(packet, info.size); +2873 +2874 self.spaces[pn_space].for_path(path_id).lost_packets.insert( +2875 packet, +2876 LostPacket { +2877 time_sent: info.time_sent, +2878 }, +2879 ); +2880 } +2881 +2882 let path = self.path_data_mut(path_id); +2883 if path.mtud.black_hole_detected(now) { +2884 path.congestion.on_mtu_update(path.mtud.current_mtu()); +2885 if let Some(max_datagram_size) = self.datagrams().max_size() { +2886 self.datagrams.drop_oversized(max_datagram_size); +2887 } +2888 self.path_stats +2889 .entry(path_id) +2890 .or_default() +2891 .black_holes_detected += 1; +2892 } +2893 +2894 // Don't apply congestion penalty for lost ack-only packets +2895 let lost_ack_eliciting = +2896 old_bytes_in_flight != self.path_data_mut(path_id).in_flight.bytes; +2897 +2898 if lost_ack_eliciting { +2899 self.path_stats +2900 .entry(path_id) +2901 .or_default() +2902 .congestion_events += 1; +2903 self.path_data_mut(path_id).congestion.on_congestion_event( +2904 now, +2905 largest_lost_sent, +2906 in_persistent_congestion, +2907 false, +2908 size_of_lost_packets, +2909 ); +2910 } +2911 } +2912 +2913 // Handle a lost MTU probe +2914 if let Some(packet) = lost_mtu_probe { +2915 let info = self.spaces[SpaceId::Data] +2916 .for_path(path_id) +2917 .take(packet) +2918 .unwrap(); // safe: lost_mtu_probe is omitted from lost_packets, and +2919 // therefore must not have been removed yet +2920 self.paths +2921 .get_mut(&path_id) +2922 .unwrap() +2923 .remove_in_flight(&info); +2924 self.path_data_mut(path_id).mtud.on_probe_lost(); +2925 self.path_stats +2926 .entry(path_id) +2927 .or_default() +2928 .lost_plpmtud_probes += 1; +2929 } +2930 } +2931 +2932 /// Returns the earliest time packets should be declared lost for all spaces on a path. +2933 /// +2934 /// If a path has an acknowledged packet with any prior un-acknowledged packets, the +2935 /// earliest un-acknowledged packet can be declared lost after a timeout has elapsed. +2936 /// The time returned is when this packet should be declared lost. +2937 fn loss_time_and_space(&self, path_id: PathId) -> Option<(Instant, SpaceId)> { +2938 SpaceId::iter() +2939 .filter_map(|id| { +2940 self.spaces[id] +2941 .number_spaces +2942 .get(&path_id) +2943 .and_then(|pns| pns.loss_time) +2944 .map(|time| (time, id)) +2945 }) +2946 .min_by_key(|&(time, _)| time) +2947 } +2948 +2949 /// Returns the earliest next PTO should fire for all spaces on a path. +2950 fn pto_time_and_space(&mut self, now: Instant, path_id: PathId) -> Option<(Instant, SpaceId)> { +2951 let path = self.path(path_id)?; +2952 let pto_count = path.pto_count; +2953 let backoff = 2u32.pow(pto_count.min(MAX_BACKOFF_EXPONENT)); +2954 let mut duration = path.rtt.pto_base() * backoff; +2955 +2956 if path_id == PathId::ZERO +2957 && path.in_flight.ack_eliciting == 0 +2958 && !self.peer_completed_address_validation(PathId::ZERO) +2959 { +2960 // Address Validation during Connection Establishment: +2961 // https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1. To prevent a +2962 // deadlock if an Initial or Handshake packet from the server is lost and the +2963 // server can not send more due to its anti-amplification limit the client must +2964 // send another packet on PTO. +2965 let space = match self.highest_space { +2966 SpaceId::Handshake => SpaceId::Handshake, +2967 _ => SpaceId::Initial, +2968 }; +2969 +2970 return Some((now + duration, space)); +2971 } +2972 +2973 let mut result = None; +2974 for space in SpaceId::iter() { +2975 let Some(pns) = self.spaces[space].number_spaces.get(&path_id) else { +2976 continue; +2977 }; +2978 +2979 if !pns.has_in_flight() { +2980 continue; +2981 } +2982 if space == SpaceId::Data { +2983 // Skip ApplicationData until handshake completes. +2984 if self.is_handshaking() { +2985 return result; +2986 } +2987 // Include max_ack_delay and backoff for ApplicationData. +2988 duration += self.ack_frequency.max_ack_delay_for_pto() * backoff; +2989 } +2990 let Some(last_ack_eliciting) = pns.time_of_last_ack_eliciting_packet else { +2991 continue; +2992 }; +2993 let pto = last_ack_eliciting + duration; +2994 if result.is_none_or(|(earliest_pto, _)| pto < earliest_pto) { +2995 if path.anti_amplification_blocked(1) { +2996 // Nothing would be able to be sent. +2997 continue; +2998 } +2999 if path.in_flight.ack_eliciting == 0 { +3000 // Nothing ack-eliciting, no PTO to arm/fire. +3001 continue; +3002 } +3003 result = Some((pto, space)); +3004 } +3005 } +3006 result +3007 } +3008 +3009 fn peer_completed_address_validation(&self, path: PathId) -> bool { +3010 // TODO(flub): This logic needs updating for multipath +3011 if self.side.is_server() || self.state.is_closed() { +3012 return true; +3013 } +3014 // The server is guaranteed to have validated our address if any of our handshake or 1-RTT +3015 // packets are acknowledged or we've seen HANDSHAKE_DONE and discarded handshake keys. +3016 self.spaces[SpaceId::Handshake] +3017 .path_space(PathId::ZERO) +3018 .and_then(|pns| pns.largest_acked_packet) +3019 .is_some() +3020 || self.spaces[SpaceId::Data] +3021 .path_space(path) +3022 .and_then(|pns| pns.largest_acked_packet) +3023 .is_some() +3024 || (self.spaces[SpaceId::Data].crypto.is_some() +3025 && self.spaces[SpaceId::Handshake].crypto.is_none()) +3026 } +3027 +3028 /// Resets the the [`PathTimer::LossDetection`] timer to the next instant it may be needed +3029 /// +3030 /// The timer must fire if either: +3031 /// - An ack-eliciting packet we sent needs to be declared lost. +3032 /// - A tail-loss probe needs to be sent. +3033 /// +3034 /// See [`Connection::on_loss_detection_timeout`] for details. +3035 fn set_loss_detection_timer(&mut self, now: Instant, path_id: PathId) { +3036 if self.state.is_closed() { +3037 // No loss detection takes place on closed connections, and `close_common` already +3038 // stopped time timer. Ensure we don't restart it inadvertently, e.g. in response to a +3039 // reordered packet being handled by state-insensitive code. +3040 return; +3041 } +3042 +3043 if let Some((loss_time, _)) = self.loss_time_and_space(path_id) { +3044 // Time threshold loss detection. +3045 self.timers.set( +3046 Timer::PerPath(path_id, PathTimer::LossDetection), +3047 loss_time, +3048 self.qlog.with_time(now), +3049 ); +3050 return; +3051 } +3052 +3053 // Determine which PN space to arm PTO for. +3054 // Calculate PTO duration +3055 if let Some((timeout, _)) = self.pto_time_and_space(now, path_id) { +3056 self.timers.set( +3057 Timer::PerPath(path_id, PathTimer::LossDetection), +3058 timeout, +3059 self.qlog.with_time(now), +3060 ); +3061 } else { +3062 self.timers.stop( +3063 Timer::PerPath(path_id, PathTimer::LossDetection), 3064 self.qlog.with_time(now), 3065 ); -3066 } else { -3067 self.timers.stop( -3068 Timer::PerPath(path_id, PathTimer::LossDetection), -3069 self.qlog.with_time(now), -3070 ); -3071 } -3072 } -3073 -3074 /// The maximum probe timeout across all paths -3075 /// -3076 /// If `is_closing` is set to `true` it will filter out paths that have not yet been used. -3077 /// -3078 /// See [`Connection::pto`] -3079 fn pto_max_path(&self, space: SpaceId, is_closing: bool) -> Duration { -3080 match space { -3081 SpaceId::Initial | SpaceId::Handshake => self.pto(space, PathId::ZERO), -3082 SpaceId::Data => self -3083 .paths -3084 .iter() -3085 .filter_map(|(path_id, state)| { -3086 if is_closing && state.data.total_sent == 0 && state.data.total_recvd == 0 { -3087 // If we are closing and haven't sent anything yet, do not include -3088 None -3089 } else { -3090 let pto = self.pto(space, *path_id); -3091 Some(pto) -3092 } -3093 }) -3094 .max() -3095 .expect("there should be one at least path"), -3096 } -3097 } -3098 -3099 /// Probe Timeout -3100 /// -3101 /// The PTO is logically the time in which you'd expect to receive an acknowledgement -3102 /// for a packet. So approximately RTT + max_ack_delay. -3103 fn pto(&self, space: SpaceId, path_id: PathId) -> Duration { -3104 let max_ack_delay = match space { -3105 SpaceId::Initial | SpaceId::Handshake => Duration::ZERO, -3106 SpaceId::Data => self.ack_frequency.max_ack_delay_for_pto(), -3107 }; -3108 self.path_data(path_id).rtt.pto_base() + max_ack_delay -3109 } -3110 -3111 fn on_packet_authenticated( -3112 &mut self, -3113 now: Instant, -3114 space_id: SpaceId, -3115 path_id: PathId, -3116 ecn: Option<EcnCodepoint>, -3117 packet: Option<u64>, -3118 spin: bool, -3119 is_1rtt: bool, -3120 ) { -3121 self.total_authed_packets += 1; -3122 if let Some(last_allowed_receive) = self -3123 .paths -3124 .get(&path_id) -3125 .and_then(|path| path.data.last_allowed_receive) -3126 { -3127 if now > last_allowed_receive { -3128 warn!("received data on path which we abandoned more than 3 * PTO ago"); -3129 // The peer failed to respond with a PATH_ABANDON in time. -3130 if !self.state.is_closed() { -3131 // TODO(flub): What should the error code be? -3132 self.state.move_to_closed(TransportError::NO_ERROR( -3133 "peer failed to respond with PATH_ABANDON in time", -3134 )); -3135 self.close_common(); -3136 self.set_close_timer(now); -3137 self.close = true; -3138 } -3139 return; -3140 } -3141 } -3142 -3143 self.reset_keep_alive(path_id, now); -3144 self.reset_idle_timeout(now, space_id, path_id); -3145 self.permit_idle_reset = true; -3146 self.receiving_ecn |= ecn.is_some(); -3147 if let Some(x) = ecn { -3148 let space = &mut self.spaces[space_id]; -3149 space.for_path(path_id).ecn_counters += x; -3150 -3151 if x.is_ce() { -3152 space -3153 .for_path(path_id) -3154 .pending_acks -3155 .set_immediate_ack_required(); -3156 } -3157 } -3158 -3159 let packet = match packet { -3160 Some(x) => x, -3161 None => return, -3162 }; -3163 match &self.side { -3164 ConnectionSide::Client { .. } => { -3165 // If we received a handshake packet that authenticated, then we're talking to -3166 // the real server. From now on we should no longer allow the server to migrate -3167 // its address. -3168 if space_id == SpaceId::Handshake { -3169 if let Some(hs) = self.state.as_handshake_mut() { -3170 hs.allow_server_migration = false; -3171 } -3172 } -3173 } -3174 ConnectionSide::Server { .. } => { -3175 if self.spaces[SpaceId::Initial].crypto.is_some() && space_id == SpaceId::Handshake -3176 { -3177 // A server stops sending and processing Initial packets when it receives its first Handshake packet. -3178 self.discard_space(now, SpaceId::Initial); -3179 } -3180 if self.zero_rtt_crypto.is_some() && is_1rtt { -3181 // Discard 0-RTT keys soon after receiving a 1-RTT packet -3182 self.set_key_discard_timer(now, space_id) -3183 } -3184 } -3185 } -3186 let space = self.spaces[space_id].for_path(path_id); -3187 space.pending_acks.insert_one(packet, now); -3188 if packet >= space.rx_packet.unwrap_or_default() { -3189 space.rx_packet = Some(packet); -3190 // Update outgoing spin bit, inverting iff we're the client -3191 self.spin = self.side.is_client() ^ spin; -3192 } -3193 } -3194 -3195 /// Resets the idle timeout timers -3196 /// -3197 /// Without multipath there is only the connection-wide idle timeout. When multipath is -3198 /// enabled there is an additional per-path idle timeout. -3199 fn reset_idle_timeout(&mut self, now: Instant, space: SpaceId, path_id: PathId) { -3200 // First reset the global idle timeout. -3201 if let Some(timeout) = self.idle_timeout { -3202 if self.state.is_closed() { -3203 self.timers -3204 .stop(Timer::Conn(ConnTimer::Idle), self.qlog.with_time(now)); -3205 } else { -3206 let dt = cmp::max(timeout, 3 * self.pto_max_path(space, false)); -3207 self.timers.set( -3208 Timer::Conn(ConnTimer::Idle), -3209 now + dt, -3210 self.qlog.with_time(now), -3211 ); -3212 } -3213 } -3214 -3215 // Now handle the per-path state -3216 if let Some(timeout) = self.path_data(path_id).idle_timeout { -3217 if self.state.is_closed() { -3218 self.timers.stop( -3219 Timer::PerPath(path_id, PathTimer::PathIdle), -3220 self.qlog.with_time(now), -3221 ); -3222 } else { -3223 let dt = cmp::max(timeout, 3 * self.pto(space, path_id)); -3224 self.timers.set( -3225 Timer::PerPath(path_id, PathTimer::PathIdle), -3226 now + dt, -3227 self.qlog.with_time(now), -3228 ); -3229 } -3230 } -3231 } -3232 -3233 /// Resets both the [`ConnTimer::KeepAlive`] and [`PathTimer::PathKeepAlive`] timers -3234 fn reset_keep_alive(&mut self, path_id: PathId, now: Instant) { -3235 if !self.state.is_established() { -3236 return; -3237 } -3238 -3239 if let Some(interval) = self.config.keep_alive_interval { -3240 self.timers.set( -3241 Timer::Conn(ConnTimer::KeepAlive), -3242 now + interval, -3243 self.qlog.with_time(now), -3244 ); -3245 } -3246 -3247 if let Some(interval) = self.path_data(path_id).keep_alive { -3248 self.timers.set( -3249 Timer::PerPath(path_id, PathTimer::PathKeepAlive), -3250 now + interval, -3251 self.qlog.with_time(now), -3252 ); -3253 } -3254 } -3255 -3256 /// Sets the timer for when a previously issued CID should be retired next -3257 fn reset_cid_retirement(&mut self, now: Instant) { -3258 if let Some((_path, t)) = self.next_cid_retirement() { -3259 self.timers.set( -3260 Timer::Conn(ConnTimer::PushNewCid), -3261 t, -3262 self.qlog.with_time(now), -3263 ); -3264 } -3265 } -3266 -3267 /// The next time when a previously issued CID should be retired -3268 fn next_cid_retirement(&self) -> Option<(PathId, Instant)> { -3269 self.local_cid_state -3270 .iter() -3271 .filter_map(|(path_id, cid_state)| cid_state.next_timeout().map(|t| (*path_id, t))) -3272 .min_by_key(|(_path_id, timeout)| *timeout) -3273 } -3274 -3275 /// Handle the already-decrypted first packet from the client -3276 /// -3277 /// Decrypting the first packet in the `Endpoint` allows stateless packet handling to be more -3278 /// efficient. -3279 pub(crate) fn handle_first_packet( -3280 &mut self, -3281 now: Instant, -3282 network_path: FourTuple, -3283 ecn: Option<EcnCodepoint>, -3284 packet_number: u64, -3285 packet: InitialPacket, -3286 remaining: Option<BytesMut>, -3287 ) -> Result<(), ConnectionError> { -3288 let span = trace_span!("first recv"); -3289 let _guard = span.enter(); -3290 debug_assert!(self.side.is_server()); -3291 let len = packet.header_data.len() + packet.payload.len(); -3292 let path_id = PathId::ZERO; -3293 self.path_data_mut(path_id).total_recvd = len as u64; -3294 -3295 if let Some(hs) = self.state.as_handshake_mut() { -3296 hs.expected_token = packet.header.token.clone(); -3297 } else { -3298 unreachable!("first packet must be delivered in Handshake state"); -3299 } -3300 -3301 // The first packet is always on PathId::ZERO -3302 self.on_packet_authenticated( -3303 now, -3304 SpaceId::Initial, -3305 path_id, -3306 ecn, -3307 Some(packet_number), -3308 false, -3309 false, -3310 ); +3066 } +3067 } +3068 +3069 /// The maximum probe timeout across all paths +3070 /// +3071 /// If `is_closing` is set to `true` it will filter out paths that have not yet been used. +3072 /// +3073 /// See [`Connection::pto`] +3074 fn pto_max_path(&self, space: SpaceId, is_closing: bool) -> Duration { +3075 match space { +3076 SpaceId::Initial | SpaceId::Handshake => self.pto(space, PathId::ZERO), +3077 SpaceId::Data => self +3078 .paths +3079 .iter() +3080 .filter_map(|(path_id, state)| { +3081 if is_closing && state.data.total_sent == 0 && state.data.total_recvd == 0 { +3082 // If we are closing and haven't sent anything yet, do not include +3083 None +3084 } else { +3085 let pto = self.pto(space, *path_id); +3086 Some(pto) +3087 } +3088 }) +3089 .max() +3090 .expect("there should be one at least path"), +3091 } +3092 } +3093 +3094 /// Probe Timeout +3095 /// +3096 /// The PTO is logically the time in which you'd expect to receive an acknowledgement +3097 /// for a packet. So approximately RTT + max_ack_delay. +3098 fn pto(&self, space: SpaceId, path_id: PathId) -> Duration { +3099 let max_ack_delay = match space { +3100 SpaceId::Initial | SpaceId::Handshake => Duration::ZERO, +3101 SpaceId::Data => self.ack_frequency.max_ack_delay_for_pto(), +3102 }; +3103 self.path_data(path_id).rtt.pto_base() + max_ack_delay +3104 } +3105 +3106 fn on_packet_authenticated( +3107 &mut self, +3108 now: Instant, +3109 space_id: SpaceId, +3110 path_id: PathId, +3111 ecn: Option<EcnCodepoint>, +3112 packet: Option<u64>, +3113 spin: bool, +3114 is_1rtt: bool, +3115 ) { +3116 self.total_authed_packets += 1; +3117 if let Some(last_allowed_receive) = self +3118 .paths +3119 .get(&path_id) +3120 .and_then(|path| path.data.last_allowed_receive) +3121 { +3122 if now > last_allowed_receive { +3123 warn!("received data on path which we abandoned more than 3 * PTO ago"); +3124 // The peer failed to respond with a PATH_ABANDON in time. +3125 if !self.state.is_closed() { +3126 // TODO(flub): What should the error code be? +3127 self.state.move_to_closed(TransportError::NO_ERROR( +3128 "peer failed to respond with PATH_ABANDON in time", +3129 )); +3130 self.close_common(); +3131 self.set_close_timer(now); +3132 self.close = true; +3133 } +3134 return; +3135 } +3136 } +3137 +3138 self.reset_keep_alive(path_id, now); +3139 self.reset_idle_timeout(now, space_id, path_id); +3140 self.permit_idle_reset = true; +3141 self.receiving_ecn |= ecn.is_some(); +3142 if let Some(x) = ecn { +3143 let space = &mut self.spaces[space_id]; +3144 space.for_path(path_id).ecn_counters += x; +3145 +3146 if x.is_ce() { +3147 space +3148 .for_path(path_id) +3149 .pending_acks +3150 .set_immediate_ack_required(); +3151 } +3152 } +3153 +3154 let packet = match packet { +3155 Some(x) => x, +3156 None => return, +3157 }; +3158 match &self.side { +3159 ConnectionSide::Client { .. } => { +3160 // If we received a handshake packet that authenticated, then we're talking to +3161 // the real server. From now on we should no longer allow the server to migrate +3162 // its address. +3163 if space_id == SpaceId::Handshake { +3164 if let Some(hs) = self.state.as_handshake_mut() { +3165 hs.allow_server_migration = false; +3166 } +3167 } +3168 } +3169 ConnectionSide::Server { .. } => { +3170 if self.spaces[SpaceId::Initial].crypto.is_some() && space_id == SpaceId::Handshake +3171 { +3172 // A server stops sending and processing Initial packets when it receives its first Handshake packet. +3173 self.discard_space(now, SpaceId::Initial); +3174 } +3175 if self.zero_rtt_crypto.is_some() && is_1rtt { +3176 // Discard 0-RTT keys soon after receiving a 1-RTT packet +3177 self.set_key_discard_timer(now, space_id) +3178 } +3179 } +3180 } +3181 let space = self.spaces[space_id].for_path(path_id); +3182 space.pending_acks.insert_one(packet, now); +3183 if packet >= space.rx_packet.unwrap_or_default() { +3184 space.rx_packet = Some(packet); +3185 // Update outgoing spin bit, inverting iff we're the client +3186 self.spin = self.side.is_client() ^ spin; +3187 } +3188 } +3189 +3190 /// Resets the idle timeout timers +3191 /// +3192 /// Without multipath there is only the connection-wide idle timeout. When multipath is +3193 /// enabled there is an additional per-path idle timeout. +3194 fn reset_idle_timeout(&mut self, now: Instant, space: SpaceId, path_id: PathId) { +3195 // First reset the global idle timeout. +3196 if let Some(timeout) = self.idle_timeout { +3197 if self.state.is_closed() { +3198 self.timers +3199 .stop(Timer::Conn(ConnTimer::Idle), self.qlog.with_time(now)); +3200 } else { +3201 let dt = cmp::max(timeout, 3 * self.pto_max_path(space, false)); +3202 self.timers.set( +3203 Timer::Conn(ConnTimer::Idle), +3204 now + dt, +3205 self.qlog.with_time(now), +3206 ); +3207 } +3208 } +3209 +3210 // Now handle the per-path state +3211 if let Some(timeout) = self.path_data(path_id).idle_timeout { +3212 if self.state.is_closed() { +3213 self.timers.stop( +3214 Timer::PerPath(path_id, PathTimer::PathIdle), +3215 self.qlog.with_time(now), +3216 ); +3217 } else { +3218 let dt = cmp::max(timeout, 3 * self.pto(space, path_id)); +3219 self.timers.set( +3220 Timer::PerPath(path_id, PathTimer::PathIdle), +3221 now + dt, +3222 self.qlog.with_time(now), +3223 ); +3224 } +3225 } +3226 } +3227 +3228 /// Resets both the [`ConnTimer::KeepAlive`] and [`PathTimer::PathKeepAlive`] timers +3229 fn reset_keep_alive(&mut self, path_id: PathId, now: Instant) { +3230 if !self.state.is_established() { +3231 return; +3232 } +3233 +3234 if let Some(interval) = self.config.keep_alive_interval { +3235 self.timers.set( +3236 Timer::Conn(ConnTimer::KeepAlive), +3237 now + interval, +3238 self.qlog.with_time(now), +3239 ); +3240 } +3241 +3242 if let Some(interval) = self.path_data(path_id).keep_alive { +3243 self.timers.set( +3244 Timer::PerPath(path_id, PathTimer::PathKeepAlive), +3245 now + interval, +3246 self.qlog.with_time(now), +3247 ); +3248 } +3249 } +3250 +3251 /// Sets the timer for when a previously issued CID should be retired next +3252 fn reset_cid_retirement(&mut self, now: Instant) { +3253 if let Some((_path, t)) = self.next_cid_retirement() { +3254 self.timers.set( +3255 Timer::Conn(ConnTimer::PushNewCid), +3256 t, +3257 self.qlog.with_time(now), +3258 ); +3259 } +3260 } +3261 +3262 /// The next time when a previously issued CID should be retired +3263 fn next_cid_retirement(&self) -> Option<(PathId, Instant)> { +3264 self.local_cid_state +3265 .iter() +3266 .filter_map(|(path_id, cid_state)| cid_state.next_timeout().map(|t| (*path_id, t))) +3267 .min_by_key(|(_path_id, timeout)| *timeout) +3268 } +3269 +3270 /// Handle the already-decrypted first packet from the client +3271 /// +3272 /// Decrypting the first packet in the `Endpoint` allows stateless packet handling to be more +3273 /// efficient. +3274 pub(crate) fn handle_first_packet( +3275 &mut self, +3276 now: Instant, +3277 network_path: FourTuple, +3278 ecn: Option<EcnCodepoint>, +3279 packet_number: u64, +3280 packet: InitialPacket, +3281 remaining: Option<BytesMut>, +3282 ) -> Result<(), ConnectionError> { +3283 let span = trace_span!("first recv"); +3284 let _guard = span.enter(); +3285 debug_assert!(self.side.is_server()); +3286 let len = packet.header_data.len() + packet.payload.len(); +3287 let path_id = PathId::ZERO; +3288 self.path_data_mut(path_id).total_recvd = len as u64; +3289 +3290 if let Some(hs) = self.state.as_handshake_mut() { +3291 hs.expected_token = packet.header.token.clone(); +3292 } else { +3293 unreachable!("first packet must be delivered in Handshake state"); +3294 } +3295 +3296 // The first packet is always on PathId::ZERO +3297 self.on_packet_authenticated( +3298 now, +3299 SpaceId::Initial, +3300 path_id, +3301 ecn, +3302 Some(packet_number), +3303 false, +3304 false, +3305 ); +3306 +3307 let packet: Packet = packet.into(); +3308 +3309 let mut qlog = QlogRecvPacket::new(len); +3310 qlog.header(&packet.header, Some(packet_number), path_id); 3311 -3312 let packet: Packet = packet.into(); -3313 -3314 let mut qlog = QlogRecvPacket::new(len); -3315 qlog.header(&packet.header, Some(packet_number), path_id); -3316 -3317 self.process_decrypted_packet( -3318 now, -3319 network_path, -3320 path_id, -3321 Some(packet_number), -3322 packet, -3323 &mut qlog, -3324 )?; -3325 self.qlog.emit_packet_received(qlog, now); -3326 if let Some(data) = remaining { -3327 self.handle_coalesced(now, network_path, path_id, ecn, data); -3328 } -3329 -3330 self.qlog.emit_recovery_metrics( -3331 path_id, -3332 &mut self.paths.get_mut(&path_id).unwrap().data, -3333 now, -3334 ); -3335 -3336 Ok(()) -3337 } -3338 -3339 fn init_0rtt(&mut self, now: Instant) { -3340 let (header, packet) = match self.crypto.early_crypto() { -3341 Some(x) => x, -3342 None => return, -3343 }; -3344 if self.side.is_client() { -3345 match self.crypto.transport_parameters() { -3346 Ok(params) => { -3347 let params = params -3348 .expect("crypto layer didn't supply transport parameters with ticket"); -3349 // Certain values must not be cached -3350 let params = TransportParameters { -3351 initial_src_cid: None, -3352 original_dst_cid: None, -3353 preferred_address: None, -3354 retry_src_cid: None, -3355 stateless_reset_token: None, -3356 min_ack_delay: None, -3357 ack_delay_exponent: TransportParameters::default().ack_delay_exponent, -3358 max_ack_delay: TransportParameters::default().max_ack_delay, -3359 initial_max_path_id: None, -3360 ..params -3361 }; -3362 self.set_peer_params(params); -3363 self.qlog.emit_peer_transport_params_restored(self, now); -3364 } -3365 Err(e) => { -3366 error!("session ticket has malformed transport parameters: {}", e); -3367 return; -3368 } -3369 } -3370 } -3371 trace!("0-RTT enabled"); -3372 self.zero_rtt_enabled = true; -3373 self.zero_rtt_crypto = Some(ZeroRttCrypto { header, packet }); -3374 } -3375 -3376 fn read_crypto( -3377 &mut self, -3378 space: SpaceId, -3379 crypto: &frame::Crypto, -3380 payload_len: usize, -3381 ) -> Result<(), TransportError> { -3382 let expected = if !self.state.is_handshake() { -3383 SpaceId::Data -3384 } else if self.highest_space == SpaceId::Initial { -3385 SpaceId::Initial -3386 } else { -3387 // On the server, self.highest_space can be Data after receiving the client's first -3388 // flight, but we expect Handshake CRYPTO until the handshake is complete. -3389 SpaceId::Handshake -3390 }; -3391 // We can't decrypt Handshake packets when highest_space is Initial, CRYPTO frames in 0-RTT -3392 // packets are illegal, and we don't process 1-RTT packets until the handshake is -3393 // complete. Therefore, we will never see CRYPTO data from a later-than-expected space. -3394 debug_assert!(space <= expected, "received out-of-order CRYPTO data"); -3395 -3396 let end = crypto.offset + crypto.data.len() as u64; -3397 if space < expected && end > self.spaces[space].crypto_stream.bytes_read() { -3398 warn!( -3399 "received new {:?} CRYPTO data when expecting {:?}", -3400 space, expected -3401 ); -3402 return Err(TransportError::PROTOCOL_VIOLATION( -3403 "new data at unexpected encryption level", -3404 )); -3405 } -3406 -3407 let space = &mut self.spaces[space]; -3408 let max = end.saturating_sub(space.crypto_stream.bytes_read()); -3409 if max > self.config.crypto_buffer_size as u64 { -3410 return Err(TransportError::CRYPTO_BUFFER_EXCEEDED("")); -3411 } -3412 -3413 space -3414 .crypto_stream -3415 .insert(crypto.offset, crypto.data.clone(), payload_len); -3416 while let Some(chunk) = space.crypto_stream.read(usize::MAX, true) { -3417 trace!("consumed {} CRYPTO bytes", chunk.bytes.len()); -3418 if self.crypto.read_handshake(&chunk.bytes)? { -3419 self.events.push_back(Event::HandshakeDataReady); -3420 } -3421 } -3422 -3423 Ok(()) -3424 } -3425 -3426 fn write_crypto(&mut self) { -3427 loop { -3428 let space = self.highest_space; -3429 let mut outgoing = Vec::new(); -3430 if let Some(crypto) = self.crypto.write_handshake(&mut outgoing) { -3431 match space { -3432 SpaceId::Initial => { -3433 self.upgrade_crypto(SpaceId::Handshake, crypto); -3434 } -3435 SpaceId::Handshake => { -3436 self.upgrade_crypto(SpaceId::Data, crypto); -3437 } -3438 _ => unreachable!("got updated secrets during 1-RTT"), -3439 } -3440 } -3441 if outgoing.is_empty() { -3442 if space == self.highest_space { -3443 break; -3444 } else { -3445 // Keys updated, check for more data to send -3446 continue; -3447 } -3448 } -3449 let offset = self.spaces[space].crypto_offset; -3450 let outgoing = Bytes::from(outgoing); -3451 if let Some(hs) = self.state.as_handshake_mut() { -3452 if space == SpaceId::Initial && offset == 0 && self.side.is_client() { -3453 hs.client_hello = Some(outgoing.clone()); -3454 } -3455 } -3456 self.spaces[space].crypto_offset += outgoing.len() as u64; -3457 trace!("wrote {} {:?} CRYPTO bytes", outgoing.len(), space); -3458 self.spaces[space].pending.crypto.push_back(frame::Crypto { -3459 offset, -3460 data: outgoing, -3461 }); -3462 } -3463 } -3464 -3465 /// Switch to stronger cryptography during handshake -3466 fn upgrade_crypto(&mut self, space: SpaceId, crypto: Keys) { -3467 debug_assert!( -3468 self.spaces[space].crypto.is_none(), -3469 "already reached packet space {space:?}" -3470 ); -3471 trace!("{:?} keys ready", space); -3472 if space == SpaceId::Data { -3473 // Precompute the first key update -3474 self.next_crypto = Some( -3475 self.crypto -3476 .next_1rtt_keys() -3477 .expect("handshake should be complete"), -3478 ); -3479 } -3480 -3481 self.spaces[space].crypto = Some(crypto); -3482 debug_assert!(space as usize > self.highest_space as usize); -3483 self.highest_space = space; -3484 if space == SpaceId::Data && self.side.is_client() { -3485 // Discard 0-RTT keys because 1-RTT keys are available. -3486 self.zero_rtt_crypto = None; -3487 } -3488 } -3489 -3490 fn discard_space(&mut self, now: Instant, space_id: SpaceId) { -3491 debug_assert!(space_id != SpaceId::Data); -3492 trace!("discarding {:?} keys", space_id); -3493 if space_id == SpaceId::Initial { -3494 // No longer needed -3495 if let ConnectionSide::Client { token, .. } = &mut self.side { -3496 *token = Bytes::new(); -3497 } -3498 } -3499 let space = &mut self.spaces[space_id]; -3500 space.crypto = None; -3501 let pns = space.for_path(PathId::ZERO); -3502 pns.time_of_last_ack_eliciting_packet = None; -3503 pns.loss_time = None; -3504 pns.loss_probes = 0; -3505 let sent_packets = mem::take(&mut pns.sent_packets); -3506 let path = self.paths.get_mut(&PathId::ZERO).unwrap(); -3507 for (_, packet) in sent_packets.into_iter() { -3508 path.data.remove_in_flight(&packet); -3509 } -3510 -3511 self.set_loss_detection_timer(now, PathId::ZERO) -3512 } -3513 -3514 fn handle_coalesced( -3515 &mut self, -3516 now: Instant, -3517 network_path: FourTuple, -3518 path_id: PathId, -3519 ecn: Option<EcnCodepoint>, -3520 data: BytesMut, -3521 ) { -3522 self.path_data_mut(path_id) -3523 .inc_total_recvd(data.len() as u64); -3524 let mut remaining = Some(data); -3525 let cid_len = self -3526 .local_cid_state -3527 .values() -3528 .map(|cid_state| cid_state.cid_len()) -3529 .next() -3530 .expect("one cid_state must exist"); -3531 while let Some(data) = remaining { -3532 match PartialDecode::new( -3533 data, -3534 &FixedLengthConnectionIdParser::new(cid_len), -3535 &[self.version], -3536 self.endpoint_config.grease_quic_bit, -3537 ) { -3538 Ok((partial_decode, rest)) => { -3539 remaining = rest; -3540 self.handle_decode(now, network_path, path_id, ecn, partial_decode); -3541 } -3542 Err(e) => { -3543 trace!("malformed header: {}", e); -3544 return; -3545 } -3546 } -3547 } -3548 } -3549 -3550 fn handle_decode( -3551 &mut self, -3552 now: Instant, -3553 network_path: FourTuple, -3554 path_id: PathId, -3555 ecn: Option<EcnCodepoint>, -3556 partial_decode: PartialDecode, -3557 ) { -3558 let qlog = QlogRecvPacket::new(partial_decode.len()); -3559 if let Some(decoded) = packet_crypto::unprotect_header( -3560 partial_decode, -3561 &self.spaces, -3562 self.zero_rtt_crypto.as_ref(), -3563 self.peer_params.stateless_reset_token, -3564 ) { -3565 self.handle_packet( -3566 now, -3567 network_path, -3568 path_id, -3569 ecn, -3570 decoded.packet, -3571 decoded.stateless_reset, -3572 qlog, -3573 ); -3574 } -3575 } -3576 -3577 fn handle_packet( -3578 &mut self, -3579 now: Instant, -3580 network_path: FourTuple, -3581 path_id: PathId, -3582 ecn: Option<EcnCodepoint>, -3583 packet: Option<Packet>, -3584 stateless_reset: bool, -3585 mut qlog: QlogRecvPacket, -3586 ) { -3587 self.stats.udp_rx.ios += 1; -3588 if let Some(ref packet) = packet { -3589 trace!( -3590 "got {:?} packet ({} bytes) from {} using id {}", -3591 packet.header.space(), -3592 packet.payload.len() + packet.header_data.len(), -3593 network_path, -3594 packet.header.dst_cid(), -3595 ); -3596 } -3597 -3598 if self.is_handshaking() { -3599 if path_id != PathId::ZERO { -3600 debug!(%network_path, %path_id, "discarding multipath packet during handshake"); -3601 return; -3602 } -3603 if network_path != self.path_data_mut(path_id).network_path { -3604 if let Some(hs) = self.state.as_handshake() { -3605 if hs.allow_server_migration { -3606 trace!(%network_path, prev = %self.path_data(path_id).network_path, "server migrated to new remote"); -3607 self.path_data_mut(path_id).network_path = network_path; -3608 self.qlog.emit_tuple_assigned(path_id, network_path, now); -3609 } else { -3610 debug!("discarding packet with unexpected remote during handshake"); -3611 return; -3612 } -3613 } else { -3614 debug!("discarding packet with unexpected remote during handshake"); -3615 return; -3616 } -3617 } -3618 } -3619 -3620 let was_closed = self.state.is_closed(); -3621 let was_drained = self.state.is_drained(); -3622 -3623 let decrypted = match packet { -3624 None => Err(None), -3625 Some(mut packet) => self -3626 .decrypt_packet(now, path_id, &mut packet) -3627 .map(move |number| (packet, number)), -3628 }; -3629 let result = match decrypted { -3630 _ if stateless_reset => { -3631 debug!("got stateless reset"); -3632 Err(ConnectionError::Reset) -3633 } -3634 Err(Some(e)) => { -3635 warn!("illegal packet: {}", e); -3636 Err(e.into()) -3637 } -3638 Err(None) => { -3639 debug!("failed to authenticate packet"); -3640 self.authentication_failures += 1; -3641 let integrity_limit = self.spaces[self.highest_space] -3642 .crypto -3643 .as_ref() -3644 .unwrap() -3645 .packet -3646 .local -3647 .integrity_limit(); -3648 if self.authentication_failures > integrity_limit { -3649 Err(TransportError::AEAD_LIMIT_REACHED("integrity limit violated").into()) -3650 } else { -3651 return; -3652 } -3653 } -3654 Ok((packet, number)) => { -3655 qlog.header(&packet.header, number, path_id); -3656 let span = match number { -3657 Some(pn) => trace_span!("recv", space = ?packet.header.space(), pn), -3658 None => trace_span!("recv", space = ?packet.header.space()), -3659 }; -3660 let _guard = span.enter(); -3661 -3662 let dedup = self.spaces[packet.header.space()] -3663 .path_space_mut(path_id) -3664 .map(|pns| &mut pns.dedup); -3665 if number.zip(dedup).is_some_and(|(n, d)| d.insert(n)) { -3666 debug!("discarding possible duplicate packet"); +3312 self.process_decrypted_packet( +3313 now, +3314 network_path, +3315 path_id, +3316 Some(packet_number), +3317 packet, +3318 &mut qlog, +3319 )?; +3320 self.qlog.emit_packet_received(qlog, now); +3321 if let Some(data) = remaining { +3322 self.handle_coalesced(now, network_path, path_id, ecn, data); +3323 } +3324 +3325 self.qlog.emit_recovery_metrics( +3326 path_id, +3327 &mut self.paths.get_mut(&path_id).unwrap().data, +3328 now, +3329 ); +3330 +3331 Ok(()) +3332 } +3333 +3334 fn init_0rtt(&mut self, now: Instant) { +3335 let (header, packet) = match self.crypto.early_crypto() { +3336 Some(x) => x, +3337 None => return, +3338 }; +3339 if self.side.is_client() { +3340 match self.crypto.transport_parameters() { +3341 Ok(params) => { +3342 let params = params +3343 .expect("crypto layer didn't supply transport parameters with ticket"); +3344 // Certain values must not be cached +3345 let params = TransportParameters { +3346 initial_src_cid: None, +3347 original_dst_cid: None, +3348 preferred_address: None, +3349 retry_src_cid: None, +3350 stateless_reset_token: None, +3351 min_ack_delay: None, +3352 ack_delay_exponent: TransportParameters::default().ack_delay_exponent, +3353 max_ack_delay: TransportParameters::default().max_ack_delay, +3354 initial_max_path_id: None, +3355 ..params +3356 }; +3357 self.set_peer_params(params); +3358 self.qlog.emit_peer_transport_params_restored(self, now); +3359 } +3360 Err(e) => { +3361 error!("session ticket has malformed transport parameters: {}", e); +3362 return; +3363 } +3364 } +3365 } +3366 trace!("0-RTT enabled"); +3367 self.zero_rtt_enabled = true; +3368 self.zero_rtt_crypto = Some(ZeroRttCrypto { header, packet }); +3369 } +3370 +3371 fn read_crypto( +3372 &mut self, +3373 space: SpaceId, +3374 crypto: &frame::Crypto, +3375 payload_len: usize, +3376 ) -> Result<(), TransportError> { +3377 let expected = if !self.state.is_handshake() { +3378 SpaceId::Data +3379 } else if self.highest_space == SpaceId::Initial { +3380 SpaceId::Initial +3381 } else { +3382 // On the server, self.highest_space can be Data after receiving the client's first +3383 // flight, but we expect Handshake CRYPTO until the handshake is complete. +3384 SpaceId::Handshake +3385 }; +3386 // We can't decrypt Handshake packets when highest_space is Initial, CRYPTO frames in 0-RTT +3387 // packets are illegal, and we don't process 1-RTT packets until the handshake is +3388 // complete. Therefore, we will never see CRYPTO data from a later-than-expected space. +3389 debug_assert!(space <= expected, "received out-of-order CRYPTO data"); +3390 +3391 let end = crypto.offset + crypto.data.len() as u64; +3392 if space < expected && end > self.spaces[space].crypto_stream.bytes_read() { +3393 warn!( +3394 "received new {:?} CRYPTO data when expecting {:?}", +3395 space, expected +3396 ); +3397 return Err(TransportError::PROTOCOL_VIOLATION( +3398 "new data at unexpected encryption level", +3399 )); +3400 } +3401 +3402 let space = &mut self.spaces[space]; +3403 let max = end.saturating_sub(space.crypto_stream.bytes_read()); +3404 if max > self.config.crypto_buffer_size as u64 { +3405 return Err(TransportError::CRYPTO_BUFFER_EXCEEDED("")); +3406 } +3407 +3408 space +3409 .crypto_stream +3410 .insert(crypto.offset, crypto.data.clone(), payload_len); +3411 while let Some(chunk) = space.crypto_stream.read(usize::MAX, true) { +3412 trace!("consumed {} CRYPTO bytes", chunk.bytes.len()); +3413 if self.crypto.read_handshake(&chunk.bytes)? { +3414 self.events.push_back(Event::HandshakeDataReady); +3415 } +3416 } +3417 +3418 Ok(()) +3419 } +3420 +3421 fn write_crypto(&mut self) { +3422 loop { +3423 let space = self.highest_space; +3424 let mut outgoing = Vec::new(); +3425 if let Some(crypto) = self.crypto.write_handshake(&mut outgoing) { +3426 match space { +3427 SpaceId::Initial => { +3428 self.upgrade_crypto(SpaceId::Handshake, crypto); +3429 } +3430 SpaceId::Handshake => { +3431 self.upgrade_crypto(SpaceId::Data, crypto); +3432 } +3433 _ => unreachable!("got updated secrets during 1-RTT"), +3434 } +3435 } +3436 if outgoing.is_empty() { +3437 if space == self.highest_space { +3438 break; +3439 } else { +3440 // Keys updated, check for more data to send +3441 continue; +3442 } +3443 } +3444 let offset = self.spaces[space].crypto_offset; +3445 let outgoing = Bytes::from(outgoing); +3446 if let Some(hs) = self.state.as_handshake_mut() { +3447 if space == SpaceId::Initial && offset == 0 && self.side.is_client() { +3448 hs.client_hello = Some(outgoing.clone()); +3449 } +3450 } +3451 self.spaces[space].crypto_offset += outgoing.len() as u64; +3452 trace!("wrote {} {:?} CRYPTO bytes", outgoing.len(), space); +3453 self.spaces[space].pending.crypto.push_back(frame::Crypto { +3454 offset, +3455 data: outgoing, +3456 }); +3457 } +3458 } +3459 +3460 /// Switch to stronger cryptography during handshake +3461 fn upgrade_crypto(&mut self, space: SpaceId, crypto: Keys) { +3462 debug_assert!( +3463 self.spaces[space].crypto.is_none(), +3464 "already reached packet space {space:?}" +3465 ); +3466 trace!("{:?} keys ready", space); +3467 if space == SpaceId::Data { +3468 // Precompute the first key update +3469 self.next_crypto = Some( +3470 self.crypto +3471 .next_1rtt_keys() +3472 .expect("handshake should be complete"), +3473 ); +3474 } +3475 +3476 self.spaces[space].crypto = Some(crypto); +3477 debug_assert!(space as usize > self.highest_space as usize); +3478 self.highest_space = space; +3479 if space == SpaceId::Data && self.side.is_client() { +3480 // Discard 0-RTT keys because 1-RTT keys are available. +3481 self.zero_rtt_crypto = None; +3482 } +3483 } +3484 +3485 fn discard_space(&mut self, now: Instant, space_id: SpaceId) { +3486 debug_assert!(space_id != SpaceId::Data); +3487 trace!("discarding {:?} keys", space_id); +3488 if space_id == SpaceId::Initial { +3489 // No longer needed +3490 if let ConnectionSide::Client { token, .. } = &mut self.side { +3491 *token = Bytes::new(); +3492 } +3493 } +3494 let space = &mut self.spaces[space_id]; +3495 space.crypto = None; +3496 let pns = space.for_path(PathId::ZERO); +3497 pns.time_of_last_ack_eliciting_packet = None; +3498 pns.loss_time = None; +3499 pns.loss_probes = 0; +3500 let sent_packets = mem::take(&mut pns.sent_packets); +3501 let path = self.paths.get_mut(&PathId::ZERO).unwrap(); +3502 for (_, packet) in sent_packets.into_iter() { +3503 path.data.remove_in_flight(&packet); +3504 } +3505 +3506 self.set_loss_detection_timer(now, PathId::ZERO) +3507 } +3508 +3509 fn handle_coalesced( +3510 &mut self, +3511 now: Instant, +3512 network_path: FourTuple, +3513 path_id: PathId, +3514 ecn: Option<EcnCodepoint>, +3515 data: BytesMut, +3516 ) { +3517 self.path_data_mut(path_id) +3518 .inc_total_recvd(data.len() as u64); +3519 let mut remaining = Some(data); +3520 let cid_len = self +3521 .local_cid_state +3522 .values() +3523 .map(|cid_state| cid_state.cid_len()) +3524 .next() +3525 .expect("one cid_state must exist"); +3526 while let Some(data) = remaining { +3527 match PartialDecode::new( +3528 data, +3529 &FixedLengthConnectionIdParser::new(cid_len), +3530 &[self.version], +3531 self.endpoint_config.grease_quic_bit, +3532 ) { +3533 Ok((partial_decode, rest)) => { +3534 remaining = rest; +3535 self.handle_decode(now, network_path, path_id, ecn, partial_decode); +3536 } +3537 Err(e) => { +3538 trace!("malformed header: {}", e); +3539 return; +3540 } +3541 } +3542 } +3543 } +3544 +3545 fn handle_decode( +3546 &mut self, +3547 now: Instant, +3548 network_path: FourTuple, +3549 path_id: PathId, +3550 ecn: Option<EcnCodepoint>, +3551 partial_decode: PartialDecode, +3552 ) { +3553 let qlog = QlogRecvPacket::new(partial_decode.len()); +3554 if let Some(decoded) = packet_crypto::unprotect_header( +3555 partial_decode, +3556 &self.spaces, +3557 self.zero_rtt_crypto.as_ref(), +3558 self.peer_params.stateless_reset_token, +3559 ) { +3560 self.handle_packet( +3561 now, +3562 network_path, +3563 path_id, +3564 ecn, +3565 decoded.packet, +3566 decoded.stateless_reset, +3567 qlog, +3568 ); +3569 } +3570 } +3571 +3572 fn handle_packet( +3573 &mut self, +3574 now: Instant, +3575 network_path: FourTuple, +3576 path_id: PathId, +3577 ecn: Option<EcnCodepoint>, +3578 packet: Option<Packet>, +3579 stateless_reset: bool, +3580 mut qlog: QlogRecvPacket, +3581 ) { +3582 self.stats.udp_rx.ios += 1; +3583 if let Some(ref packet) = packet { +3584 trace!( +3585 "got {:?} packet ({} bytes) from {} using id {}", +3586 packet.header.space(), +3587 packet.payload.len() + packet.header_data.len(), +3588 network_path, +3589 packet.header.dst_cid(), +3590 ); +3591 } +3592 +3593 if self.is_handshaking() { +3594 if path_id != PathId::ZERO { +3595 debug!(%network_path, %path_id, "discarding multipath packet during handshake"); +3596 return; +3597 } +3598 if network_path != self.path_data_mut(path_id).network_path { +3599 if let Some(hs) = self.state.as_handshake() { +3600 if hs.allow_server_migration { +3601 trace!(%network_path, prev = %self.path_data(path_id).network_path, "server migrated to new remote"); +3602 self.path_data_mut(path_id).network_path = network_path; +3603 self.qlog.emit_tuple_assigned(path_id, network_path, now); +3604 } else { +3605 debug!("discarding packet with unexpected remote during handshake"); +3606 return; +3607 } +3608 } else { +3609 debug!("discarding packet with unexpected remote during handshake"); +3610 return; +3611 } +3612 } +3613 } +3614 +3615 let was_closed = self.state.is_closed(); +3616 let was_drained = self.state.is_drained(); +3617 +3618 let decrypted = match packet { +3619 None => Err(None), +3620 Some(mut packet) => self +3621 .decrypt_packet(now, path_id, &mut packet) +3622 .map(move |number| (packet, number)), +3623 }; +3624 let result = match decrypted { +3625 _ if stateless_reset => { +3626 debug!("got stateless reset"); +3627 Err(ConnectionError::Reset) +3628 } +3629 Err(Some(e)) => { +3630 warn!("illegal packet: {}", e); +3631 Err(e.into()) +3632 } +3633 Err(None) => { +3634 debug!("failed to authenticate packet"); +3635 self.authentication_failures += 1; +3636 let integrity_limit = self.spaces[self.highest_space] +3637 .crypto +3638 .as_ref() +3639 .unwrap() +3640 .packet +3641 .local +3642 .integrity_limit(); +3643 if self.authentication_failures > integrity_limit { +3644 Err(TransportError::AEAD_LIMIT_REACHED("integrity limit violated").into()) +3645 } else { +3646 return; +3647 } +3648 } +3649 Ok((packet, number)) => { +3650 qlog.header(&packet.header, number, path_id); +3651 let span = match number { +3652 Some(pn) => trace_span!("recv", space = ?packet.header.space(), pn), +3653 None => trace_span!("recv", space = ?packet.header.space()), +3654 }; +3655 let _guard = span.enter(); +3656 +3657 let dedup = self.spaces[packet.header.space()] +3658 .path_space_mut(path_id) +3659 .map(|pns| &mut pns.dedup); +3660 if number.zip(dedup).is_some_and(|(n, d)| d.insert(n)) { +3661 debug!("discarding possible duplicate packet"); +3662 self.qlog.emit_packet_received(qlog, now); +3663 return; +3664 } else if self.state.is_handshake() && packet.header.is_short() { +3665 // TODO: SHOULD buffer these to improve reordering tolerance. +3666 trace!("dropping short packet during handshake"); 3667 self.qlog.emit_packet_received(qlog, now); 3668 return; -3669 } else if self.state.is_handshake() && packet.header.is_short() { -3670 // TODO: SHOULD buffer these to improve reordering tolerance. -3671 trace!("dropping short packet during handshake"); -3672 self.qlog.emit_packet_received(qlog, now); -3673 return; -3674 } else { -3675 if let Header::Initial(InitialHeader { ref token, .. }) = packet.header { -3676 if let Some(hs) = self.state.as_handshake() { -3677 if self.side.is_server() && token != &hs.expected_token { -3678 // Clients must send the same retry token in every Initial. Initial -3679 // packets can be spoofed, so we discard rather than killing the -3680 // connection. -3681 warn!("discarding Initial with invalid retry token"); -3682 self.qlog.emit_packet_received(qlog, now); -3683 return; -3684 } -3685 } -3686 } -3687 -3688 if !self.state.is_closed() { -3689 let spin = match packet.header { -3690 Header::Short { spin, .. } => spin, -3691 _ => false, -3692 }; -3693 -3694 if self.side().is_server() && !self.abandoned_paths.contains(&path_id) { -3695 // Only the client is allowed to open paths -3696 self.ensure_path(path_id, network_path, now, number); -3697 } -3698 if self.paths.contains_key(&path_id) { -3699 self.on_packet_authenticated( -3700 now, -3701 packet.header.space(), -3702 path_id, -3703 ecn, -3704 number, -3705 spin, -3706 packet.header.is_1rtt(), -3707 ); -3708 } -3709 } -3710 -3711 let res = self.process_decrypted_packet( -3712 now, -3713 network_path, -3714 path_id, -3715 number, -3716 packet, -3717 &mut qlog, -3718 ); -3719 -3720 self.qlog.emit_packet_received(qlog, now); -3721 res -3722 } -3723 } -3724 }; -3725 -3726 // State transitions for error cases -3727 if let Err(conn_err) = result { -3728 match conn_err { -3729 ConnectionError::ApplicationClosed(reason) => self.state.move_to_closed(reason), -3730 ConnectionError::ConnectionClosed(reason) => self.state.move_to_closed(reason), -3731 ConnectionError::Reset -3732 | ConnectionError::TransportError(TransportError { -3733 code: TransportErrorCode::AEAD_LIMIT_REACHED, -3734 .. -3735 }) => { -3736 self.state.move_to_drained(Some(conn_err)); -3737 } -3738 ConnectionError::TimedOut => { -3739 unreachable!("timeouts aren't generated by packet processing"); -3740 } -3741 ConnectionError::TransportError(err) => { -3742 debug!("closing connection due to transport error: {}", err); -3743 self.state.move_to_closed(err); -3744 } -3745 ConnectionError::VersionMismatch => { -3746 self.state.move_to_draining(Some(conn_err)); -3747 } -3748 ConnectionError::LocallyClosed => { -3749 unreachable!("LocallyClosed isn't generated by packet processing"); -3750 } -3751 ConnectionError::CidsExhausted => { -3752 unreachable!("CidsExhausted isn't generated by packet processing"); -3753 } -3754 }; -3755 } -3756 -3757 if !was_closed && self.state.is_closed() { -3758 self.close_common(); -3759 if !self.state.is_drained() { -3760 self.set_close_timer(now); -3761 } -3762 } -3763 if !was_drained && self.state.is_drained() { -3764 self.endpoint_events.push_back(EndpointEventInner::Drained); -3765 // Close timer may have been started previously, e.g. if we sent a close and got a -3766 // stateless reset in response -3767 self.timers -3768 .stop(Timer::Conn(ConnTimer::Close), self.qlog.with_time(now)); -3769 } -3770 -3771 // Transmit CONNECTION_CLOSE if necessary -3772 if matches!(self.state.as_type(), StateType::Closed) { -3773 // If there is no PathData for this PathId the packet was for a brand new -3774 // path. It was a valid packet however, so the remote is valid and we want to -3775 // send CONNECTION_CLOSE. -3776 let path_remote = self -3777 .paths -3778 .get(&path_id) -3779 .map(|p| p.data.network_path) -3780 .unwrap_or(network_path); -3781 self.close = network_path == path_remote; -3782 } -3783 } -3784 -3785 fn process_decrypted_packet( -3786 &mut self, -3787 now: Instant, -3788 network_path: FourTuple, -3789 path_id: PathId, -3790 number: Option<u64>, -3791 packet: Packet, -3792 qlog: &mut QlogRecvPacket, -3793 ) -> Result<(), ConnectionError> { -3794 if !self.paths.contains_key(&path_id) { -3795 // There is a chance this is a server side, first (for this path) packet, which would -3796 // be a protocol violation. It's more likely, however, that this is a packet of a -3797 // pruned path -3798 trace!(%path_id, ?number, "discarding packet for unknown path"); -3799 return Ok(()); -3800 } -3801 let state = match self.state.as_type() { -3802 StateType::Established => { -3803 match packet.header.space() { -3804 SpaceId::Data => self.process_payload( -3805 now, -3806 network_path, -3807 path_id, -3808 number.unwrap(), -3809 packet, -3810 qlog, -3811 )?, -3812 _ if packet.header.has_frames() => { -3813 self.process_early_payload(now, path_id, packet, qlog)? -3814 } -3815 _ => { -3816 trace!("discarding unexpected pre-handshake packet"); -3817 } -3818 } -3819 return Ok(()); -3820 } -3821 StateType::Closed => { -3822 for result in frame::Iter::new(packet.payload.freeze())? { -3823 let frame = match result { -3824 Ok(frame) => frame, -3825 Err(err) => { -3826 debug!("frame decoding error: {err:?}"); -3827 continue; -3828 } +3669 } else { +3670 if let Header::Initial(InitialHeader { ref token, .. }) = packet.header { +3671 if let Some(hs) = self.state.as_handshake() { +3672 if self.side.is_server() && token != &hs.expected_token { +3673 // Clients must send the same retry token in every Initial. Initial +3674 // packets can be spoofed, so we discard rather than killing the +3675 // connection. +3676 warn!("discarding Initial with invalid retry token"); +3677 self.qlog.emit_packet_received(qlog, now); +3678 return; +3679 } +3680 } +3681 } +3682 +3683 if !self.state.is_closed() { +3684 let spin = match packet.header { +3685 Header::Short { spin, .. } => spin, +3686 _ => false, +3687 }; +3688 +3689 if self.side().is_server() && !self.abandoned_paths.contains(&path_id) { +3690 // Only the client is allowed to open paths +3691 self.ensure_path(path_id, network_path, now, number); +3692 } +3693 if self.paths.contains_key(&path_id) { +3694 self.on_packet_authenticated( +3695 now, +3696 packet.header.space(), +3697 path_id, +3698 ecn, +3699 number, +3700 spin, +3701 packet.header.is_1rtt(), +3702 ); +3703 } +3704 } +3705 +3706 let res = self.process_decrypted_packet( +3707 now, +3708 network_path, +3709 path_id, +3710 number, +3711 packet, +3712 &mut qlog, +3713 ); +3714 +3715 self.qlog.emit_packet_received(qlog, now); +3716 res +3717 } +3718 } +3719 }; +3720 +3721 // State transitions for error cases +3722 if let Err(conn_err) = result { +3723 match conn_err { +3724 ConnectionError::ApplicationClosed(reason) => self.state.move_to_closed(reason), +3725 ConnectionError::ConnectionClosed(reason) => self.state.move_to_closed(reason), +3726 ConnectionError::Reset +3727 | ConnectionError::TransportError(TransportError { +3728 code: TransportErrorCode::AEAD_LIMIT_REACHED, +3729 .. +3730 }) => { +3731 self.state.move_to_drained(Some(conn_err)); +3732 } +3733 ConnectionError::TimedOut => { +3734 unreachable!("timeouts aren't generated by packet processing"); +3735 } +3736 ConnectionError::TransportError(err) => { +3737 debug!("closing connection due to transport error: {}", err); +3738 self.state.move_to_closed(err); +3739 } +3740 ConnectionError::VersionMismatch => { +3741 self.state.move_to_draining(Some(conn_err)); +3742 } +3743 ConnectionError::LocallyClosed => { +3744 unreachable!("LocallyClosed isn't generated by packet processing"); +3745 } +3746 ConnectionError::CidsExhausted => { +3747 unreachable!("CidsExhausted isn't generated by packet processing"); +3748 } +3749 }; +3750 } +3751 +3752 if !was_closed && self.state.is_closed() { +3753 self.close_common(); +3754 if !self.state.is_drained() { +3755 self.set_close_timer(now); +3756 } +3757 } +3758 if !was_drained && self.state.is_drained() { +3759 self.endpoint_events.push_back(EndpointEventInner::Drained); +3760 // Close timer may have been started previously, e.g. if we sent a close and got a +3761 // stateless reset in response +3762 self.timers +3763 .stop(Timer::Conn(ConnTimer::Close), self.qlog.with_time(now)); +3764 } +3765 +3766 // Transmit CONNECTION_CLOSE if necessary +3767 if matches!(self.state.as_type(), StateType::Closed) { +3768 // If there is no PathData for this PathId the packet was for a brand new +3769 // path. It was a valid packet however, so the remote is valid and we want to +3770 // send CONNECTION_CLOSE. +3771 let path_remote = self +3772 .paths +3773 .get(&path_id) +3774 .map(|p| p.data.network_path) +3775 .unwrap_or(network_path); +3776 self.close = network_path == path_remote; +3777 } +3778 } +3779 +3780 fn process_decrypted_packet( +3781 &mut self, +3782 now: Instant, +3783 network_path: FourTuple, +3784 path_id: PathId, +3785 number: Option<u64>, +3786 packet: Packet, +3787 qlog: &mut QlogRecvPacket, +3788 ) -> Result<(), ConnectionError> { +3789 if !self.paths.contains_key(&path_id) { +3790 // There is a chance this is a server side, first (for this path) packet, which would +3791 // be a protocol violation. It's more likely, however, that this is a packet of a +3792 // pruned path +3793 trace!(%path_id, ?number, "discarding packet for unknown path"); +3794 return Ok(()); +3795 } +3796 let state = match self.state.as_type() { +3797 StateType::Established => { +3798 match packet.header.space() { +3799 SpaceId::Data => self.process_payload( +3800 now, +3801 network_path, +3802 path_id, +3803 number.unwrap(), +3804 packet, +3805 qlog, +3806 )?, +3807 _ if packet.header.has_frames() => { +3808 self.process_early_payload(now, path_id, packet, qlog)? +3809 } +3810 _ => { +3811 trace!("discarding unexpected pre-handshake packet"); +3812 } +3813 } +3814 return Ok(()); +3815 } +3816 StateType::Closed => { +3817 for result in frame::Iter::new(packet.payload.freeze())? { +3818 let frame = match result { +3819 Ok(frame) => frame, +3820 Err(err) => { +3821 debug!("frame decoding error: {err:?}"); +3822 continue; +3823 } +3824 }; +3825 qlog.frame(&frame); +3826 +3827 if let Frame::Padding = frame { +3828 continue; 3829 }; -3830 qlog.frame(&frame); -3831 -3832 if let Frame::Padding = frame { -3833 continue; -3834 }; -3835 -3836 self.stats.frame_rx.record(frame.ty()); -3837 -3838 if let Frame::Close(_error) = frame { -3839 self.state.move_to_draining(None); -3840 break; -3841 } -3842 } -3843 return Ok(()); -3844 } -3845 StateType::Draining | StateType::Drained => return Ok(()), -3846 StateType::Handshake => self.state.as_handshake_mut().expect("checked"), -3847 }; -3848 -3849 match packet.header { -3850 Header::Retry { -3851 src_cid: rem_cid, .. -3852 } => { -3853 debug_assert_eq!(path_id, PathId::ZERO); -3854 if self.side.is_server() { -3855 return Err(TransportError::PROTOCOL_VIOLATION("client sent Retry").into()); -3856 } -3857 -3858 let is_valid_retry = self -3859 .rem_cids -3860 .get(&path_id) -3861 .map(|cids| cids.active()) -3862 .map(|orig_dst_cid| { -3863 self.crypto.is_valid_retry( -3864 orig_dst_cid, -3865 &packet.header_data, -3866 &packet.payload, -3867 ) -3868 }) -3869 .unwrap_or_default(); -3870 if self.total_authed_packets > 1 -3871 || packet.payload.len() <= 16 // token + 16 byte tag -3872 || !is_valid_retry -3873 { -3874 trace!("discarding invalid Retry"); -3875 // - After the client has received and processed an Initial or Retry -3876 // packet from the server, it MUST discard any subsequent Retry -3877 // packets that it receives. -3878 // - A client MUST discard a Retry packet with a zero-length Retry Token -3879 // field. -3880 // - Clients MUST discard Retry packets that have a Retry Integrity Tag -3881 // that cannot be validated -3882 return Ok(()); -3883 } -3884 -3885 trace!("retrying with CID {}", rem_cid); -3886 let client_hello = state.client_hello.take().unwrap(); -3887 self.retry_src_cid = Some(rem_cid); -3888 self.rem_cids -3889 .get_mut(&path_id) -3890 .expect("PathId::ZERO not yet abandoned, is_valid_retry would have been false") -3891 .update_initial_cid(rem_cid); -3892 self.rem_handshake_cid = rem_cid; +3830 +3831 self.stats.frame_rx.record(frame.ty()); +3832 +3833 if let Frame::Close(_error) = frame { +3834 self.state.move_to_draining(None); +3835 break; +3836 } +3837 } +3838 return Ok(()); +3839 } +3840 StateType::Draining | StateType::Drained => return Ok(()), +3841 StateType::Handshake => self.state.as_handshake_mut().expect("checked"), +3842 }; +3843 +3844 match packet.header { +3845 Header::Retry { +3846 src_cid: rem_cid, .. +3847 } => { +3848 debug_assert_eq!(path_id, PathId::ZERO); +3849 if self.side.is_server() { +3850 return Err(TransportError::PROTOCOL_VIOLATION("client sent Retry").into()); +3851 } +3852 +3853 let is_valid_retry = self +3854 .rem_cids +3855 .get(&path_id) +3856 .map(|cids| cids.active()) +3857 .map(|orig_dst_cid| { +3858 self.crypto.is_valid_retry( +3859 orig_dst_cid, +3860 &packet.header_data, +3861 &packet.payload, +3862 ) +3863 }) +3864 .unwrap_or_default(); +3865 if self.total_authed_packets > 1 +3866 || packet.payload.len() <= 16 // token + 16 byte tag +3867 || !is_valid_retry +3868 { +3869 trace!("discarding invalid Retry"); +3870 // - After the client has received and processed an Initial or Retry +3871 // packet from the server, it MUST discard any subsequent Retry +3872 // packets that it receives. +3873 // - A client MUST discard a Retry packet with a zero-length Retry Token +3874 // field. +3875 // - Clients MUST discard Retry packets that have a Retry Integrity Tag +3876 // that cannot be validated +3877 return Ok(()); +3878 } +3879 +3880 trace!("retrying with CID {}", rem_cid); +3881 let client_hello = state.client_hello.take().unwrap(); +3882 self.retry_src_cid = Some(rem_cid); +3883 self.rem_cids +3884 .get_mut(&path_id) +3885 .expect("PathId::ZERO not yet abandoned, is_valid_retry would have been false") +3886 .update_initial_cid(rem_cid); +3887 self.rem_handshake_cid = rem_cid; +3888 +3889 let space = &mut self.spaces[SpaceId::Initial]; +3890 if let Some(info) = space.for_path(PathId::ZERO).take(0) { +3891 self.on_packet_acked(now, PathId::ZERO, info); +3892 }; 3893 -3894 let space = &mut self.spaces[SpaceId::Initial]; -3895 if let Some(info) = space.for_path(PathId::ZERO).take(0) { -3896 self.on_packet_acked(now, PathId::ZERO, info); -3897 }; -3898 -3899 self.discard_space(now, SpaceId::Initial); // Make sure we clean up after -3900 // any retransmitted Initials -3901 self.spaces[SpaceId::Initial] = { -3902 let mut space = PacketSpace::new(now, SpaceId::Initial, &mut self.rng); -3903 space.crypto = Some(self.crypto.initial_keys(rem_cid, self.side.side())); -3904 space.crypto_offset = client_hello.len() as u64; -3905 space.for_path(path_id).next_packet_number = self.spaces[SpaceId::Initial] -3906 .for_path(path_id) -3907 .next_packet_number; -3908 space.pending.crypto.push_back(frame::Crypto { -3909 offset: 0, -3910 data: client_hello, -3911 }); -3912 space -3913 }; -3914 -3915 // Retransmit all 0-RTT data -3916 let zero_rtt = mem::take( -3917 &mut self.spaces[SpaceId::Data] -3918 .for_path(PathId::ZERO) -3919 .sent_packets, -3920 ); -3921 for (_, info) in zero_rtt.into_iter() { -3922 self.paths -3923 .get_mut(&PathId::ZERO) -3924 .unwrap() -3925 .remove_in_flight(&info); -3926 self.spaces[SpaceId::Data].pending |= info.retransmits; -3927 } -3928 self.streams.retransmit_all_for_0rtt(); -3929 -3930 let token_len = packet.payload.len() - 16; -3931 let ConnectionSide::Client { ref mut token, .. } = self.side else { -3932 unreachable!("we already short-circuited if we're server"); -3933 }; -3934 *token = packet.payload.freeze().split_to(token_len); -3935 -3936 self.state = State::handshake(state::Handshake { -3937 expected_token: Bytes::new(), -3938 rem_cid_set: false, -3939 client_hello: None, -3940 allow_server_migration: true, -3941 }); -3942 Ok(()) -3943 } -3944 Header::Long { -3945 ty: LongType::Handshake, -3946 src_cid: rem_cid, -3947 dst_cid: loc_cid, -3948 .. -3949 } => { -3950 debug_assert_eq!(path_id, PathId::ZERO); -3951 if rem_cid != self.rem_handshake_cid { -3952 debug!( -3953 "discarding packet with mismatched remote CID: {} != {}", -3954 self.rem_handshake_cid, rem_cid -3955 ); -3956 return Ok(()); -3957 } -3958 self.on_path_validated(path_id); +3894 self.discard_space(now, SpaceId::Initial); // Make sure we clean up after +3895 // any retransmitted Initials +3896 self.spaces[SpaceId::Initial] = { +3897 let mut space = PacketSpace::new(now, SpaceId::Initial, &mut self.rng); +3898 space.crypto = Some(self.crypto.initial_keys(rem_cid, self.side.side())); +3899 space.crypto_offset = client_hello.len() as u64; +3900 space.for_path(path_id).next_packet_number = self.spaces[SpaceId::Initial] +3901 .for_path(path_id) +3902 .next_packet_number; +3903 space.pending.crypto.push_back(frame::Crypto { +3904 offset: 0, +3905 data: client_hello, +3906 }); +3907 space +3908 }; +3909 +3910 // Retransmit all 0-RTT data +3911 let zero_rtt = mem::take( +3912 &mut self.spaces[SpaceId::Data] +3913 .for_path(PathId::ZERO) +3914 .sent_packets, +3915 ); +3916 for (_, info) in zero_rtt.into_iter() { +3917 self.paths +3918 .get_mut(&PathId::ZERO) +3919 .unwrap() +3920 .remove_in_flight(&info); +3921 self.spaces[SpaceId::Data].pending |= info.retransmits; +3922 } +3923 self.streams.retransmit_all_for_0rtt(); +3924 +3925 let token_len = packet.payload.len() - 16; +3926 let ConnectionSide::Client { ref mut token, .. } = self.side else { +3927 unreachable!("we already short-circuited if we're server"); +3928 }; +3929 *token = packet.payload.freeze().split_to(token_len); +3930 +3931 self.state = State::handshake(state::Handshake { +3932 expected_token: Bytes::new(), +3933 rem_cid_set: false, +3934 client_hello: None, +3935 allow_server_migration: true, +3936 }); +3937 Ok(()) +3938 } +3939 Header::Long { +3940 ty: LongType::Handshake, +3941 src_cid: rem_cid, +3942 dst_cid: loc_cid, +3943 .. +3944 } => { +3945 debug_assert_eq!(path_id, PathId::ZERO); +3946 if rem_cid != self.rem_handshake_cid { +3947 debug!( +3948 "discarding packet with mismatched remote CID: {} != {}", +3949 self.rem_handshake_cid, rem_cid +3950 ); +3951 return Ok(()); +3952 } +3953 self.on_path_validated(path_id); +3954 +3955 self.process_early_payload(now, path_id, packet, qlog)?; +3956 if self.state.is_closed() { +3957 return Ok(()); +3958 } 3959 -3960 self.process_early_payload(now, path_id, packet, qlog)?; -3961 if self.state.is_closed() { +3960 if self.crypto.is_handshaking() { +3961 trace!("handshake ongoing"); 3962 return Ok(()); 3963 } 3964 -3965 if self.crypto.is_handshaking() { -3966 trace!("handshake ongoing"); -3967 return Ok(()); -3968 } -3969 -3970 if self.side.is_client() { -3971 // Client-only because server params were set from the client's Initial -3972 let params = self.crypto.transport_parameters()?.ok_or_else(|| { -3973 TransportError::new( -3974 TransportErrorCode::crypto(0x6d), -3975 "transport parameters missing".to_owned(), -3976 ) -3977 })?; -3978 -3979 if self.has_0rtt() { -3980 if !self.crypto.early_data_accepted().unwrap() { -3981 debug_assert!(self.side.is_client()); -3982 debug!("0-RTT rejected"); -3983 self.accepted_0rtt = false; -3984 self.streams.zero_rtt_rejected(); -3985 -3986 // Discard already-queued frames -3987 self.spaces[SpaceId::Data].pending = Retransmits::default(); -3988 -3989 // Discard 0-RTT packets -3990 let sent_packets = mem::take( -3991 &mut self.spaces[SpaceId::Data].for_path(path_id).sent_packets, -3992 ); -3993 for (_, packet) in sent_packets.into_iter() { -3994 self.paths -3995 .get_mut(&path_id) -3996 .unwrap() -3997 .remove_in_flight(&packet); -3998 } -3999 } else { -4000 self.accepted_0rtt = true; -4001 params.validate_resumption_from(&self.peer_params)?; -4002 } -4003 } -4004 if let Some(token) = params.stateless_reset_token { -4005 // TODO(matheus23): Reset token for a remote, or for a 4-tuple? -4006 let remote = self.path_data(path_id).network_path.remote; -4007 self.endpoint_events -4008 .push_back(EndpointEventInner::ResetToken(path_id, remote, token)); -4009 } -4010 self.handle_peer_params(params, loc_cid, rem_cid, now)?; -4011 self.issue_first_cids(now); -4012 } else { -4013 // Server-only -4014 self.spaces[SpaceId::Data].pending.handshake_done = true; -4015 self.discard_space(now, SpaceId::Handshake); -4016 self.events.push_back(Event::HandshakeConfirmed); -4017 trace!("handshake confirmed"); -4018 } -4019 -4020 self.events.push_back(Event::Connected); -4021 self.state.move_to_established(); -4022 trace!("established"); -4023 -4024 // Multipath can only be enabled after the state has reached Established. -4025 // So this can not happen any earlier. -4026 self.issue_first_path_cids(now); -4027 Ok(()) -4028 } -4029 Header::Initial(InitialHeader { -4030 src_cid: rem_cid, -4031 dst_cid: loc_cid, -4032 .. -4033 }) => { -4034 debug_assert_eq!(path_id, PathId::ZERO); -4035 if !state.rem_cid_set { -4036 trace!("switching remote CID to {}", rem_cid); -4037 let mut state = state.clone(); -4038 self.rem_cids -4039 .get_mut(&path_id) -4040 .expect("PathId::ZERO not yet abandoned") -4041 .update_initial_cid(rem_cid); -4042 self.rem_handshake_cid = rem_cid; -4043 self.orig_rem_cid = rem_cid; -4044 state.rem_cid_set = true; -4045 self.state.move_to_handshake(state); -4046 } else if rem_cid != self.rem_handshake_cid { -4047 debug!( -4048 "discarding packet with mismatched remote CID: {} != {}", -4049 self.rem_handshake_cid, rem_cid -4050 ); -4051 return Ok(()); -4052 } -4053 -4054 let starting_space = self.highest_space; -4055 self.process_early_payload(now, path_id, packet, qlog)?; -4056 -4057 if self.side.is_server() -4058 && starting_space == SpaceId::Initial -4059 && self.highest_space != SpaceId::Initial -4060 { -4061 let params = self.crypto.transport_parameters()?.ok_or_else(|| { -4062 TransportError::new( -4063 TransportErrorCode::crypto(0x6d), -4064 "transport parameters missing".to_owned(), -4065 ) -4066 })?; -4067 self.handle_peer_params(params, loc_cid, rem_cid, now)?; -4068 self.issue_first_cids(now); -4069 self.init_0rtt(now); -4070 } -4071 Ok(()) -4072 } -4073 Header::Long { -4074 ty: LongType::ZeroRtt, -4075 .. -4076 } => { -4077 self.process_payload(now, network_path, path_id, number.unwrap(), packet, qlog)?; -4078 Ok(()) -4079 } -4080 Header::VersionNegotiate { .. } => { -4081 if self.total_authed_packets > 1 { -4082 return Ok(()); -4083 } -4084 let supported = packet -4085 .payload -4086 .chunks(4) -4087 .any(|x| match <[u8; 4]>::try_from(x) { -4088 Ok(version) => self.version == u32::from_be_bytes(version), -4089 Err(_) => false, -4090 }); -4091 if supported { -4092 return Ok(()); -4093 } -4094 debug!("remote doesn't support our version"); -4095 Err(ConnectionError::VersionMismatch) -4096 } -4097 Header::Short { .. } => unreachable!( -4098 "short packets received during handshake are discarded in handle_packet" -4099 ), -4100 } -4101 } -4102 -4103 /// Process an Initial or Handshake packet payload -4104 fn process_early_payload( -4105 &mut self, -4106 now: Instant, -4107 path_id: PathId, -4108 packet: Packet, -4109 #[allow(unused)] qlog: &mut QlogRecvPacket, -4110 ) -> Result<(), TransportError> { -4111 debug_assert_ne!(packet.header.space(), SpaceId::Data); -4112 debug_assert_eq!(path_id, PathId::ZERO); -4113 let payload_len = packet.payload.len(); -4114 let mut ack_eliciting = false; -4115 for result in frame::Iter::new(packet.payload.freeze())? { -4116 let frame = result?; -4117 qlog.frame(&frame); -4118 let span = match frame { -4119 Frame::Padding => continue, -4120 _ => Some(trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty)), -4121 }; +3965 if self.side.is_client() { +3966 // Client-only because server params were set from the client's Initial +3967 let params = self.crypto.transport_parameters()?.ok_or_else(|| { +3968 TransportError::new( +3969 TransportErrorCode::crypto(0x6d), +3970 "transport parameters missing".to_owned(), +3971 ) +3972 })?; +3973 +3974 if self.has_0rtt() { +3975 if !self.crypto.early_data_accepted().unwrap() { +3976 debug_assert!(self.side.is_client()); +3977 debug!("0-RTT rejected"); +3978 self.accepted_0rtt = false; +3979 self.streams.zero_rtt_rejected(); +3980 +3981 // Discard already-queued frames +3982 self.spaces[SpaceId::Data].pending = Retransmits::default(); +3983 +3984 // Discard 0-RTT packets +3985 let sent_packets = mem::take( +3986 &mut self.spaces[SpaceId::Data].for_path(path_id).sent_packets, +3987 ); +3988 for (_, packet) in sent_packets.into_iter() { +3989 self.paths +3990 .get_mut(&path_id) +3991 .unwrap() +3992 .remove_in_flight(&packet); +3993 } +3994 } else { +3995 self.accepted_0rtt = true; +3996 params.validate_resumption_from(&self.peer_params)?; +3997 } +3998 } +3999 if let Some(token) = params.stateless_reset_token { +4000 // TODO(matheus23): Reset token for a remote, or for a 4-tuple? +4001 let remote = self.path_data(path_id).network_path.remote; +4002 self.endpoint_events +4003 .push_back(EndpointEventInner::ResetToken(path_id, remote, token)); +4004 } +4005 self.handle_peer_params(params, loc_cid, rem_cid, now)?; +4006 self.issue_first_cids(now); +4007 } else { +4008 // Server-only +4009 self.spaces[SpaceId::Data].pending.handshake_done = true; +4010 self.discard_space(now, SpaceId::Handshake); +4011 self.events.push_back(Event::HandshakeConfirmed); +4012 trace!("handshake confirmed"); +4013 } +4014 +4015 self.events.push_back(Event::Connected); +4016 self.state.move_to_established(); +4017 trace!("established"); +4018 +4019 // Multipath can only be enabled after the state has reached Established. +4020 // So this can not happen any earlier. +4021 self.issue_first_path_cids(now); +4022 Ok(()) +4023 } +4024 Header::Initial(InitialHeader { +4025 src_cid: rem_cid, +4026 dst_cid: loc_cid, +4027 .. +4028 }) => { +4029 debug_assert_eq!(path_id, PathId::ZERO); +4030 if !state.rem_cid_set { +4031 trace!("switching remote CID to {}", rem_cid); +4032 let mut state = state.clone(); +4033 self.rem_cids +4034 .get_mut(&path_id) +4035 .expect("PathId::ZERO not yet abandoned") +4036 .update_initial_cid(rem_cid); +4037 self.rem_handshake_cid = rem_cid; +4038 self.orig_rem_cid = rem_cid; +4039 state.rem_cid_set = true; +4040 self.state.move_to_handshake(state); +4041 } else if rem_cid != self.rem_handshake_cid { +4042 debug!( +4043 "discarding packet with mismatched remote CID: {} != {}", +4044 self.rem_handshake_cid, rem_cid +4045 ); +4046 return Ok(()); +4047 } +4048 +4049 let starting_space = self.highest_space; +4050 self.process_early_payload(now, path_id, packet, qlog)?; +4051 +4052 if self.side.is_server() +4053 && starting_space == SpaceId::Initial +4054 && self.highest_space != SpaceId::Initial +4055 { +4056 let params = self.crypto.transport_parameters()?.ok_or_else(|| { +4057 TransportError::new( +4058 TransportErrorCode::crypto(0x6d), +4059 "transport parameters missing".to_owned(), +4060 ) +4061 })?; +4062 self.handle_peer_params(params, loc_cid, rem_cid, now)?; +4063 self.issue_first_cids(now); +4064 self.init_0rtt(now); +4065 } +4066 Ok(()) +4067 } +4068 Header::Long { +4069 ty: LongType::ZeroRtt, +4070 .. +4071 } => { +4072 self.process_payload(now, network_path, path_id, number.unwrap(), packet, qlog)?; +4073 Ok(()) +4074 } +4075 Header::VersionNegotiate { .. } => { +4076 if self.total_authed_packets > 1 { +4077 return Ok(()); +4078 } +4079 let supported = packet +4080 .payload +4081 .chunks(4) +4082 .any(|x| match <[u8; 4]>::try_from(x) { +4083 Ok(version) => self.version == u32::from_be_bytes(version), +4084 Err(_) => false, +4085 }); +4086 if supported { +4087 return Ok(()); +4088 } +4089 debug!("remote doesn't support our version"); +4090 Err(ConnectionError::VersionMismatch) +4091 } +4092 Header::Short { .. } => unreachable!( +4093 "short packets received during handshake are discarded in handle_packet" +4094 ), +4095 } +4096 } +4097 +4098 /// Process an Initial or Handshake packet payload +4099 fn process_early_payload( +4100 &mut self, +4101 now: Instant, +4102 path_id: PathId, +4103 packet: Packet, +4104 #[allow(unused)] qlog: &mut QlogRecvPacket, +4105 ) -> Result<(), TransportError> { +4106 debug_assert_ne!(packet.header.space(), SpaceId::Data); +4107 debug_assert_eq!(path_id, PathId::ZERO); +4108 let payload_len = packet.payload.len(); +4109 let mut ack_eliciting = false; +4110 for result in frame::Iter::new(packet.payload.freeze())? { +4111 let frame = result?; +4112 qlog.frame(&frame); +4113 let span = match frame { +4114 Frame::Padding => continue, +4115 _ => Some(trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty)), +4116 }; +4117 +4118 self.stats.frame_rx.record(frame.ty()); +4119 +4120 let _guard = span.as_ref().map(|x| x.enter()); +4121 ack_eliciting |= frame.is_ack_eliciting(); 4122 -4123 self.stats.frame_rx.record(frame.ty()); -4124 -4125 let _guard = span.as_ref().map(|x| x.enter()); -4126 ack_eliciting |= frame.is_ack_eliciting(); -4127 -4128 // Process frames -4129 if frame.is_1rtt() && packet.header.space() != SpaceId::Data { -4130 return Err(TransportError::PROTOCOL_VIOLATION( -4131 "illegal frame type in handshake", -4132 )); -4133 } -4134 -4135 match frame { -4136 Frame::Padding | Frame::Ping => {} -4137 Frame::Crypto(frame) => { -4138 self.read_crypto(packet.header.space(), &frame, payload_len)?; -4139 } -4140 Frame::Ack(ack) => { -4141 self.on_ack_received(now, packet.header.space(), ack)?; +4123 // Process frames +4124 if frame.is_1rtt() && packet.header.space() != SpaceId::Data { +4125 return Err(TransportError::PROTOCOL_VIOLATION( +4126 "illegal frame type in handshake", +4127 )); +4128 } +4129 +4130 match frame { +4131 Frame::Padding | Frame::Ping => {} +4132 Frame::Crypto(frame) => { +4133 self.read_crypto(packet.header.space(), &frame, payload_len)?; +4134 } +4135 Frame::Ack(ack) => { +4136 self.on_ack_received(now, packet.header.space(), ack)?; +4137 } +4138 Frame::PathAck(ack) => { +4139 span.as_ref() +4140 .map(|span| span.record("path", tracing::field::debug(&ack.path_id))); +4141 self.on_path_ack_received(now, packet.header.space(), ack)?; 4142 } -4143 Frame::PathAck(ack) => { -4144 span.as_ref() -4145 .map(|span| span.record("path", tracing::field::debug(&ack.path_id))); -4146 self.on_path_ack_received(now, packet.header.space(), ack)?; -4147 } -4148 Frame::Close(reason) => { -4149 self.state.move_to_draining(Some(reason.into())); -4150 return Ok(()); -4151 } -4152 _ => { -4153 let mut err = -4154 TransportError::PROTOCOL_VIOLATION("illegal frame type in handshake"); -4155 err.frame = frame::MaybeFrame::Known(frame.ty()); -4156 return Err(err); -4157 } -4158 } -4159 } -4160 -4161 if ack_eliciting { -4162 // In the initial and handshake spaces, ACKs must be sent immediately -4163 self.spaces[packet.header.space()] -4164 .for_path(path_id) -4165 .pending_acks -4166 .set_immediate_ack_required(); -4167 } -4168 -4169 self.write_crypto(); -4170 Ok(()) -4171 } -4172 -4173 /// Processes the packet payload, always in the data space. -4174 fn process_payload( -4175 &mut self, -4176 now: Instant, -4177 network_path: FourTuple, -4178 path_id: PathId, -4179 number: u64, -4180 packet: Packet, -4181 #[allow(unused)] qlog: &mut QlogRecvPacket, -4182 ) -> Result<(), TransportError> { -4183 let payload = packet.payload.freeze(); -4184 let mut is_probing_packet = true; -4185 let mut close = None; -4186 let payload_len = payload.len(); -4187 let mut ack_eliciting = false; -4188 // if this packet triggers a path migration and includes a observed address frame, it's -4189 // stored here -4190 let mut migration_observed_addr = None; -4191 for result in frame::Iter::new(payload)? { -4192 let frame = result?; -4193 qlog.frame(&frame); -4194 let span = match frame { -4195 Frame::Padding => continue, -4196 _ => trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty), -4197 }; -4198 -4199 self.stats.frame_rx.record(frame.ty()); -4200 // Crypto, Stream and Datagram frames are special cased in order no pollute -4201 // the log with payload data -4202 match &frame { -4203 Frame::Crypto(f) => { -4204 trace!(offset = f.offset, len = f.data.len(), "got crypto frame"); -4205 } -4206 Frame::Stream(f) => { -4207 trace!(id = %f.id, offset = f.offset, len = f.data.len(), fin = f.fin, "got stream frame"); -4208 } -4209 Frame::Datagram(f) => { -4210 trace!(len = f.data.len(), "got datagram frame"); -4211 } -4212 f => { -4213 trace!("got frame {f}"); -4214 } -4215 } -4216 -4217 let _guard = span.enter(); -4218 if packet.header.is_0rtt() { -4219 match frame { -4220 Frame::Crypto(_) | Frame::Close(Close::Application(_)) => { -4221 return Err(TransportError::PROTOCOL_VIOLATION( -4222 "illegal frame type in 0-RTT", -4223 )); -4224 } -4225 _ => { -4226 if frame.is_1rtt() { -4227 return Err(TransportError::PROTOCOL_VIOLATION( -4228 "illegal frame type in 0-RTT", -4229 )); -4230 } -4231 } -4232 } -4233 } -4234 ack_eliciting |= frame.is_ack_eliciting(); -4235 -4236 // Check whether this could be a probing packet -4237 match frame { -4238 Frame::Padding -4239 | Frame::PathChallenge(_) -4240 | Frame::PathResponse(_) -4241 | Frame::NewConnectionId(_) -4242 | Frame::ObservedAddr(_) => {} -4243 _ => { -4244 is_probing_packet = false; -4245 } -4246 } -4247 -4248 match frame { -4249 Frame::Crypto(frame) => { -4250 self.read_crypto(SpaceId::Data, &frame, payload_len)?; +4143 Frame::Close(reason) => { +4144 self.state.move_to_draining(Some(reason.into())); +4145 return Ok(()); +4146 } +4147 _ => { +4148 let mut err = +4149 TransportError::PROTOCOL_VIOLATION("illegal frame type in handshake"); +4150 err.frame = frame::MaybeFrame::Known(frame.ty()); +4151 return Err(err); +4152 } +4153 } +4154 } +4155 +4156 if ack_eliciting { +4157 // In the initial and handshake spaces, ACKs must be sent immediately +4158 self.spaces[packet.header.space()] +4159 .for_path(path_id) +4160 .pending_acks +4161 .set_immediate_ack_required(); +4162 } +4163 +4164 self.write_crypto(); +4165 Ok(()) +4166 } +4167 +4168 /// Processes the packet payload, always in the data space. +4169 fn process_payload( +4170 &mut self, +4171 now: Instant, +4172 network_path: FourTuple, +4173 path_id: PathId, +4174 number: u64, +4175 packet: Packet, +4176 #[allow(unused)] qlog: &mut QlogRecvPacket, +4177 ) -> Result<(), TransportError> { +4178 let payload = packet.payload.freeze(); +4179 let mut is_probing_packet = true; +4180 let mut close = None; +4181 let payload_len = payload.len(); +4182 let mut ack_eliciting = false; +4183 // if this packet triggers a path migration and includes a observed address frame, it's +4184 // stored here +4185 let mut migration_observed_addr = None; +4186 for result in frame::Iter::new(payload)? { +4187 let frame = result?; +4188 qlog.frame(&frame); +4189 let span = match frame { +4190 Frame::Padding => continue, +4191 _ => trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty), +4192 }; +4193 +4194 self.stats.frame_rx.record(frame.ty()); +4195 // Crypto, Stream and Datagram frames are special cased in order no pollute +4196 // the log with payload data +4197 match &frame { +4198 Frame::Crypto(f) => { +4199 trace!(offset = f.offset, len = f.data.len(), "got crypto frame"); +4200 } +4201 Frame::Stream(f) => { +4202 trace!(id = %f.id, offset = f.offset, len = f.data.len(), fin = f.fin, "got stream frame"); +4203 } +4204 Frame::Datagram(f) => { +4205 trace!(len = f.data.len(), "got datagram frame"); +4206 } +4207 f => { +4208 trace!("got frame {f}"); +4209 } +4210 } +4211 +4212 let _guard = span.enter(); +4213 if packet.header.is_0rtt() { +4214 match frame { +4215 Frame::Crypto(_) | Frame::Close(Close::Application(_)) => { +4216 return Err(TransportError::PROTOCOL_VIOLATION( +4217 "illegal frame type in 0-RTT", +4218 )); +4219 } +4220 _ => { +4221 if frame.is_1rtt() { +4222 return Err(TransportError::PROTOCOL_VIOLATION( +4223 "illegal frame type in 0-RTT", +4224 )); +4225 } +4226 } +4227 } +4228 } +4229 ack_eliciting |= frame.is_ack_eliciting(); +4230 +4231 // Check whether this could be a probing packet +4232 match frame { +4233 Frame::Padding +4234 | Frame::PathChallenge(_) +4235 | Frame::PathResponse(_) +4236 | Frame::NewConnectionId(_) +4237 | Frame::ObservedAddr(_) => {} +4238 _ => { +4239 is_probing_packet = false; +4240 } +4241 } +4242 +4243 match frame { +4244 Frame::Crypto(frame) => { +4245 self.read_crypto(SpaceId::Data, &frame, payload_len)?; +4246 } +4247 Frame::Stream(frame) => { +4248 if self.streams.received(frame, payload_len)?.should_transmit() { +4249 self.spaces[SpaceId::Data].pending.max_data = true; +4250 } 4251 } -4252 Frame::Stream(frame) => { -4253 if self.streams.received(frame, payload_len)?.should_transmit() { -4254 self.spaces[SpaceId::Data].pending.max_data = true; -4255 } -4256 } -4257 Frame::Ack(ack) => { -4258 self.on_ack_received(now, SpaceId::Data, ack)?; -4259 } -4260 Frame::PathAck(ack) => { -4261 span.record("path", tracing::field::debug(&ack.path_id)); -4262 self.on_path_ack_received(now, SpaceId::Data, ack)?; -4263 } -4264 Frame::Padding | Frame::Ping => {} -4265 Frame::Close(reason) => { -4266 close = Some(reason); -4267 } -4268 Frame::PathChallenge(challenge) => { -4269 let path = &mut self -4270 .path_mut(path_id) -4271 .expect("payload is processed only after the path becomes known"); -4272 path.path_responses.push(number, challenge.0, network_path); -4273 // At this point, update_network_path_or_discard was already called, so -4274 // we don't need to be lenient about `local_ip` possibly mis-matching. -4275 if network_path == path.network_path { -4276 // PATH_CHALLENGE on active path, possible off-path packet forwarding -4277 // attack. Send a non-probing packet to recover the active path. -4278 // TODO(flub): No longer true! We now path_challege also to validate -4279 // the path if the path is new, without an RFC9000-style -4280 // migration involved. This means we add in an extra -4281 // IMMEDIATE_ACK on some challenges. It isn't really wrong to do -4282 // so, but it still is something untidy. We should instead -4283 // suppress this when we know the remote is still validating the -4284 // path. -4285 match self.peer_supports_ack_frequency() { -4286 true => self.immediate_ack(path_id), -4287 false => { -4288 self.ping_path(path_id).ok(); -4289 } -4290 } -4291 } -4292 } -4293 Frame::PathResponse(response) => { -4294 let path = self -4295 .paths -4296 .get_mut(&path_id) -4297 .expect("payload is processed only after the path becomes known"); -4298 -4299 use PathTimer::*; -4300 use paths::OnPathResponseReceived::*; -4301 match path -4302 .data -4303 .on_path_response_received(now, response.0, network_path) -4304 { -4305 OnPath { was_open } => { -4306 let qlog = self.qlog.with_time(now); +4252 Frame::Ack(ack) => { +4253 self.on_ack_received(now, SpaceId::Data, ack)?; +4254 } +4255 Frame::PathAck(ack) => { +4256 span.record("path", tracing::field::debug(&ack.path_id)); +4257 self.on_path_ack_received(now, SpaceId::Data, ack)?; +4258 } +4259 Frame::Padding | Frame::Ping => {} +4260 Frame::Close(reason) => { +4261 close = Some(reason); +4262 } +4263 Frame::PathChallenge(challenge) => { +4264 let path = &mut self +4265 .path_mut(path_id) +4266 .expect("payload is processed only after the path becomes known"); +4267 path.path_responses.push(number, challenge.0, network_path); +4268 // At this point, update_network_path_or_discard was already called, so +4269 // we don't need to be lenient about `local_ip` possibly mis-matching. +4270 if network_path == path.network_path { +4271 // PATH_CHALLENGE on active path, possible off-path packet forwarding +4272 // attack. Send a non-probing packet to recover the active path. +4273 // TODO(flub): No longer true! We now path_challege also to validate +4274 // the path if the path is new, without an RFC9000-style +4275 // migration involved. This means we add in an extra +4276 // IMMEDIATE_ACK on some challenges. It isn't really wrong to do +4277 // so, but it still is something untidy. We should instead +4278 // suppress this when we know the remote is still validating the +4279 // path. +4280 match self.peer_supports_ack_frequency() { +4281 true => self.immediate_ack(path_id), +4282 false => { +4283 self.ping_path(path_id).ok(); +4284 } +4285 } +4286 } +4287 } +4288 Frame::PathResponse(response) => { +4289 let path = self +4290 .paths +4291 .get_mut(&path_id) +4292 .expect("payload is processed only after the path becomes known"); +4293 +4294 use PathTimer::*; +4295 use paths::OnPathResponseReceived::*; +4296 match path +4297 .data +4298 .on_path_response_received(now, response.0, network_path) +4299 { +4300 OnPath { was_open } => { +4301 let qlog = self.qlog.with_time(now); +4302 +4303 self.timers +4304 .stop(Timer::PerPath(path_id, PathValidation), qlog.clone()); +4305 self.timers +4306 .stop(Timer::PerPath(path_id, PathOpen), qlog.clone()); 4307 -4308 self.timers -4309 .stop(Timer::PerPath(path_id, PathValidation), qlog.clone()); -4310 self.timers -4311 .stop(Timer::PerPath(path_id, PathOpen), qlog.clone()); -4312 -4313 let next_challenge = path -4314 .data -4315 .earliest_expiring_challenge() -4316 .map(|time| time + self.ack_frequency.max_ack_delay_for_pto()); -4317 self.timers.set_or_stop( -4318 Timer::PerPath(path_id, PathChallengeLost), -4319 next_challenge, -4320 qlog, -4321 ); -4322 -4323 if !was_open { -4324 self.events -4325 .push_back(Event::Path(PathEvent::Opened { id: path_id })); -4326 if let Some(observed) = path.data.last_observed_addr_report.as_ref() -4327 { -4328 self.events.push_back(Event::Path(PathEvent::ObservedAddr { -4329 id: path_id, -4330 addr: observed.socket_addr(), -4331 })); -4332 } -4333 } -4334 if let Some((_, ref mut prev)) = path.prev { -4335 prev.challenges_sent.clear(); -4336 prev.send_new_challenge = false; -4337 } -4338 } -4339 OffPath => { -4340 debug!("Response to off-path PathChallenge!"); -4341 let next_challenge = path -4342 .data -4343 .earliest_expiring_challenge() -4344 .map(|time| time + self.ack_frequency.max_ack_delay_for_pto()); -4345 self.timers.set_or_stop( -4346 Timer::PerPath(path_id, PathChallengeLost), -4347 next_challenge, -4348 self.qlog.with_time(now), -4349 ); -4350 } -4351 Invalid { expected } => { -4352 debug!(%response, %network_path, %expected, "ignoring invalid PATH_RESPONSE") -4353 } -4354 Unknown => debug!(%response, "ignoring invalid PATH_RESPONSE"), -4355 } -4356 } -4357 Frame::MaxData(frame::MaxData(bytes)) => { -4358 self.streams.received_max_data(bytes); -4359 } -4360 Frame::MaxStreamData(frame::MaxStreamData { id, offset }) => { -4361 self.streams.received_max_stream_data(id, offset)?; -4362 } -4363 Frame::MaxStreams(frame::MaxStreams { dir, count }) => { -4364 self.streams.received_max_streams(dir, count)?; +4308 let next_challenge = path +4309 .data +4310 .earliest_expiring_challenge() +4311 .map(|time| time + self.ack_frequency.max_ack_delay_for_pto()); +4312 self.timers.set_or_stop( +4313 Timer::PerPath(path_id, PathChallengeLost), +4314 next_challenge, +4315 qlog, +4316 ); +4317 +4318 if !was_open { +4319 self.events +4320 .push_back(Event::Path(PathEvent::Opened { id: path_id })); +4321 if let Some(observed) = path.data.last_observed_addr_report.as_ref() +4322 { +4323 self.events.push_back(Event::Path(PathEvent::ObservedAddr { +4324 id: path_id, +4325 addr: observed.socket_addr(), +4326 })); +4327 } +4328 } +4329 if let Some((_, ref mut prev)) = path.prev { +4330 prev.challenges_sent.clear(); +4331 prev.send_new_challenge = false; +4332 } +4333 } +4334 OffPath => { +4335 debug!("Response to off-path PathChallenge!"); +4336 let next_challenge = path +4337 .data +4338 .earliest_expiring_challenge() +4339 .map(|time| time + self.ack_frequency.max_ack_delay_for_pto()); +4340 self.timers.set_or_stop( +4341 Timer::PerPath(path_id, PathChallengeLost), +4342 next_challenge, +4343 self.qlog.with_time(now), +4344 ); +4345 } +4346 Invalid { expected } => { +4347 debug!(%response, %network_path, %expected, "ignoring invalid PATH_RESPONSE") +4348 } +4349 Unknown => debug!(%response, "ignoring invalid PATH_RESPONSE"), +4350 } +4351 } +4352 Frame::MaxData(frame::MaxData(bytes)) => { +4353 self.streams.received_max_data(bytes); +4354 } +4355 Frame::MaxStreamData(frame::MaxStreamData { id, offset }) => { +4356 self.streams.received_max_stream_data(id, offset)?; +4357 } +4358 Frame::MaxStreams(frame::MaxStreams { dir, count }) => { +4359 self.streams.received_max_streams(dir, count)?; +4360 } +4361 Frame::ResetStream(frame) => { +4362 if self.streams.received_reset(frame)?.should_transmit() { +4363 self.spaces[SpaceId::Data].pending.max_data = true; +4364 } 4365 } -4366 Frame::ResetStream(frame) => { -4367 if self.streams.received_reset(frame)?.should_transmit() { -4368 self.spaces[SpaceId::Data].pending.max_data = true; -4369 } -4370 } -4371 Frame::DataBlocked { offset } => { -4372 debug!(offset, "peer claims to be blocked at connection level"); -4373 } -4374 Frame::StreamDataBlocked { id, offset } => { -4375 if id.initiator() == self.side.side() && id.dir() == Dir::Uni { -4376 debug!("got STREAM_DATA_BLOCKED on send-only {}", id); -4377 return Err(TransportError::STREAM_STATE_ERROR( -4378 "STREAM_DATA_BLOCKED on send-only stream", -4379 )); -4380 } -4381 debug!( -4382 stream = %id, -4383 offset, "peer claims to be blocked at stream level" -4384 ); -4385 } -4386 Frame::StreamsBlocked { dir, limit } => { -4387 if limit > MAX_STREAM_COUNT { -4388 return Err(TransportError::FRAME_ENCODING_ERROR( -4389 "unrepresentable stream limit", -4390 )); -4391 } -4392 debug!( -4393 "peer claims to be blocked opening more than {} {} streams", -4394 limit, dir -4395 ); -4396 } -4397 Frame::StopSending(frame::StopSending { id, error_code }) => { -4398 if id.initiator() != self.side.side() { -4399 if id.dir() == Dir::Uni { -4400 debug!("got STOP_SENDING on recv-only {}", id); -4401 return Err(TransportError::STREAM_STATE_ERROR( -4402 "STOP_SENDING on recv-only stream", -4403 )); -4404 } -4405 } else if self.streams.is_local_unopened(id) { -4406 return Err(TransportError::STREAM_STATE_ERROR( -4407 "STOP_SENDING on unopened stream", -4408 )); -4409 } -4410 self.streams.received_stop_sending(id, error_code); -4411 } -4412 Frame::RetireConnectionId(frame::RetireConnectionId { path_id, sequence }) => { -4413 if let Some(ref path_id) = path_id { -4414 span.record("path", tracing::field::debug(&path_id)); -4415 } -4416 let path_id = path_id.unwrap_or_default(); -4417 match self.local_cid_state.get_mut(&path_id) { -4418 None => error!(?path_id, "RETIRE_CONNECTION_ID for unknown path"), -4419 Some(cid_state) => { -4420 let allow_more_cids = cid_state -4421 .on_cid_retirement(sequence, self.peer_params.issue_cids_limit())?; -4422 -4423 // If the path has closed, we do not issue more CIDs for this path -4424 // For details see https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.2.2 -4425 // > an endpoint SHOULD provide new connection IDs for that path, if still open, using PATH_NEW_CONNECTION_ID frames. -4426 let has_path = !self.abandoned_paths.contains(&path_id); -4427 let allow_more_cids = allow_more_cids && has_path; -4428 -4429 self.endpoint_events -4430 .push_back(EndpointEventInner::RetireConnectionId( -4431 now, -4432 path_id, -4433 sequence, -4434 allow_more_cids, -4435 )); -4436 } -4437 } -4438 } -4439 Frame::NewConnectionId(frame) => { -4440 let path_id = if let Some(path_id) = frame.path_id { -4441 if !self.is_multipath_negotiated() { +4366 Frame::DataBlocked { offset } => { +4367 debug!(offset, "peer claims to be blocked at connection level"); +4368 } +4369 Frame::StreamDataBlocked { id, offset } => { +4370 if id.initiator() == self.side.side() && id.dir() == Dir::Uni { +4371 debug!("got STREAM_DATA_BLOCKED on send-only {}", id); +4372 return Err(TransportError::STREAM_STATE_ERROR( +4373 "STREAM_DATA_BLOCKED on send-only stream", +4374 )); +4375 } +4376 debug!( +4377 stream = %id, +4378 offset, "peer claims to be blocked at stream level" +4379 ); +4380 } +4381 Frame::StreamsBlocked { dir, limit } => { +4382 if limit > MAX_STREAM_COUNT { +4383 return Err(TransportError::FRAME_ENCODING_ERROR( +4384 "unrepresentable stream limit", +4385 )); +4386 } +4387 debug!( +4388 "peer claims to be blocked opening more than {} {} streams", +4389 limit, dir +4390 ); +4391 } +4392 Frame::StopSending(frame::StopSending { id, error_code }) => { +4393 if id.initiator() != self.side.side() { +4394 if id.dir() == Dir::Uni { +4395 debug!("got STOP_SENDING on recv-only {}", id); +4396 return Err(TransportError::STREAM_STATE_ERROR( +4397 "STOP_SENDING on recv-only stream", +4398 )); +4399 } +4400 } else if self.streams.is_local_unopened(id) { +4401 return Err(TransportError::STREAM_STATE_ERROR( +4402 "STOP_SENDING on unopened stream", +4403 )); +4404 } +4405 self.streams.received_stop_sending(id, error_code); +4406 } +4407 Frame::RetireConnectionId(frame::RetireConnectionId { path_id, sequence }) => { +4408 if let Some(ref path_id) = path_id { +4409 span.record("path", tracing::field::debug(&path_id)); +4410 } +4411 let path_id = path_id.unwrap_or_default(); +4412 match self.local_cid_state.get_mut(&path_id) { +4413 None => error!(?path_id, "RETIRE_CONNECTION_ID for unknown path"), +4414 Some(cid_state) => { +4415 let allow_more_cids = cid_state +4416 .on_cid_retirement(sequence, self.peer_params.issue_cids_limit())?; +4417 +4418 // If the path has closed, we do not issue more CIDs for this path +4419 // For details see https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.2.2 +4420 // > an endpoint SHOULD provide new connection IDs for that path, if still open, using PATH_NEW_CONNECTION_ID frames. +4421 let has_path = !self.abandoned_paths.contains(&path_id); +4422 let allow_more_cids = allow_more_cids && has_path; +4423 +4424 self.endpoint_events +4425 .push_back(EndpointEventInner::RetireConnectionId( +4426 now, +4427 path_id, +4428 sequence, +4429 allow_more_cids, +4430 )); +4431 } +4432 } +4433 } +4434 Frame::NewConnectionId(frame) => { +4435 let path_id = if let Some(path_id) = frame.path_id { +4436 if !self.is_multipath_negotiated() { +4437 return Err(TransportError::PROTOCOL_VIOLATION( +4438 "received PATH_NEW_CONNECTION_ID frame when multipath was not negotiated", +4439 )); +4440 } +4441 if path_id > self.local_max_path_id { 4442 return Err(TransportError::PROTOCOL_VIOLATION( -4443 "received PATH_NEW_CONNECTION_ID frame when multipath was not negotiated", +4443 "PATH_NEW_CONNECTION_ID contains path_id exceeding current max", 4444 )); 4445 } -4446 if path_id > self.local_max_path_id { -4447 return Err(TransportError::PROTOCOL_VIOLATION( -4448 "PATH_NEW_CONNECTION_ID contains path_id exceeding current max", -4449 )); -4450 } -4451 path_id -4452 } else { -4453 PathId::ZERO -4454 }; -4455 -4456 if self.abandoned_paths.contains(&path_id) { -4457 trace!("ignoring issued CID for abandoned path"); -4458 continue; -4459 } -4460 if let Some(ref path_id) = frame.path_id { -4461 span.record("path", tracing::field::debug(&path_id)); -4462 } -4463 let rem_cids = self -4464 .rem_cids -4465 .entry(path_id) -4466 .or_insert_with(|| CidQueue::new(frame.id)); -4467 if rem_cids.active().is_empty() { +4446 path_id +4447 } else { +4448 PathId::ZERO +4449 }; +4450 +4451 if self.abandoned_paths.contains(&path_id) { +4452 trace!("ignoring issued CID for abandoned path"); +4453 continue; +4454 } +4455 if let Some(ref path_id) = frame.path_id { +4456 span.record("path", tracing::field::debug(&path_id)); +4457 } +4458 let rem_cids = self +4459 .rem_cids +4460 .entry(path_id) +4461 .or_insert_with(|| CidQueue::new(frame.id)); +4462 if rem_cids.active().is_empty() { +4463 return Err(TransportError::PROTOCOL_VIOLATION( +4464 "NEW_CONNECTION_ID when CIDs aren't in use", +4465 )); +4466 } +4467 if frame.retire_prior_to > frame.sequence { 4468 return Err(TransportError::PROTOCOL_VIOLATION( -4469 "NEW_CONNECTION_ID when CIDs aren't in use", +4469 "NEW_CONNECTION_ID retiring unissued CIDs", 4470 )); 4471 } -4472 if frame.retire_prior_to > frame.sequence { -4473 return Err(TransportError::PROTOCOL_VIOLATION( -4474 "NEW_CONNECTION_ID retiring unissued CIDs", -4475 )); -4476 } -4477 -4478 use crate::cid_queue::InsertError; -4479 match rem_cids.insert(frame) { -4480 Ok(None) if self.path(path_id).is_none() => { -4481 // if this gives us CIDs to open a new path and a nat traversal attempt -4482 // is underway we could try to probe a pending remote -4483 self.continue_nat_traversal_round(now); -4484 } -4485 Ok(None) => {} -4486 Ok(Some((retired, reset_token))) => { -4487 let pending_retired = -4488 &mut self.spaces[SpaceId::Data].pending.retire_cids; -4489 /// Ensure `pending_retired` cannot grow without bound. Limit is -4490 /// somewhat arbitrary but very permissive. -4491 const MAX_PENDING_RETIRED_CIDS: u64 = CidQueue::LEN as u64 * 10; -4492 // We don't bother counting in-flight frames because those are bounded -4493 // by congestion control. -4494 if (pending_retired.len() as u64) -4495 .saturating_add(retired.end.saturating_sub(retired.start)) -4496 > MAX_PENDING_RETIRED_CIDS -4497 { -4498 return Err(TransportError::CONNECTION_ID_LIMIT_ERROR( -4499 "queued too many retired CIDs", -4500 )); -4501 } -4502 pending_retired.extend(retired.map(|seq| (path_id, seq))); -4503 // TODO(matheus23): Reset token for a remote or a full 4-tuple? -4504 self.set_reset_token(path_id, network_path.remote, reset_token); -4505 } -4506 Err(InsertError::ExceedsLimit) => { -4507 return Err(TransportError::CONNECTION_ID_LIMIT_ERROR("")); -4508 } -4509 Err(InsertError::Retired) => { -4510 trace!("discarding already-retired"); -4511 // RETIRE_CONNECTION_ID might not have been previously sent if e.g. a -4512 // range of connection IDs larger than the active connection ID limit -4513 // was retired all at once via retire_prior_to. -4514 self.spaces[SpaceId::Data] -4515 .pending -4516 .retire_cids -4517 .push((path_id, frame.sequence)); -4518 continue; -4519 } -4520 }; -4521 -4522 if self.side.is_server() -4523 && path_id == PathId::ZERO -4524 && self -4525 .rem_cids -4526 .get(&PathId::ZERO) -4527 .map(|cids| cids.active_seq() == 0) -4528 .unwrap_or_default() -4529 { -4530 // We're a server still using the initial remote CID for the client, so -4531 // let's switch immediately to enable clientside stateless resets. -4532 self.update_rem_cid(PathId::ZERO); -4533 } -4534 } -4535 Frame::NewToken(NewToken { token }) => { -4536 let ConnectionSide::Client { -4537 token_store, -4538 server_name, -4539 .. -4540 } = &self.side -4541 else { -4542 return Err(TransportError::PROTOCOL_VIOLATION("client sent NEW_TOKEN")); -4543 }; -4544 if token.is_empty() { -4545 return Err(TransportError::FRAME_ENCODING_ERROR("empty token")); -4546 } -4547 trace!("got new token"); -4548 token_store.insert(server_name, token); -4549 } -4550 Frame::Datagram(datagram) => { -4551 if self -4552 .datagrams -4553 .received(datagram, &self.config.datagram_receive_buffer_size)? -4554 { -4555 self.events.push_back(Event::DatagramReceived); -4556 } -4557 } -4558 Frame::AckFrequency(ack_frequency) => { -4559 // This frame can only be sent in the Data space -4560 -4561 if !self.ack_frequency.ack_frequency_received(&ack_frequency)? { -4562 // The AckFrequency frame is stale (we have already received a more -4563 // recent one) -4564 continue; -4565 } -4566 -4567 // Update the params for all of our paths -4568 for (path_id, space) in self.spaces[SpaceId::Data].number_spaces.iter_mut() { -4569 space.pending_acks.set_ack_frequency_params(&ack_frequency); -4570 -4571 // Our `max_ack_delay` has been updated, so we may need to adjust -4572 // its associated timeout -4573 if let Some(timeout) = space -4574 .pending_acks -4575 .max_ack_delay_timeout(self.ack_frequency.max_ack_delay) -4576 { -4577 self.timers.set( -4578 Timer::PerPath(*path_id, PathTimer::MaxAckDelay), -4579 timeout, -4580 self.qlog.with_time(now), -4581 ); -4582 } -4583 } -4584 } -4585 Frame::ImmediateAck => { -4586 // This frame can only be sent in the Data space -4587 for pns in self.spaces[SpaceId::Data].iter_paths_mut() { -4588 pns.pending_acks.set_immediate_ack_required(); -4589 } -4590 } -4591 Frame::HandshakeDone => { -4592 if self.side.is_server() { -4593 return Err(TransportError::PROTOCOL_VIOLATION( -4594 "client sent HANDSHAKE_DONE", -4595 )); -4596 } -4597 if self.spaces[SpaceId::Handshake].crypto.is_some() { -4598 self.discard_space(now, SpaceId::Handshake); -4599 } -4600 self.events.push_back(Event::HandshakeConfirmed); -4601 trace!("handshake confirmed"); -4602 } -4603 Frame::ObservedAddr(observed) => { -4604 // check if params allows the peer to send report and this node to receive it -4605 trace!(seq_no = %observed.seq_no, ip = %observed.ip, port = observed.port); -4606 if !self -4607 .peer_params -4608 .address_discovery_role -4609 .should_report(&self.config.address_discovery_role) -4610 { -4611 return Err(TransportError::PROTOCOL_VIOLATION( -4612 "received OBSERVED_ADDRESS frame when not negotiated", -4613 )); -4614 } -4615 // must only be sent in data space -4616 if packet.header.space() != SpaceId::Data { -4617 return Err(TransportError::PROTOCOL_VIOLATION( -4618 "OBSERVED_ADDRESS frame outside data space", -4619 )); -4620 } -4621 -4622 let path = self.path_data_mut(path_id); -4623 if network_path == path.network_path { -4624 if let Some(updated) = path.update_observed_addr_report(observed) { -4625 if path.open { -4626 self.events.push_back(Event::Path(PathEvent::ObservedAddr { -4627 id: path_id, -4628 addr: updated, -4629 })); -4630 } -4631 // otherwise the event is reported when the path is deemed open -4632 } -4633 } else { -4634 // include in migration -4635 migration_observed_addr = Some(observed) -4636 } -4637 } -4638 Frame::PathAbandon(frame::PathAbandon { -4639 path_id, -4640 error_code, -4641 }) => { -4642 span.record("path", tracing::field::debug(&path_id)); -4643 // TODO(flub): don't really know which error code to use here. -4644 let already_abandoned = match self.close_path(now, path_id, error_code.into()) { -4645 Ok(()) => { -4646 trace!("peer abandoned path"); -4647 false -4648 } -4649 Err(ClosePathError::LastOpenPath) => { -4650 trace!("peer abandoned last path, closing connection"); -4651 // TODO(flub): which error code? -4652 return Err(TransportError::NO_ERROR("last path abandoned by peer")); -4653 } -4654 Err(ClosePathError::ClosedPath) => { -4655 trace!("peer abandoned already closed path"); -4656 true -4657 } -4658 }; -4659 // If we receive a retransmit of PATH_ABANDON then we may already have -4660 // abandoned this path locally. In that case the DiscardPath timer -4661 // may already have fired and we no longer have any state for this path. -4662 // Only set this timer if we still have path state. -4663 if self.path(path_id).is_some() && !already_abandoned { -4664 // TODO(flub): Checking is_some() here followed by a number of calls -4665 // that would panic if it was None is really ugly. If only we -4666 // could do something like PathData::pto(). One day we'll have -4667 // unified SpaceId and PathId and this will be possible. -4668 let delay = self.pto(SpaceId::Data, path_id) * 3; -4669 self.timers.set( -4670 Timer::PerPath(path_id, PathTimer::DiscardPath), -4671 now + delay, -4672 self.qlog.with_time(now), -4673 ); -4674 } -4675 } -4676 Frame::PathStatusAvailable(info) => { -4677 span.record("path", tracing::field::debug(&info.path_id)); -4678 if self.is_multipath_negotiated() { -4679 self.on_path_status( -4680 info.path_id, -4681 PathStatus::Available, -4682 info.status_seq_no, -4683 ); -4684 } else { -4685 return Err(TransportError::PROTOCOL_VIOLATION( -4686 "received PATH_STATUS_AVAILABLE frame when multipath was not negotiated", -4687 )); -4688 } -4689 } -4690 Frame::PathStatusBackup(info) => { -4691 span.record("path", tracing::field::debug(&info.path_id)); -4692 if self.is_multipath_negotiated() { -4693 self.on_path_status(info.path_id, PathStatus::Backup, info.status_seq_no); -4694 } else { -4695 return Err(TransportError::PROTOCOL_VIOLATION( -4696 "received PATH_STATUS_BACKUP frame when multipath was not negotiated", -4697 )); -4698 } -4699 } -4700 Frame::MaxPathId(frame::MaxPathId(path_id)) => { -4701 span.record("path", tracing::field::debug(&path_id)); -4702 if !self.is_multipath_negotiated() { -4703 return Err(TransportError::PROTOCOL_VIOLATION( -4704 "received MAX_PATH_ID frame when multipath was not negotiated", -4705 )); -4706 } -4707 // frames that do not increase the path id are ignored -4708 if path_id > self.remote_max_path_id { -4709 self.remote_max_path_id = path_id; -4710 self.issue_first_path_cids(now); -4711 while let Some(true) = self.continue_nat_traversal_round(now) {} -4712 } -4713 } -4714 Frame::PathsBlocked(frame::PathsBlocked(max_path_id)) => { -4715 // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST -4716 // be treated as a connection error of type PROTOCOL_VIOLATION. -4717 // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids> -4718 if self.is_multipath_negotiated() { -4719 if self.local_max_path_id > max_path_id { -4720 return Err(TransportError::PROTOCOL_VIOLATION( -4721 "PATHS_BLOCKED maximum path identifier was larger than local maximum", -4722 )); -4723 } -4724 debug!("received PATHS_BLOCKED({:?})", max_path_id); -4725 // TODO(@divma): ensure max concurrent paths -4726 } else { -4727 return Err(TransportError::PROTOCOL_VIOLATION( -4728 "received PATHS_BLOCKED frame when not multipath was not negotiated", -4729 )); -4730 } -4731 } -4732 Frame::PathCidsBlocked(frame::PathCidsBlocked { path_id, next_seq }) => { -4733 // Nothing to do. This is recorded in the frame stats, but otherwise we -4734 // always issue all CIDs we're allowed to issue, so either this is an -4735 // impatient peer or a bug on our side. -4736 -4737 // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST -4738 // be treated as a connection error of type PROTOCOL_VIOLATION. -4739 // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids> -4740 if self.is_multipath_negotiated() { -4741 if path_id > self.local_max_path_id { -4742 return Err(TransportError::PROTOCOL_VIOLATION( -4743 "PATH_CIDS_BLOCKED path identifier was larger than local maximum", -4744 )); -4745 } -4746 if next_seq.0 -4747 > self -4748 .local_cid_state -4749 .get(&path_id) -4750 .map(|cid_state| cid_state.active_seq().1 + 1) -4751 .unwrap_or_default() -4752 { -4753 return Err(TransportError::PROTOCOL_VIOLATION( -4754 "PATH_CIDS_BLOCKED next sequence number larger than in local state", -4755 )); -4756 } -4757 debug!(%path_id, %next_seq, "received PATH_CIDS_BLOCKED"); -4758 } else { -4759 return Err(TransportError::PROTOCOL_VIOLATION( -4760 "received PATH_CIDS_BLOCKED frame when not multipath was not negotiated", -4761 )); -4762 } -4763 } -4764 Frame::AddAddress(addr) => { -4765 let client_state = match self.iroh_hp.client_side_mut() { -4766 Ok(state) => state, -4767 Err(err) => { -4768 return Err(TransportError::PROTOCOL_VIOLATION(format!( -4769 "Nat traversal(ADD_ADDRESS): {err}" -4770 ))); -4771 } -4772 }; +4472 +4473 use crate::cid_queue::InsertError; +4474 match rem_cids.insert(frame) { +4475 Ok(None) if self.path(path_id).is_none() => { +4476 // if this gives us CIDs to open a new path and a nat traversal attempt +4477 // is underway we could try to probe a pending remote +4478 self.continue_nat_traversal_round(now); +4479 } +4480 Ok(None) => {} +4481 Ok(Some((retired, reset_token))) => { +4482 let pending_retired = +4483 &mut self.spaces[SpaceId::Data].pending.retire_cids; +4484 /// Ensure `pending_retired` cannot grow without bound. Limit is +4485 /// somewhat arbitrary but very permissive. +4486 const MAX_PENDING_RETIRED_CIDS: u64 = CidQueue::LEN as u64 * 10; +4487 // We don't bother counting in-flight frames because those are bounded +4488 // by congestion control. +4489 if (pending_retired.len() as u64) +4490 .saturating_add(retired.end.saturating_sub(retired.start)) +4491 > MAX_PENDING_RETIRED_CIDS +4492 { +4493 return Err(TransportError::CONNECTION_ID_LIMIT_ERROR( +4494 "queued too many retired CIDs", +4495 )); +4496 } +4497 pending_retired.extend(retired.map(|seq| (path_id, seq))); +4498 // TODO(matheus23): Reset token for a remote or a full 4-tuple? +4499 self.set_reset_token(path_id, network_path.remote, reset_token); +4500 } +4501 Err(InsertError::ExceedsLimit) => { +4502 return Err(TransportError::CONNECTION_ID_LIMIT_ERROR("")); +4503 } +4504 Err(InsertError::Retired) => { +4505 trace!("discarding already-retired"); +4506 // RETIRE_CONNECTION_ID might not have been previously sent if e.g. a +4507 // range of connection IDs larger than the active connection ID limit +4508 // was retired all at once via retire_prior_to. +4509 self.spaces[SpaceId::Data] +4510 .pending +4511 .retire_cids +4512 .push((path_id, frame.sequence)); +4513 continue; +4514 } +4515 }; +4516 +4517 if self.side.is_server() +4518 && path_id == PathId::ZERO +4519 && self +4520 .rem_cids +4521 .get(&PathId::ZERO) +4522 .map(|cids| cids.active_seq() == 0) +4523 .unwrap_or_default() +4524 { +4525 // We're a server still using the initial remote CID for the client, so +4526 // let's switch immediately to enable clientside stateless resets. +4527 self.update_rem_cid(PathId::ZERO); +4528 } +4529 } +4530 Frame::NewToken(NewToken { token }) => { +4531 let ConnectionSide::Client { +4532 token_store, +4533 server_name, +4534 .. +4535 } = &self.side +4536 else { +4537 return Err(TransportError::PROTOCOL_VIOLATION("client sent NEW_TOKEN")); +4538 }; +4539 if token.is_empty() { +4540 return Err(TransportError::FRAME_ENCODING_ERROR("empty token")); +4541 } +4542 trace!("got new token"); +4543 token_store.insert(server_name, token); +4544 } +4545 Frame::Datagram(datagram) => { +4546 if self +4547 .datagrams +4548 .received(datagram, &self.config.datagram_receive_buffer_size)? +4549 { +4550 self.events.push_back(Event::DatagramReceived); +4551 } +4552 } +4553 Frame::AckFrequency(ack_frequency) => { +4554 // This frame can only be sent in the Data space +4555 +4556 if !self.ack_frequency.ack_frequency_received(&ack_frequency)? { +4557 // The AckFrequency frame is stale (we have already received a more +4558 // recent one) +4559 continue; +4560 } +4561 +4562 // Update the params for all of our paths +4563 for (path_id, space) in self.spaces[SpaceId::Data].number_spaces.iter_mut() { +4564 space.pending_acks.set_ack_frequency_params(&ack_frequency); +4565 +4566 // Our `max_ack_delay` has been updated, so we may need to adjust +4567 // its associated timeout +4568 if let Some(timeout) = space +4569 .pending_acks +4570 .max_ack_delay_timeout(self.ack_frequency.max_ack_delay) +4571 { +4572 self.timers.set( +4573 Timer::PerPath(*path_id, PathTimer::MaxAckDelay), +4574 timeout, +4575 self.qlog.with_time(now), +4576 ); +4577 } +4578 } +4579 } +4580 Frame::ImmediateAck => { +4581 // This frame can only be sent in the Data space +4582 for pns in self.spaces[SpaceId::Data].iter_paths_mut() { +4583 pns.pending_acks.set_immediate_ack_required(); +4584 } +4585 } +4586 Frame::HandshakeDone => { +4587 if self.side.is_server() { +4588 return Err(TransportError::PROTOCOL_VIOLATION( +4589 "client sent HANDSHAKE_DONE", +4590 )); +4591 } +4592 if self.spaces[SpaceId::Handshake].crypto.is_some() { +4593 self.discard_space(now, SpaceId::Handshake); +4594 } +4595 self.events.push_back(Event::HandshakeConfirmed); +4596 trace!("handshake confirmed"); +4597 } +4598 Frame::ObservedAddr(observed) => { +4599 // check if params allows the peer to send report and this node to receive it +4600 trace!(seq_no = %observed.seq_no, ip = %observed.ip, port = observed.port); +4601 if !self +4602 .peer_params +4603 .address_discovery_role +4604 .should_report(&self.config.address_discovery_role) +4605 { +4606 return Err(TransportError::PROTOCOL_VIOLATION( +4607 "received OBSERVED_ADDRESS frame when not negotiated", +4608 )); +4609 } +4610 // must only be sent in data space +4611 if packet.header.space() != SpaceId::Data { +4612 return Err(TransportError::PROTOCOL_VIOLATION( +4613 "OBSERVED_ADDRESS frame outside data space", +4614 )); +4615 } +4616 +4617 let path = self.path_data_mut(path_id); +4618 if network_path == path.network_path { +4619 if let Some(updated) = path.update_observed_addr_report(observed) { +4620 if path.open { +4621 self.events.push_back(Event::Path(PathEvent::ObservedAddr { +4622 id: path_id, +4623 addr: updated, +4624 })); +4625 } +4626 // otherwise the event is reported when the path is deemed open +4627 } +4628 } else { +4629 // include in migration +4630 migration_observed_addr = Some(observed) +4631 } +4632 } +4633 Frame::PathAbandon(frame::PathAbandon { +4634 path_id, +4635 error_code, +4636 }) => { +4637 span.record("path", tracing::field::debug(&path_id)); +4638 // TODO(flub): don't really know which error code to use here. +4639 let already_abandoned = match self.close_path(now, path_id, error_code.into()) { +4640 Ok(()) => { +4641 trace!("peer abandoned path"); +4642 false +4643 } +4644 Err(ClosePathError::LastOpenPath) => { +4645 trace!("peer abandoned last path, closing connection"); +4646 // TODO(flub): which error code? +4647 return Err(TransportError::NO_ERROR("last path abandoned by peer")); +4648 } +4649 Err(ClosePathError::ClosedPath) => { +4650 trace!("peer abandoned already closed path"); +4651 true +4652 } +4653 }; +4654 // If we receive a retransmit of PATH_ABANDON then we may already have +4655 // abandoned this path locally. In that case the DiscardPath timer +4656 // may already have fired and we no longer have any state for this path. +4657 // Only set this timer if we still have path state. +4658 if self.path(path_id).is_some() && !already_abandoned { +4659 // TODO(flub): Checking is_some() here followed by a number of calls +4660 // that would panic if it was None is really ugly. If only we +4661 // could do something like PathData::pto(). One day we'll have +4662 // unified SpaceId and PathId and this will be possible. +4663 let delay = self.pto(SpaceId::Data, path_id) * 3; +4664 self.timers.set( +4665 Timer::PerPath(path_id, PathTimer::DiscardPath), +4666 now + delay, +4667 self.qlog.with_time(now), +4668 ); +4669 } +4670 } +4671 Frame::PathStatusAvailable(info) => { +4672 span.record("path", tracing::field::debug(&info.path_id)); +4673 if self.is_multipath_negotiated() { +4674 self.on_path_status( +4675 info.path_id, +4676 PathStatus::Available, +4677 info.status_seq_no, +4678 ); +4679 } else { +4680 return Err(TransportError::PROTOCOL_VIOLATION( +4681 "received PATH_STATUS_AVAILABLE frame when multipath was not negotiated", +4682 )); +4683 } +4684 } +4685 Frame::PathStatusBackup(info) => { +4686 span.record("path", tracing::field::debug(&info.path_id)); +4687 if self.is_multipath_negotiated() { +4688 self.on_path_status(info.path_id, PathStatus::Backup, info.status_seq_no); +4689 } else { +4690 return Err(TransportError::PROTOCOL_VIOLATION( +4691 "received PATH_STATUS_BACKUP frame when multipath was not negotiated", +4692 )); +4693 } +4694 } +4695 Frame::MaxPathId(frame::MaxPathId(path_id)) => { +4696 span.record("path", tracing::field::debug(&path_id)); +4697 if !self.is_multipath_negotiated() { +4698 return Err(TransportError::PROTOCOL_VIOLATION( +4699 "received MAX_PATH_ID frame when multipath was not negotiated", +4700 )); +4701 } +4702 // frames that do not increase the path id are ignored +4703 if path_id > self.remote_max_path_id { +4704 self.remote_max_path_id = path_id; +4705 self.issue_first_path_cids(now); +4706 while let Some(true) = self.continue_nat_traversal_round(now) {} +4707 } +4708 } +4709 Frame::PathsBlocked(frame::PathsBlocked(max_path_id)) => { +4710 // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST +4711 // be treated as a connection error of type PROTOCOL_VIOLATION. +4712 // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids> +4713 if self.is_multipath_negotiated() { +4714 if self.local_max_path_id > max_path_id { +4715 return Err(TransportError::PROTOCOL_VIOLATION( +4716 "PATHS_BLOCKED maximum path identifier was larger than local maximum", +4717 )); +4718 } +4719 debug!("received PATHS_BLOCKED({:?})", max_path_id); +4720 // TODO(@divma): ensure max concurrent paths +4721 } else { +4722 return Err(TransportError::PROTOCOL_VIOLATION( +4723 "received PATHS_BLOCKED frame when not multipath was not negotiated", +4724 )); +4725 } +4726 } +4727 Frame::PathCidsBlocked(frame::PathCidsBlocked { path_id, next_seq }) => { +4728 // Nothing to do. This is recorded in the frame stats, but otherwise we +4729 // always issue all CIDs we're allowed to issue, so either this is an +4730 // impatient peer or a bug on our side. +4731 +4732 // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST +4733 // be treated as a connection error of type PROTOCOL_VIOLATION. +4734 // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids> +4735 if self.is_multipath_negotiated() { +4736 if path_id > self.local_max_path_id { +4737 return Err(TransportError::PROTOCOL_VIOLATION( +4738 "PATH_CIDS_BLOCKED path identifier was larger than local maximum", +4739 )); +4740 } +4741 if next_seq.0 +4742 > self +4743 .local_cid_state +4744 .get(&path_id) +4745 .map(|cid_state| cid_state.active_seq().1 + 1) +4746 .unwrap_or_default() +4747 { +4748 return Err(TransportError::PROTOCOL_VIOLATION( +4749 "PATH_CIDS_BLOCKED next sequence number larger than in local state", +4750 )); +4751 } +4752 debug!(%path_id, %next_seq, "received PATH_CIDS_BLOCKED"); +4753 } else { +4754 return Err(TransportError::PROTOCOL_VIOLATION( +4755 "received PATH_CIDS_BLOCKED frame when not multipath was not negotiated", +4756 )); +4757 } +4758 } +4759 Frame::AddAddress(addr) => { +4760 let client_state = match self.iroh_hp.client_side_mut() { +4761 Ok(state) => state, +4762 Err(err) => { +4763 return Err(TransportError::PROTOCOL_VIOLATION(format!( +4764 "Nat traversal(ADD_ADDRESS): {err}" +4765 ))); +4766 } +4767 }; +4768 +4769 if !client_state.check_remote_address(&addr) { +4770 // if the address is not valid we flag it, but update anyway +4771 warn!(?addr, "server sent illegal ADD_ADDRESS frame"); +4772 } 4773 -4774 if !client_state.check_remote_address(&addr) { -4775 // if the address is not valid we flag it, but update anyway -4776 warn!(?addr, "server sent illegal ADD_ADDRESS frame"); -4777 } -4778 -4779 match client_state.add_remote_address(addr) { -4780 Ok(maybe_added) => { -4781 if let Some(added) = maybe_added { -4782 self.events.push_back(Event::NatTraversal( -4783 iroh_hp::Event::AddressAdded(added), -4784 )); -4785 } -4786 } -4787 Err(e) => { -4788 warn!(%e, "failed to add remote address") -4789 } -4790 } -4791 } -4792 Frame::RemoveAddress(addr) => { -4793 let client_state = match self.iroh_hp.client_side_mut() { -4794 Ok(state) => state, -4795 Err(err) => { -4796 return Err(TransportError::PROTOCOL_VIOLATION(format!( -4797 "Nat traversal(REMOVE_ADDRESS): {err}" -4798 ))); -4799 } -4800 }; -4801 if let Some(removed_addr) = client_state.remove_remote_address(addr) { -4802 self.events -4803 .push_back(Event::NatTraversal(iroh_hp::Event::AddressRemoved( -4804 removed_addr, -4805 ))); -4806 } -4807 } -4808 Frame::ReachOut(reach_out) => { -4809 let server_state = match self.iroh_hp.server_side_mut() { -4810 Ok(state) => state, -4811 Err(err) => { -4812 return Err(TransportError::PROTOCOL_VIOLATION(format!( -4813 "Nat traversal(REACH_OUT): {err}" -4814 ))); -4815 } -4816 }; -4817 -4818 if let Err(err) = server_state.handle_reach_out(reach_out) { -4819 return Err(TransportError::PROTOCOL_VIOLATION(format!( -4820 "Nat traversal(REACH_OUT): {err}" -4821 ))); -4822 } -4823 } -4824 } -4825 } -4826 -4827 let space = self.spaces[SpaceId::Data].for_path(path_id); -4828 if space -4829 .pending_acks -4830 .packet_received(now, number, ack_eliciting, &space.dedup) -4831 { -4832 if self.abandoned_paths.contains(&path_id) { -4833 // § 3.4.3 QUIC-MULTIPATH: promptly send ACKs for packets received from -4834 // abandoned paths. -4835 space.pending_acks.set_immediate_ack_required(); -4836 } else { -4837 self.timers.set( -4838 Timer::PerPath(path_id, PathTimer::MaxAckDelay), -4839 now + self.ack_frequency.max_ack_delay, -4840 self.qlog.with_time(now), -4841 ); -4842 } -4843 } -4844 -4845 // Issue stream ID credit due to ACKs of outgoing finish/resets and incoming finish/resets -4846 // on stopped streams. Incoming finishes/resets on open streams are not handled here as they -4847 // are only freed, and hence only issue credit, once the application has been notified -4848 // during a read on the stream. -4849 let pending = &mut self.spaces[SpaceId::Data].pending; -4850 self.streams.queue_max_stream_id(pending); +4774 match client_state.add_remote_address(addr) { +4775 Ok(maybe_added) => { +4776 if let Some(added) = maybe_added { +4777 self.events.push_back(Event::NatTraversal( +4778 iroh_hp::Event::AddressAdded(added), +4779 )); +4780 } +4781 } +4782 Err(e) => { +4783 warn!(%e, "failed to add remote address") +4784 } +4785 } +4786 } +4787 Frame::RemoveAddress(addr) => { +4788 let client_state = match self.iroh_hp.client_side_mut() { +4789 Ok(state) => state, +4790 Err(err) => { +4791 return Err(TransportError::PROTOCOL_VIOLATION(format!( +4792 "Nat traversal(REMOVE_ADDRESS): {err}" +4793 ))); +4794 } +4795 }; +4796 if let Some(removed_addr) = client_state.remove_remote_address(addr) { +4797 self.events +4798 .push_back(Event::NatTraversal(iroh_hp::Event::AddressRemoved( +4799 removed_addr, +4800 ))); +4801 } +4802 } +4803 Frame::ReachOut(reach_out) => { +4804 let server_state = match self.iroh_hp.server_side_mut() { +4805 Ok(state) => state, +4806 Err(err) => { +4807 return Err(TransportError::PROTOCOL_VIOLATION(format!( +4808 "Nat traversal(REACH_OUT): {err}" +4809 ))); +4810 } +4811 }; +4812 +4813 if let Err(err) = server_state.handle_reach_out(reach_out) { +4814 return Err(TransportError::PROTOCOL_VIOLATION(format!( +4815 "Nat traversal(REACH_OUT): {err}" +4816 ))); +4817 } +4818 } +4819 } +4820 } +4821 +4822 let space = self.spaces[SpaceId::Data].for_path(path_id); +4823 if space +4824 .pending_acks +4825 .packet_received(now, number, ack_eliciting, &space.dedup) +4826 { +4827 if self.abandoned_paths.contains(&path_id) { +4828 // § 3.4.3 QUIC-MULTIPATH: promptly send ACKs for packets received from +4829 // abandoned paths. +4830 space.pending_acks.set_immediate_ack_required(); +4831 } else { +4832 self.timers.set( +4833 Timer::PerPath(path_id, PathTimer::MaxAckDelay), +4834 now + self.ack_frequency.max_ack_delay, +4835 self.qlog.with_time(now), +4836 ); +4837 } +4838 } +4839 +4840 // Issue stream ID credit due to ACKs of outgoing finish/resets and incoming finish/resets +4841 // on stopped streams. Incoming finishes/resets on open streams are not handled here as they +4842 // are only freed, and hence only issue credit, once the application has been notified +4843 // during a read on the stream. +4844 let pending = &mut self.spaces[SpaceId::Data].pending; +4845 self.streams.queue_max_stream_id(pending); +4846 +4847 if let Some(reason) = close { +4848 self.state.move_to_draining(Some(reason.into())); +4849 self.close = true; +4850 } 4851 -4852 if let Some(reason) = close { -4853 self.state.move_to_draining(Some(reason.into())); -4854 self.close = true; -4855 } -4856 -4857 if Some(number) == self.spaces[SpaceId::Data].for_path(path_id).rx_packet -4858 && !is_probing_packet -4859 && network_path != self.path_data(path_id).network_path -4860 { -4861 let ConnectionSide::Server { ref server_config } = self.side else { -4862 panic!("packets from unknown remote should be dropped by clients"); -4863 }; -4864 debug_assert!( -4865 server_config.migration, -4866 "migration-initiating packets should have been dropped immediately" -4867 ); -4868 self.migrate(path_id, now, network_path, migration_observed_addr); -4869 // Break linkability, if possible -4870 self.update_rem_cid(path_id); -4871 self.spin = false; -4872 } -4873 -4874 Ok(()) -4875 } -4876 -4877 fn migrate( -4878 &mut self, -4879 path_id: PathId, -4880 now: Instant, -4881 network_path: FourTuple, -4882 observed_addr: Option<ObservedAddr>, -4883 ) { -4884 trace!(%network_path, %path_id, "migration initiated"); -4885 self.path_counter = self.path_counter.wrapping_add(1); -4886 // TODO(@divma): conditions for path migration in multipath are very specific, check them -4887 // again to prevent path migrations that should actually create a new path -4888 -4889 // Reset rtt/congestion state for new path unless it looks like a NAT rebinding. -4890 // Note that the congestion window will not grow until validation terminates. Helps mitigate -4891 // amplification attacks performed by spoofing source addresses. -4892 let prev_pto = self.pto(SpaceId::Data, path_id); -4893 let known_path = self.paths.get_mut(&path_id).expect("known path"); -4894 let path = &mut known_path.data; -4895 let mut new_path = if network_path.remote.is_ipv4() -4896 && network_path.remote.ip() == path.network_path.remote.ip() -4897 { -4898 PathData::from_previous(network_path, path, self.path_counter, now) -4899 } else { -4900 let peer_max_udp_payload_size = -4901 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()) -4902 .unwrap_or(u16::MAX); -4903 PathData::new( -4904 network_path, -4905 self.allow_mtud, -4906 Some(peer_max_udp_payload_size), -4907 self.path_counter, -4908 now, -4909 &self.config, -4910 ) -4911 }; -4912 new_path.last_observed_addr_report = path.last_observed_addr_report.clone(); -4913 if let Some(report) = observed_addr { -4914 if let Some(updated) = new_path.update_observed_addr_report(report) { -4915 tracing::info!("adding observed addr event from migration"); -4916 self.events.push_back(Event::Path(PathEvent::ObservedAddr { -4917 id: path_id, -4918 addr: updated, -4919 })); -4920 } -4921 } -4922 new_path.send_new_challenge = true; -4923 -4924 let mut prev = mem::replace(path, new_path); -4925 // Don't clobber the original path if the previous one hasn't been validated yet -4926 if !prev.is_validating_path() { -4927 prev.send_new_challenge = true; -4928 // We haven't updated the remote CID yet, this captures the remote CID we were using on -4929 // the previous path. -4930 -4931 known_path.prev = Some((self.rem_cids.get(&path_id).unwrap().active(), prev)); -4932 } -4933 -4934 // We need to re-assign the correct remote to this path in qlog -4935 self.qlog.emit_tuple_assigned(path_id, network_path, now); -4936 -4937 self.timers.set( -4938 Timer::PerPath(path_id, PathTimer::PathValidation), -4939 now + 3 * cmp::max(self.pto(SpaceId::Data, path_id), prev_pto), -4940 self.qlog.with_time(now), -4941 ); -4942 } -4943 -4944 /// Handle a change in the local address, i.e. an active migration -4945 pub fn local_address_changed(&mut self) { -4946 // TODO(flub): if multipath is enabled this needs to create a new path entirely. -4947 self.update_rem_cid(PathId::ZERO); -4948 self.ping(); -4949 } -4950 -4951 /// Switch to a previously unused remote connection ID, if possible -4952 fn update_rem_cid(&mut self, path_id: PathId) { -4953 let Some((reset_token, retired)) = -4954 self.rem_cids.get_mut(&path_id).and_then(|cids| cids.next()) -4955 else { -4956 return; -4957 }; -4958 -4959 // Retire the current remote CID and any CIDs we had to skip. -4960 self.spaces[SpaceId::Data] -4961 .pending -4962 .retire_cids -4963 .extend(retired.map(|seq| (path_id, seq))); -4964 let remote = self.path_data(path_id).network_path.remote; -4965 self.set_reset_token(path_id, remote, reset_token); -4966 } -4967 -4968 /// Sends this reset token to the endpoint -4969 /// -4970 /// The endpoint needs to know the reset tokens issued by the peer, so that if the peer -4971 /// sends a reset token it knows to route it to this connection. See RFC 9000 section -4972 /// 10.3. Stateless Reset. -4973 /// -4974 /// Reset tokens are different for each path, the endpoint identifies paths by peer -4975 /// socket address however, not by path ID. -4976 fn set_reset_token(&mut self, path_id: PathId, remote: SocketAddr, reset_token: ResetToken) { -4977 self.endpoint_events -4978 .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token)); -4979 -4980 // During the handshake the server sends a reset token in the transport -4981 // parameters. When we are the client and we receive the reset token during the -4982 // handshake we want this to affect our peer transport parameters. -4983 // TODO(flub): Pretty sure this is pointless, the entire params is overwritten -4984 // shortly after this was called. And then the params don't have this anymore. -4985 if path_id == PathId::ZERO { -4986 self.peer_params.stateless_reset_token = Some(reset_token); -4987 } -4988 } -4989 -4990 /// Issue an initial set of connection IDs to the peer upon connection -4991 fn issue_first_cids(&mut self, now: Instant) { -4992 if self -4993 .local_cid_state -4994 .get(&PathId::ZERO) -4995 .expect("PathId::ZERO exists when the connection is created") -4996 .cid_len() -4997 == 0 -4998 { -4999 return; -5000 } -5001 -5002 // Subtract 1 to account for the CID we supplied while handshaking -5003 let mut n = self.peer_params.issue_cids_limit() - 1; -5004 if let ConnectionSide::Server { server_config } = &self.side { -5005 if server_config.has_preferred_address() { -5006 // We also sent a CID in the transport parameters -5007 n -= 1; -5008 } -5009 } -5010 self.endpoint_events -5011 .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n)); -5012 } -5013 -5014 /// Issues an initial set of CIDs for paths that have not yet had any CIDs issued -5015 /// -5016 /// Later CIDs are issued when CIDs expire or are retired by the peer. -5017 fn issue_first_path_cids(&mut self, now: Instant) { -5018 if let Some(max_path_id) = self.max_path_id() { -5019 let mut path_id = self.max_path_id_with_cids.next(); -5020 while path_id <= max_path_id { -5021 self.endpoint_events -5022 .push_back(EndpointEventInner::NeedIdentifiers( -5023 path_id, -5024 now, -5025 self.peer_params.issue_cids_limit(), -5026 )); -5027 path_id = path_id.next(); -5028 } -5029 self.max_path_id_with_cids = max_path_id; -5030 } -5031 } -5032 -5033 /// Populates a packet with frames -5034 /// -5035 /// This tries to fit as many frames as possible into the packet. -5036 /// -5037 /// *path_exclusive_only* means to only build frames which can only be sent on this -5038 /// *path. This is used in multipath for backup paths while there is still an active -5039 /// *path. -5040 fn populate_packet<'a, 'b>( -5041 &mut self, -5042 now: Instant, -5043 space_id: SpaceId, -5044 path_id: PathId, -5045 path_exclusive_only: bool, -5046 builder: &mut PacketBuilder<'a, 'b>, -5047 ) { -5048 let pn = builder.exact_number; -5049 let is_multipath_negotiated = self.is_multipath_negotiated(); -5050 let stats = &mut self.stats.frame_tx; -5051 let space = &mut self.spaces[space_id]; -5052 let path = &mut self.paths.get_mut(&path_id).expect("known path").data; -5053 let is_0rtt = space_id == SpaceId::Data && space.crypto.is_none(); -5054 space -5055 .for_path(path_id) -5056 .pending_acks -5057 .maybe_ack_non_eliciting(); -5058 -5059 // HANDSHAKE_DONE -5060 if !is_0rtt && mem::replace(&mut space.pending.handshake_done, false) { -5061 trace!("HANDSHAKE_DONE"); -5062 builder.encode(frame::HandshakeDone, stats); -5063 } -5064 -5065 // REACH_OUT -5066 // TODO(@divma): path explusive considerations -5067 if let Some((round, addresses)) = space.pending.reach_out.as_mut() { -5068 while let Some(local_addr) = addresses.pop() { -5069 let reach_out = frame::ReachOut::new(*round, local_addr); -5070 if builder.frame_space_remaining() > reach_out.size() { -5071 trace!(%round, ?local_addr, "REACH_OUT"); -5072 builder.encode(reach_out, stats); -5073 } else { -5074 addresses.push(local_addr); -5075 break; -5076 } -5077 } -5078 if addresses.is_empty() { -5079 space.pending.reach_out = None; -5080 } -5081 } -5082 -5083 // OBSERVED_ADDR -5084 if !path_exclusive_only -5085 && space_id == SpaceId::Data -5086 && self -5087 .config -5088 .address_discovery_role -5089 .should_report(&self.peer_params.address_discovery_role) -5090 && (!path.observed_addr_sent || space.pending.observed_addr) -5091 { -5092 let frame = -5093 frame::ObservedAddr::new(path.network_path.remote, self.next_observed_addr_seq_no); -5094 if builder.frame_space_remaining() > frame.size() { -5095 trace!(seq = %frame.seq_no, ip = %frame.ip, port = frame.port, "OBSERVED_ADDRESS"); -5096 builder.encode(frame, stats); -5097 -5098 self.next_observed_addr_seq_no = self.next_observed_addr_seq_no.saturating_add(1u8); -5099 path.observed_addr_sent = true; -5100 -5101 space.pending.observed_addr = false; -5102 } -5103 } -5104 -5105 // PING -5106 if mem::replace(&mut space.for_path(path_id).ping_pending, false) { -5107 trace!("PING"); -5108 builder.encode(frame::Ping, stats); -5109 } -5110 -5111 // IMMEDIATE_ACK -5112 if mem::replace(&mut space.for_path(path_id).immediate_ack_pending, false) { -5113 debug_assert_eq!( -5114 space_id, -5115 SpaceId::Data, -5116 "immediate acks must be sent in the data space" -5117 ); -5118 trace!("IMMEDIATE_ACK"); -5119 builder.encode(frame::ImmediateAck, stats); -5120 } -5121 -5122 // ACK -5123 // TODO(flub): Should this send acks for this path anyway? -5124 -5125 if !path_exclusive_only { -5126 for path_id in space -5127 .number_spaces -5128 .iter_mut() -5129 .filter(|(_, pns)| pns.pending_acks.can_send()) -5130 .map(|(&path_id, _)| path_id) -5131 .collect::<Vec<_>>() -5132 { -5133 Self::populate_acks( -5134 now, -5135 self.receiving_ecn, -5136 path_id, -5137 space_id, -5138 space, -5139 is_multipath_negotiated, -5140 builder, -5141 stats, -5142 ); -5143 } -5144 } -5145 -5146 // ACK_FREQUENCY -5147 if !path_exclusive_only && mem::replace(&mut space.pending.ack_frequency, false) { -5148 let sequence_number = self.ack_frequency.next_sequence_number(); -5149 -5150 // Safe to unwrap because this is always provided when ACK frequency is enabled -5151 let config = self.config.ack_frequency_config.as_ref().unwrap(); -5152 -5153 // Ensure the delay is within bounds to avoid a PROTOCOL_VIOLATION error -5154 let max_ack_delay = self.ack_frequency.candidate_max_ack_delay( -5155 path.rtt.get(), -5156 config, -5157 &self.peer_params, -5158 ); -5159 -5160 trace!(?max_ack_delay, "ACK_FREQUENCY"); -5161 -5162 let frame = frame::AckFrequency { -5163 sequence: sequence_number, -5164 ack_eliciting_threshold: config.ack_eliciting_threshold, -5165 request_max_ack_delay: max_ack_delay.as_micros().try_into().unwrap_or(VarInt::MAX), -5166 reordering_threshold: config.reordering_threshold, -5167 }; -5168 builder.encode(frame, stats); -5169 -5170 self.ack_frequency -5171 .ack_frequency_sent(path_id, pn, max_ack_delay); -5172 } -5173 -5174 // PATH_CHALLENGE -5175 if builder.frame_space_remaining() > frame::PathChallenge::SIZE_BOUND -5176 && space_id == SpaceId::Data -5177 && path.send_new_challenge -5178 && !self.state.is_closed() -5179 // we don't want to send new challenges if we are already closing -5180 { -5181 path.send_new_challenge = false; -5182 -5183 // Generate a new challenge every time we send a new PATH_CHALLENGE -5184 let token = self.rng.random(); -5185 let info = paths::SentChallengeInfo { -5186 sent_instant: now, -5187 network_path: path.network_path, -5188 }; -5189 path.challenges_sent.insert(token, info); -5190 let challenge = frame::PathChallenge(token); -5191 trace!(frame = %challenge); -5192 builder.encode(challenge, stats); -5193 builder.require_padding(); -5194 let pto = self.ack_frequency.max_ack_delay_for_pto() + path.rtt.pto_base(); -5195 self.timers.set( -5196 Timer::PerPath(path_id, PathTimer::PathChallengeLost), -5197 now + pto, -5198 self.qlog.with_time(now), -5199 ); +4852 if Some(number) == self.spaces[SpaceId::Data].for_path(path_id).rx_packet +4853 && !is_probing_packet +4854 && network_path != self.path_data(path_id).network_path +4855 { +4856 let ConnectionSide::Server { ref server_config } = self.side else { +4857 panic!("packets from unknown remote should be dropped by clients"); +4858 }; +4859 debug_assert!( +4860 server_config.migration, +4861 "migration-initiating packets should have been dropped immediately" +4862 ); +4863 self.migrate(path_id, now, network_path, migration_observed_addr); +4864 // Break linkability, if possible +4865 self.update_rem_cid(path_id); +4866 self.spin = false; +4867 } +4868 +4869 Ok(()) +4870 } +4871 +4872 fn migrate( +4873 &mut self, +4874 path_id: PathId, +4875 now: Instant, +4876 network_path: FourTuple, +4877 observed_addr: Option<ObservedAddr>, +4878 ) { +4879 trace!(%network_path, %path_id, "migration initiated"); +4880 self.path_counter = self.path_counter.wrapping_add(1); +4881 // TODO(@divma): conditions for path migration in multipath are very specific, check them +4882 // again to prevent path migrations that should actually create a new path +4883 +4884 // Reset rtt/congestion state for new path unless it looks like a NAT rebinding. +4885 // Note that the congestion window will not grow until validation terminates. Helps mitigate +4886 // amplification attacks performed by spoofing source addresses. +4887 let prev_pto = self.pto(SpaceId::Data, path_id); +4888 let known_path = self.paths.get_mut(&path_id).expect("known path"); +4889 let path = &mut known_path.data; +4890 let mut new_path = if network_path.remote.is_ipv4() +4891 && network_path.remote.ip() == path.network_path.remote.ip() +4892 { +4893 PathData::from_previous(network_path, path, self.path_counter, now) +4894 } else { +4895 let peer_max_udp_payload_size = +4896 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()) +4897 .unwrap_or(u16::MAX); +4898 PathData::new( +4899 network_path, +4900 self.allow_mtud, +4901 Some(peer_max_udp_payload_size), +4902 self.path_counter, +4903 now, +4904 &self.config, +4905 ) +4906 }; +4907 new_path.last_observed_addr_report = path.last_observed_addr_report.clone(); +4908 if let Some(report) = observed_addr { +4909 if let Some(updated) = new_path.update_observed_addr_report(report) { +4910 tracing::info!("adding observed addr event from migration"); +4911 self.events.push_back(Event::Path(PathEvent::ObservedAddr { +4912 id: path_id, +4913 addr: updated, +4914 })); +4915 } +4916 } +4917 new_path.send_new_challenge = true; +4918 +4919 let mut prev = mem::replace(path, new_path); +4920 // Don't clobber the original path if the previous one hasn't been validated yet +4921 if !prev.is_validating_path() { +4922 prev.send_new_challenge = true; +4923 // We haven't updated the remote CID yet, this captures the remote CID we were using on +4924 // the previous path. +4925 +4926 known_path.prev = Some((self.rem_cids.get(&path_id).unwrap().active(), prev)); +4927 } +4928 +4929 // We need to re-assign the correct remote to this path in qlog +4930 self.qlog.emit_tuple_assigned(path_id, network_path, now); +4931 +4932 self.timers.set( +4933 Timer::PerPath(path_id, PathTimer::PathValidation), +4934 now + 3 * cmp::max(self.pto(SpaceId::Data, path_id), prev_pto), +4935 self.qlog.with_time(now), +4936 ); +4937 } +4938 +4939 /// Handle a change in the local address, i.e. an active migration +4940 pub fn local_address_changed(&mut self) { +4941 // TODO(flub): if multipath is enabled this needs to create a new path entirely. +4942 self.update_rem_cid(PathId::ZERO); +4943 self.ping(); +4944 } +4945 +4946 /// Switch to a previously unused remote connection ID, if possible +4947 fn update_rem_cid(&mut self, path_id: PathId) { +4948 let Some((reset_token, retired)) = +4949 self.rem_cids.get_mut(&path_id).and_then(|cids| cids.next()) +4950 else { +4951 return; +4952 }; +4953 +4954 // Retire the current remote CID and any CIDs we had to skip. +4955 self.spaces[SpaceId::Data] +4956 .pending +4957 .retire_cids +4958 .extend(retired.map(|seq| (path_id, seq))); +4959 let remote = self.path_data(path_id).network_path.remote; +4960 self.set_reset_token(path_id, remote, reset_token); +4961 } +4962 +4963 /// Sends this reset token to the endpoint +4964 /// +4965 /// The endpoint needs to know the reset tokens issued by the peer, so that if the peer +4966 /// sends a reset token it knows to route it to this connection. See RFC 9000 section +4967 /// 10.3. Stateless Reset. +4968 /// +4969 /// Reset tokens are different for each path, the endpoint identifies paths by peer +4970 /// socket address however, not by path ID. +4971 fn set_reset_token(&mut self, path_id: PathId, remote: SocketAddr, reset_token: ResetToken) { +4972 self.endpoint_events +4973 .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token)); +4974 +4975 // During the handshake the server sends a reset token in the transport +4976 // parameters. When we are the client and we receive the reset token during the +4977 // handshake we want this to affect our peer transport parameters. +4978 // TODO(flub): Pretty sure this is pointless, the entire params is overwritten +4979 // shortly after this was called. And then the params don't have this anymore. +4980 if path_id == PathId::ZERO { +4981 self.peer_params.stateless_reset_token = Some(reset_token); +4982 } +4983 } +4984 +4985 /// Issue an initial set of connection IDs to the peer upon connection +4986 fn issue_first_cids(&mut self, now: Instant) { +4987 if self +4988 .local_cid_state +4989 .get(&PathId::ZERO) +4990 .expect("PathId::ZERO exists when the connection is created") +4991 .cid_len() +4992 == 0 +4993 { +4994 return; +4995 } +4996 +4997 // Subtract 1 to account for the CID we supplied while handshaking +4998 let mut n = self.peer_params.issue_cids_limit() - 1; +4999 if let ConnectionSide::Server { server_config } = &self.side { +5000 if server_config.has_preferred_address() { +5001 // We also sent a CID in the transport parameters +5002 n -= 1; +5003 } +5004 } +5005 self.endpoint_events +5006 .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n)); +5007 } +5008 +5009 /// Issues an initial set of CIDs for paths that have not yet had any CIDs issued +5010 /// +5011 /// Later CIDs are issued when CIDs expire or are retired by the peer. +5012 fn issue_first_path_cids(&mut self, now: Instant) { +5013 if let Some(max_path_id) = self.max_path_id() { +5014 let mut path_id = self.max_path_id_with_cids.next(); +5015 while path_id <= max_path_id { +5016 self.endpoint_events +5017 .push_back(EndpointEventInner::NeedIdentifiers( +5018 path_id, +5019 now, +5020 self.peer_params.issue_cids_limit(), +5021 )); +5022 path_id = path_id.next(); +5023 } +5024 self.max_path_id_with_cids = max_path_id; +5025 } +5026 } +5027 +5028 /// Populates a packet with frames +5029 /// +5030 /// This tries to fit as many frames as possible into the packet. +5031 /// +5032 /// *path_exclusive_only* means to only build frames which can only be sent on this +5033 /// *path. This is used in multipath for backup paths while there is still an active +5034 /// *path. +5035 fn populate_packet<'a, 'b>( +5036 &mut self, +5037 now: Instant, +5038 space_id: SpaceId, +5039 path_id: PathId, +5040 path_exclusive_only: bool, +5041 builder: &mut PacketBuilder, +5042 ) { +5043 let pn = builder.exact_number; +5044 let is_multipath_negotiated = self.is_multipath_negotiated(); +5045 let stats = &mut self.stats.frame_tx; +5046 let space = &mut self.spaces[space_id]; +5047 let path = &mut self.paths.get_mut(&path_id).expect("known path").data; +5048 let is_0rtt = space_id == SpaceId::Data && space.crypto.is_none(); +5049 space +5050 .for_path(path_id) +5051 .pending_acks +5052 .maybe_ack_non_eliciting(); +5053 +5054 // HANDSHAKE_DONE +5055 if !is_0rtt && mem::replace(&mut space.pending.handshake_done, false) { +5056 trace!("HANDSHAKE_DONE"); +5057 builder.encode(frame::HandshakeDone, stats); +5058 } +5059 +5060 // REACH_OUT +5061 // TODO(@divma): path explusive considerations +5062 if let Some((round, addresses)) = space.pending.reach_out.as_mut() { +5063 while let Some(local_addr) = addresses.pop() { +5064 let reach_out = frame::ReachOut::new(*round, local_addr); +5065 if builder.frame_space_remaining() > reach_out.size() { +5066 trace!(%round, ?local_addr, "REACH_OUT"); +5067 builder.encode(reach_out, stats); +5068 } else { +5069 addresses.push(local_addr); +5070 break; +5071 } +5072 } +5073 if addresses.is_empty() { +5074 space.pending.reach_out = None; +5075 } +5076 } +5077 +5078 // OBSERVED_ADDR +5079 if !path_exclusive_only +5080 && space_id == SpaceId::Data +5081 && self +5082 .config +5083 .address_discovery_role +5084 .should_report(&self.peer_params.address_discovery_role) +5085 && (!path.observed_addr_sent || space.pending.observed_addr) +5086 { +5087 let frame = +5088 frame::ObservedAddr::new(path.network_path.remote, self.next_observed_addr_seq_no); +5089 if builder.frame_space_remaining() > frame.size() { +5090 trace!(seq = %frame.seq_no, ip = %frame.ip, port = frame.port, "OBSERVED_ADDRESS"); +5091 builder.encode(frame, stats); +5092 +5093 self.next_observed_addr_seq_no = self.next_observed_addr_seq_no.saturating_add(1u8); +5094 path.observed_addr_sent = true; +5095 +5096 space.pending.observed_addr = false; +5097 } +5098 } +5099 +5100 // PING +5101 if mem::replace(&mut space.for_path(path_id).ping_pending, false) { +5102 trace!("PING"); +5103 builder.encode(frame::Ping, stats); +5104 } +5105 +5106 // IMMEDIATE_ACK +5107 if mem::replace(&mut space.for_path(path_id).immediate_ack_pending, false) { +5108 debug_assert_eq!( +5109 space_id, +5110 SpaceId::Data, +5111 "immediate acks must be sent in the data space" +5112 ); +5113 trace!("IMMEDIATE_ACK"); +5114 builder.encode(frame::ImmediateAck, stats); +5115 } +5116 +5117 // ACK +5118 // TODO(flub): Should this send acks for this path anyway? +5119 +5120 if !path_exclusive_only { +5121 for path_id in space +5122 .number_spaces +5123 .iter_mut() +5124 .filter(|(_, pns)| pns.pending_acks.can_send()) +5125 .map(|(&path_id, _)| path_id) +5126 .collect::<Vec<_>>() +5127 { +5128 Self::populate_acks( +5129 now, +5130 self.receiving_ecn, +5131 path_id, +5132 space_id, +5133 space, +5134 is_multipath_negotiated, +5135 builder, +5136 stats, +5137 ); +5138 } +5139 } +5140 +5141 // ACK_FREQUENCY +5142 if !path_exclusive_only && mem::replace(&mut space.pending.ack_frequency, false) { +5143 let sequence_number = self.ack_frequency.next_sequence_number(); +5144 +5145 // Safe to unwrap because this is always provided when ACK frequency is enabled +5146 let config = self.config.ack_frequency_config.as_ref().unwrap(); +5147 +5148 // Ensure the delay is within bounds to avoid a PROTOCOL_VIOLATION error +5149 let max_ack_delay = self.ack_frequency.candidate_max_ack_delay( +5150 path.rtt.get(), +5151 config, +5152 &self.peer_params, +5153 ); +5154 +5155 trace!(?max_ack_delay, "ACK_FREQUENCY"); +5156 +5157 let frame = frame::AckFrequency { +5158 sequence: sequence_number, +5159 ack_eliciting_threshold: config.ack_eliciting_threshold, +5160 request_max_ack_delay: max_ack_delay.as_micros().try_into().unwrap_or(VarInt::MAX), +5161 reordering_threshold: config.reordering_threshold, +5162 }; +5163 builder.encode(frame, stats); +5164 +5165 self.ack_frequency +5166 .ack_frequency_sent(path_id, pn, max_ack_delay); +5167 } +5168 +5169 // PATH_CHALLENGE +5170 if builder.frame_space_remaining() > frame::PathChallenge::SIZE_BOUND +5171 && space_id == SpaceId::Data +5172 && path.send_new_challenge +5173 && !self.state.is_closed() +5174 // we don't want to send new challenges if we are already closing +5175 { +5176 path.send_new_challenge = false; +5177 +5178 // Generate a new challenge every time we send a new PATH_CHALLENGE +5179 let token = self.rng.random(); +5180 let info = paths::SentChallengeInfo { +5181 sent_instant: now, +5182 network_path: path.network_path, +5183 }; +5184 path.challenges_sent.insert(token, info); +5185 let challenge = frame::PathChallenge(token); +5186 trace!(frame = %challenge); +5187 builder.encode(challenge, stats); +5188 builder.require_padding(); +5189 let pto = self.ack_frequency.max_ack_delay_for_pto() + path.rtt.pto_base(); +5190 self.timers.set( +5191 Timer::PerPath(path_id, PathTimer::PathChallengeLost), +5192 now + pto, +5193 self.qlog.with_time(now), +5194 ); +5195 +5196 if is_multipath_negotiated && !path.validated && path.send_new_challenge { +5197 // queue informing the path status along with the challenge +5198 space.pending.path_status.insert(path_id); +5199 } 5200 -5201 if is_multipath_negotiated && !path.validated && path.send_new_challenge { -5202 // queue informing the path status along with the challenge -5203 space.pending.path_status.insert(path_id); -5204 } -5205 -5206 // Always include an OBSERVED_ADDR frame with a PATH_CHALLENGE, regardless -5207 // of whether one has already been sent on this path. -5208 if space_id == SpaceId::Data -5209 && self -5210 .config -5211 .address_discovery_role -5212 .should_report(&self.peer_params.address_discovery_role) -5213 { -5214 let frame = frame::ObservedAddr::new( -5215 path.network_path.remote, -5216 self.next_observed_addr_seq_no, -5217 ); -5218 if builder.frame_space_remaining() > frame.size() { -5219 builder.encode(frame, stats); -5220 -5221 self.next_observed_addr_seq_no = -5222 self.next_observed_addr_seq_no.saturating_add(1u8); -5223 path.observed_addr_sent = true; +5201 // Always include an OBSERVED_ADDR frame with a PATH_CHALLENGE, regardless +5202 // of whether one has already been sent on this path. +5203 if space_id == SpaceId::Data +5204 && self +5205 .config +5206 .address_discovery_role +5207 .should_report(&self.peer_params.address_discovery_role) +5208 { +5209 let frame = frame::ObservedAddr::new( +5210 path.network_path.remote, +5211 self.next_observed_addr_seq_no, +5212 ); +5213 if builder.frame_space_remaining() > frame.size() { +5214 builder.encode(frame, stats); +5215 +5216 self.next_observed_addr_seq_no = +5217 self.next_observed_addr_seq_no.saturating_add(1u8); +5218 path.observed_addr_sent = true; +5219 +5220 space.pending.observed_addr = false; +5221 } +5222 } +5223 } 5224 -5225 space.pending.observed_addr = false; -5226 } -5227 } -5228 } -5229 -5230 // PATH_RESPONSE -5231 if builder.frame_space_remaining() > frame::PathResponse::SIZE_BOUND -5232 && space_id == SpaceId::Data -5233 { -5234 if let Some(token) = path.path_responses.pop_on_path(path.network_path) { -5235 let response = frame::PathResponse(token); -5236 trace!(frame = %response); -5237 builder.encode(response, stats); -5238 builder.require_padding(); -5239 -5240 // NOTE: this is technically not required but might be useful to ride the -5241 // request/response nature of path challenges to refresh an observation -5242 // Since PATH_RESPONSE is a probing frame, this is allowed by the spec. -5243 if space_id == SpaceId::Data -5244 && self -5245 .config -5246 .address_discovery_role -5247 .should_report(&self.peer_params.address_discovery_role) -5248 { -5249 let frame = frame::ObservedAddr::new( -5250 path.network_path.remote, -5251 self.next_observed_addr_seq_no, -5252 ); -5253 if builder.frame_space_remaining() > frame.size() { -5254 builder.encode(frame, stats); -5255 -5256 self.next_observed_addr_seq_no = -5257 self.next_observed_addr_seq_no.saturating_add(1u8); -5258 path.observed_addr_sent = true; -5259 -5260 space.pending.observed_addr = false; -5261 } -5262 } -5263 } -5264 } -5265 -5266 // CRYPTO -5267 while !path_exclusive_only -5268 && builder.frame_space_remaining() > frame::Crypto::SIZE_BOUND -5269 && !is_0rtt -5270 { -5271 let mut frame = match space.pending.crypto.pop_front() { -5272 Some(x) => x, -5273 None => break, -5274 }; -5275 -5276 // Calculate the maximum amount of crypto data we can store in the buffer. -5277 // Since the offset is known, we can reserve the exact size required to encode it. -5278 // For length we reserve 2bytes which allows to encode up to 2^14, -5279 // which is more than what fits into normally sized QUIC frames. -5280 let max_crypto_data_size = builder.frame_space_remaining() -5281 - 1 // Frame Type -5282 - VarInt::size(unsafe { VarInt::from_u64_unchecked(frame.offset) }) -5283 - 2; // Maximum encoded length for frame size, given we send less than 2^14 bytes -5284 -5285 let len = frame -5286 .data -5287 .len() -5288 .min(2usize.pow(14) - 1) -5289 .min(max_crypto_data_size); -5290 -5291 let data = frame.data.split_to(len); -5292 let truncated = frame::Crypto { -5293 offset: frame.offset, -5294 data, -5295 }; -5296 trace!( -5297 "CRYPTO: off {} len {}", -5298 truncated.offset, -5299 truncated.data.len() -5300 ); -5301 // TODO(@divma): revisit -5302 builder.encode(truncated.clone(), stats); -5303 -5304 if !frame.data.is_empty() { -5305 frame.offset += len as u64; -5306 space.pending.crypto.push_front(frame); -5307 } -5308 } -5309 -5310 // TODO(flub): maybe this is much higher priority? -5311 // PATH_ABANDON -5312 while !path_exclusive_only -5313 && space_id == SpaceId::Data -5314 && frame::PathAbandon::SIZE_BOUND <= builder.frame_space_remaining() -5315 { -5316 let Some((path_id, error_code)) = space.pending.path_abandon.pop_first() else { -5317 break; -5318 }; -5319 let frame = frame::PathAbandon { -5320 path_id, -5321 error_code, +5225 // PATH_RESPONSE +5226 if builder.frame_space_remaining() > frame::PathResponse::SIZE_BOUND +5227 && space_id == SpaceId::Data +5228 { +5229 if let Some(token) = path.path_responses.pop_on_path(path.network_path) { +5230 let response = frame::PathResponse(token); +5231 trace!(frame = %response); +5232 builder.encode(response, stats); +5233 builder.require_padding(); +5234 +5235 // NOTE: this is technically not required but might be useful to ride the +5236 // request/response nature of path challenges to refresh an observation +5237 // Since PATH_RESPONSE is a probing frame, this is allowed by the spec. +5238 if space_id == SpaceId::Data +5239 && self +5240 .config +5241 .address_discovery_role +5242 .should_report(&self.peer_params.address_discovery_role) +5243 { +5244 let frame = frame::ObservedAddr::new( +5245 path.network_path.remote, +5246 self.next_observed_addr_seq_no, +5247 ); +5248 if builder.frame_space_remaining() > frame.size() { +5249 builder.encode(frame, stats); +5250 +5251 self.next_observed_addr_seq_no = +5252 self.next_observed_addr_seq_no.saturating_add(1u8); +5253 path.observed_addr_sent = true; +5254 +5255 space.pending.observed_addr = false; +5256 } +5257 } +5258 } +5259 } +5260 +5261 // CRYPTO +5262 while !path_exclusive_only +5263 && builder.frame_space_remaining() > frame::Crypto::SIZE_BOUND +5264 && !is_0rtt +5265 { +5266 let mut frame = match space.pending.crypto.pop_front() { +5267 Some(x) => x, +5268 None => break, +5269 }; +5270 +5271 // Calculate the maximum amount of crypto data we can store in the buffer. +5272 // Since the offset is known, we can reserve the exact size required to encode it. +5273 // For length we reserve 2bytes which allows to encode up to 2^14, +5274 // which is more than what fits into normally sized QUIC frames. +5275 let max_crypto_data_size = builder.frame_space_remaining() +5276 - 1 // Frame Type +5277 - VarInt::size(unsafe { VarInt::from_u64_unchecked(frame.offset) }) +5278 - 2; // Maximum encoded length for frame size, given we send less than 2^14 bytes +5279 +5280 let len = frame +5281 .data +5282 .len() +5283 .min(2usize.pow(14) - 1) +5284 .min(max_crypto_data_size); +5285 +5286 let data = frame.data.split_to(len); +5287 let offset = frame.offset; +5288 let truncated = frame::Crypto { offset, data }; +5289 trace!(off = offset, len = truncated.data.len(), "CRYPTO"); +5290 builder.encode(truncated, stats); +5291 +5292 if !frame.data.is_empty() { +5293 frame.offset += len as u64; +5294 space.pending.crypto.push_front(frame); +5295 } +5296 } +5297 +5298 // TODO(flub): maybe this is much higher priority? +5299 // PATH_ABANDON +5300 while !path_exclusive_only +5301 && space_id == SpaceId::Data +5302 && frame::PathAbandon::SIZE_BOUND <= builder.frame_space_remaining() +5303 { +5304 let Some((path_id, error_code)) = space.pending.path_abandon.pop_first() else { +5305 break; +5306 }; +5307 let frame = frame::PathAbandon { +5308 path_id, +5309 error_code, +5310 }; +5311 builder.encode(frame, stats); +5312 trace!(%path_id, "PATH_ABANDON"); +5313 } +5314 +5315 // PATH_STATUS_AVAILABLE & PATH_STATUS_BACKUP +5316 while !path_exclusive_only +5317 && space_id == SpaceId::Data +5318 && frame::PathStatusAvailable::SIZE_BOUND <= builder.frame_space_remaining() +5319 { +5320 let Some(path_id) = space.pending.path_status.pop_first() else { +5321 break; 5322 }; -5323 builder.encode(frame, stats); -5324 trace!(%path_id, "PATH_ABANDON"); -5325 } -5326 -5327 // PATH_STATUS_AVAILABLE & PATH_STATUS_BACKUP -5328 while !path_exclusive_only -5329 && space_id == SpaceId::Data -5330 && frame::PathStatusAvailable::SIZE_BOUND <= builder.frame_space_remaining() -5331 { -5332 let Some(path_id) = space.pending.path_status.pop_first() else { -5333 break; -5334 }; -5335 let Some(path) = self.paths.get(&path_id).map(|path_state| &path_state.data) else { -5336 trace!(%path_id, "discarding queued path status for unknown path"); -5337 continue; -5338 }; -5339 -5340 let seq = path.status.seq(); -5341 match path.local_status() { -5342 PathStatus::Available => { -5343 let frame = frame::PathStatusAvailable { -5344 path_id, -5345 status_seq_no: seq, -5346 }; -5347 builder.encode(frame, stats); -5348 trace!(%path_id, %seq, "PATH_STATUS_AVAILABLE") -5349 } -5350 PathStatus::Backup => { -5351 let frame = frame::PathStatusBackup { -5352 path_id, -5353 status_seq_no: seq, -5354 }; -5355 builder.encode(frame, stats); -5356 trace!(%path_id, %seq, "PATH_STATUS_BACKUP") -5357 } -5358 } -5359 } -5360 -5361 // MAX_PATH_ID -5362 if space_id == SpaceId::Data -5363 && space.pending.max_path_id -5364 && frame::MaxPathId::SIZE_BOUND <= builder.frame_space_remaining() -5365 { -5366 let frame = frame::MaxPathId(self.local_max_path_id); -5367 builder.encode(frame, stats); -5368 space.pending.max_path_id = false; -5369 trace!(val = %self.local_max_path_id, "MAX_PATH_ID"); -5370 } -5371 -5372 // PATHS_BLOCKED -5373 if space_id == SpaceId::Data -5374 && space.pending.paths_blocked -5375 && frame::PathsBlocked::SIZE_BOUND <= builder.frame_space_remaining() -5376 { -5377 let frame = frame::PathsBlocked(self.remote_max_path_id); -5378 builder.encode(frame, stats); -5379 space.pending.paths_blocked = false; -5380 trace!(max_path_id = %self.remote_max_path_id, "PATHS_BLOCKED"); -5381 } -5382 -5383 // PATH_CIDS_BLOCKED -5384 while space_id == SpaceId::Data -5385 && frame::PathCidsBlocked::SIZE_BOUND <= builder.frame_space_remaining() -5386 { -5387 let Some(path_id) = space.pending.path_cids_blocked.pop() else { -5388 break; -5389 }; -5390 let next_seq = match self.rem_cids.get(&path_id) { -5391 Some(cid_queue) => cid_queue.active_seq() + 1, -5392 None => 0, -5393 }; -5394 let frame = frame::PathCidsBlocked { -5395 path_id, -5396 next_seq: VarInt(next_seq), -5397 }; -5398 builder.encode(frame, stats); -5399 trace!(%path_id, next_seq, "PATH_CIDS_BLOCKED"); -5400 } -5401 -5402 // RESET_STREAM, STOP_SENDING, MAX_DATA, MAX_STREAM_DATA, MAX_STREAMS -5403 if space_id == SpaceId::Data { -5404 self.streams -5405 .write_control_frames(builder, &mut space.pending, stats); -5406 } -5407 -5408 // NEW_CONNECTION_ID -5409 let cid_len = self -5410 .local_cid_state -5411 .values() -5412 .map(|cid_state| cid_state.cid_len()) -5413 .max() -5414 .expect("some local CID state must exist"); -5415 let new_cid_size_bound = -5416 frame::NewConnectionId::size_bound(is_multipath_negotiated, cid_len); -5417 while !path_exclusive_only && builder.frame_space_remaining() > new_cid_size_bound { -5418 let issued = match space.pending.new_cids.pop() { -5419 Some(x) => x, -5420 None => break, -5421 }; -5422 let retire_prior_to = self -5423 .local_cid_state -5424 .get(&issued.path_id) -5425 .map(|cid_state| cid_state.retire_prior_to()) -5426 .unwrap_or_else(|| panic!("missing local CID state for path={}", issued.path_id)); -5427 -5428 let cid_path_id = match is_multipath_negotiated { -5429 true => { -5430 trace!( -5431 path_id = ?issued.path_id, -5432 sequence = issued.sequence, -5433 id = %issued.id, -5434 "PATH_NEW_CONNECTION_ID", -5435 ); -5436 Some(issued.path_id) -5437 } -5438 false => { -5439 trace!( -5440 sequence = issued.sequence, -5441 id = %issued.id, -5442 "NEW_CONNECTION_ID" -5443 ); -5444 debug_assert_eq!(issued.path_id, PathId::ZERO); -5445 None -5446 } -5447 }; -5448 let frame = frame::NewConnectionId { -5449 path_id: cid_path_id, -5450 sequence: issued.sequence, -5451 retire_prior_to, -5452 id: issued.id, -5453 reset_token: issued.reset_token, -5454 }; -5455 builder.encode(frame, stats); -5456 } -5457 -5458 // RETIRE_CONNECTION_ID -5459 let retire_cid_bound = frame::RetireConnectionId::size_bound(is_multipath_negotiated); -5460 while !path_exclusive_only && builder.frame_space_remaining() > retire_cid_bound { -5461 let (path_id, sequence) = match space.pending.retire_cids.pop() { -5462 Some((PathId::ZERO, seq)) if !is_multipath_negotiated => { -5463 trace!(sequence = seq, "RETIRE_CONNECTION_ID"); -5464 (None, seq) -5465 } -5466 Some((path_id, seq)) => { -5467 trace!(%path_id, sequence = seq, "PATH_RETIRE_CONNECTION_ID"); -5468 (Some(path_id), seq) -5469 } -5470 None => break, -5471 }; -5472 let frame = frame::RetireConnectionId { path_id, sequence }; -5473 builder.encode(frame, stats); -5474 } -5475 -5476 // DATAGRAM -5477 let mut sent_datagrams = false; -5478 while !path_exclusive_only -5479 && builder.frame_space_remaining() > Datagram::SIZE_BOUND -5480 && space_id == SpaceId::Data -5481 { -5482 match self.datagrams.write(builder, stats) { -5483 true => { -5484 sent_datagrams = true; -5485 } -5486 false => break, -5487 } -5488 } -5489 if self.datagrams.send_blocked && sent_datagrams { -5490 self.events.push_back(Event::DatagramsUnblocked); -5491 self.datagrams.send_blocked = false; -5492 } +5323 let Some(path) = self.paths.get(&path_id).map(|path_state| &path_state.data) else { +5324 trace!(%path_id, "discarding queued path status for unknown path"); +5325 continue; +5326 }; +5327 +5328 let seq = path.status.seq(); +5329 match path.local_status() { +5330 PathStatus::Available => { +5331 let frame = frame::PathStatusAvailable { +5332 path_id, +5333 status_seq_no: seq, +5334 }; +5335 builder.encode(frame, stats); +5336 trace!(%path_id, %seq, "PATH_STATUS_AVAILABLE") +5337 } +5338 PathStatus::Backup => { +5339 let frame = frame::PathStatusBackup { +5340 path_id, +5341 status_seq_no: seq, +5342 }; +5343 builder.encode(frame, stats); +5344 trace!(%path_id, %seq, "PATH_STATUS_BACKUP") +5345 } +5346 } +5347 } +5348 +5349 // MAX_PATH_ID +5350 if space_id == SpaceId::Data +5351 && space.pending.max_path_id +5352 && frame::MaxPathId::SIZE_BOUND <= builder.frame_space_remaining() +5353 { +5354 let frame = frame::MaxPathId(self.local_max_path_id); +5355 builder.encode(frame, stats); +5356 space.pending.max_path_id = false; +5357 trace!(val = %self.local_max_path_id, "MAX_PATH_ID"); +5358 } +5359 +5360 // PATHS_BLOCKED +5361 if space_id == SpaceId::Data +5362 && space.pending.paths_blocked +5363 && frame::PathsBlocked::SIZE_BOUND <= builder.frame_space_remaining() +5364 { +5365 let frame = frame::PathsBlocked(self.remote_max_path_id); +5366 builder.encode(frame, stats); +5367 space.pending.paths_blocked = false; +5368 trace!(max_path_id = %self.remote_max_path_id, "PATHS_BLOCKED"); +5369 } +5370 +5371 // PATH_CIDS_BLOCKED +5372 while space_id == SpaceId::Data +5373 && frame::PathCidsBlocked::SIZE_BOUND <= builder.frame_space_remaining() +5374 { +5375 let Some(path_id) = space.pending.path_cids_blocked.pop() else { +5376 break; +5377 }; +5378 let next_seq = match self.rem_cids.get(&path_id) { +5379 Some(cid_queue) => cid_queue.active_seq() + 1, +5380 None => 0, +5381 }; +5382 let frame = frame::PathCidsBlocked { +5383 path_id, +5384 next_seq: VarInt(next_seq), +5385 }; +5386 builder.encode(frame, stats); +5387 trace!(%path_id, next_seq, "PATH_CIDS_BLOCKED"); +5388 } +5389 +5390 // RESET_STREAM, STOP_SENDING, MAX_DATA, MAX_STREAM_DATA, MAX_STREAMS +5391 if space_id == SpaceId::Data { +5392 self.streams +5393 .write_control_frames(builder, &mut space.pending, stats); +5394 } +5395 +5396 // NEW_CONNECTION_ID +5397 let cid_len = self +5398 .local_cid_state +5399 .values() +5400 .map(|cid_state| cid_state.cid_len()) +5401 .max() +5402 .expect("some local CID state must exist"); +5403 let new_cid_size_bound = +5404 frame::NewConnectionId::size_bound(is_multipath_negotiated, cid_len); +5405 while !path_exclusive_only && builder.frame_space_remaining() > new_cid_size_bound { +5406 let issued = match space.pending.new_cids.pop() { +5407 Some(x) => x, +5408 None => break, +5409 }; +5410 let retire_prior_to = self +5411 .local_cid_state +5412 .get(&issued.path_id) +5413 .map(|cid_state| cid_state.retire_prior_to()) +5414 .unwrap_or_else(|| panic!("missing local CID state for path={}", issued.path_id)); +5415 +5416 let cid_path_id = match is_multipath_negotiated { +5417 true => { +5418 trace!( +5419 path_id = ?issued.path_id, +5420 sequence = issued.sequence, +5421 id = %issued.id, +5422 "PATH_NEW_CONNECTION_ID", +5423 ); +5424 Some(issued.path_id) +5425 } +5426 false => { +5427 trace!( +5428 sequence = issued.sequence, +5429 id = %issued.id, +5430 "NEW_CONNECTION_ID" +5431 ); +5432 debug_assert_eq!(issued.path_id, PathId::ZERO); +5433 None +5434 } +5435 }; +5436 let frame = frame::NewConnectionId { +5437 path_id: cid_path_id, +5438 sequence: issued.sequence, +5439 retire_prior_to, +5440 id: issued.id, +5441 reset_token: issued.reset_token, +5442 }; +5443 builder.encode(frame, stats); +5444 } +5445 +5446 // RETIRE_CONNECTION_ID +5447 let retire_cid_bound = frame::RetireConnectionId::size_bound(is_multipath_negotiated); +5448 while !path_exclusive_only && builder.frame_space_remaining() > retire_cid_bound { +5449 let (path_id, sequence) = match space.pending.retire_cids.pop() { +5450 Some((PathId::ZERO, seq)) if !is_multipath_negotiated => { +5451 trace!(sequence = seq, "RETIRE_CONNECTION_ID"); +5452 (None, seq) +5453 } +5454 Some((path_id, seq)) => { +5455 trace!(%path_id, sequence = seq, "PATH_RETIRE_CONNECTION_ID"); +5456 (Some(path_id), seq) +5457 } +5458 None => break, +5459 }; +5460 let frame = frame::RetireConnectionId { path_id, sequence }; +5461 builder.encode(frame, stats); +5462 } +5463 +5464 // DATAGRAM +5465 let mut sent_datagrams = false; +5466 while !path_exclusive_only +5467 && builder.frame_space_remaining() > Datagram::SIZE_BOUND +5468 && space_id == SpaceId::Data +5469 { +5470 match self.datagrams.write(builder, stats) { +5471 true => { +5472 sent_datagrams = true; +5473 } +5474 false => break, +5475 } +5476 } +5477 if self.datagrams.send_blocked && sent_datagrams { +5478 self.events.push_back(Event::DatagramsUnblocked); +5479 self.datagrams.send_blocked = false; +5480 } +5481 +5482 let path = &mut self.paths.get_mut(&path_id).expect("known path").data; +5483 +5484 // NEW_TOKEN +5485 while let Some(network_path) = space.pending.new_tokens.pop() { +5486 if path_exclusive_only { +5487 break; +5488 } +5489 debug_assert_eq!(space_id, SpaceId::Data); +5490 let ConnectionSide::Server { server_config } = &self.side else { +5491 panic!("NEW_TOKEN frames should not be enqueued by clients"); +5492 }; 5493 -5494 let path = &mut self.paths.get_mut(&path_id).expect("known path").data; -5495 -5496 // NEW_TOKEN -5497 while let Some(network_path) = space.pending.new_tokens.pop() { -5498 if path_exclusive_only { -5499 break; +5494 if !network_path.is_probably_same_path(&path.network_path) { +5495 // NEW_TOKEN frames contain tokens bound to a client's IP address, and are only +5496 // useful if used from the same IP address. Thus, we abandon enqueued NEW_TOKEN +5497 // frames upon an path change. Instead, when the new path becomes validated, +5498 // NEW_TOKEN frames may be enqueued for the new path instead. +5499 continue; 5500 } -5501 debug_assert_eq!(space_id, SpaceId::Data); -5502 let ConnectionSide::Server { server_config } = &self.side else { -5503 panic!("NEW_TOKEN frames should not be enqueued by clients"); -5504 }; -5505 -5506 if !network_path.is_probably_same_path(&path.network_path) { -5507 // NEW_TOKEN frames contain tokens bound to a client's IP address, and are only -5508 // useful if used from the same IP address. Thus, we abandon enqueued NEW_TOKEN -5509 // frames upon an path change. Instead, when the new path becomes validated, -5510 // NEW_TOKEN frames may be enqueued for the new path instead. -5511 continue; -5512 } -5513 -5514 let token = Token::new( -5515 TokenPayload::Validation { -5516 ip: network_path.remote.ip(), -5517 issued: server_config.time_source.now(), -5518 }, -5519 &mut self.rng, -5520 ); -5521 let new_token = NewToken { -5522 token: token.encode(&*server_config.token_key).into(), -5523 }; -5524 -5525 if builder.frame_space_remaining() < new_token.size() { -5526 space.pending.new_tokens.push(network_path); -5527 break; -5528 } -5529 -5530 trace!("NEW_TOKEN"); -5531 builder.encode(new_token, stats); -5532 builder.retransmits_mut().new_tokens.push(network_path); -5533 } -5534 -5535 // STREAM -5536 if !path_exclusive_only && space_id == SpaceId::Data { -5537 self.streams -5538 .write_stream_frames(builder, self.config.send_fairness, stats); -5539 } -5540 -5541 // ADD_ADDRESS -5542 // TODO(@divma): check if we need to do path exclusive filters -5543 while space_id == SpaceId::Data -5544 && frame::AddAddress::SIZE_BOUND <= builder.frame_space_remaining() -5545 { -5546 if let Some(added_address) = space.pending.add_address.pop_last() { -5547 trace!( -5548 seq = %added_address.seq_no, -5549 ip = ?added_address.ip, -5550 port = added_address.port, -5551 "ADD_ADDRESS", -5552 ); -5553 builder.encode(added_address, stats); +5501 +5502 let token = Token::new( +5503 TokenPayload::Validation { +5504 ip: network_path.remote.ip(), +5505 issued: server_config.time_source.now(), +5506 }, +5507 &mut self.rng, +5508 ); +5509 let new_token = NewToken { +5510 token: token.encode(&*server_config.token_key).into(), +5511 }; +5512 +5513 if builder.frame_space_remaining() < new_token.size() { +5514 space.pending.new_tokens.push(network_path); +5515 break; +5516 } +5517 +5518 trace!("NEW_TOKEN"); +5519 builder.encode(new_token, stats); +5520 builder.retransmits_mut().new_tokens.push(network_path); +5521 } +5522 +5523 // STREAM +5524 if !path_exclusive_only && space_id == SpaceId::Data { +5525 self.streams +5526 .write_stream_frames(builder, self.config.send_fairness, stats); +5527 } +5528 +5529 // ADD_ADDRESS +5530 // TODO(@divma): check if we need to do path exclusive filters +5531 while space_id == SpaceId::Data +5532 && frame::AddAddress::SIZE_BOUND <= builder.frame_space_remaining() +5533 { +5534 if let Some(added_address) = space.pending.add_address.pop_last() { +5535 trace!( +5536 seq = %added_address.seq_no, +5537 ip = ?added_address.ip, +5538 port = added_address.port, +5539 "ADD_ADDRESS", +5540 ); +5541 builder.encode(added_address, stats); +5542 } else { +5543 break; +5544 } +5545 } +5546 +5547 // REMOVE_ADDRESS +5548 while space_id == SpaceId::Data +5549 && frame::RemoveAddress::SIZE_BOUND <= builder.frame_space_remaining() +5550 { +5551 if let Some(removed_address) = space.pending.remove_address.pop_last() { +5552 trace!(seq = %removed_address.seq_no, "REMOVE_ADDRESS"); +5553 builder.encode(removed_address, stats); 5554 } else { 5555 break; 5556 } 5557 } -5558 -5559 // REMOVE_ADDRESS -5560 while space_id == SpaceId::Data -5561 && frame::RemoveAddress::SIZE_BOUND <= builder.frame_space_remaining() -5562 { -5563 if let Some(removed_address) = space.pending.remove_address.pop_last() { -5564 trace!(seq = %removed_address.seq_no, "REMOVE_ADDRESS"); -5565 builder.encode(removed_address, stats); -5566 } else { -5567 break; -5568 } -5569 } -5570 } -5571 -5572 /// Write pending ACKs into a buffer -5573 fn populate_acks<'a, 'b>( -5574 now: Instant, -5575 receiving_ecn: bool, -5576 path_id: PathId, -5577 space_id: SpaceId, -5578 space: &mut PacketSpace, -5579 is_multipath_negotiated: bool, -5580 builder: &mut PacketBuilder<'a, 'b>, -5581 stats: &mut FrameStats, -5582 ) { -5583 // 0-RTT packets must never carry acks (which would have to be of handshake packets) -5584 debug_assert!(space.crypto.is_some(), "tried to send ACK in 0-RTT"); -5585 -5586 debug_assert!( -5587 is_multipath_negotiated || path_id == PathId::ZERO, -5588 "Only PathId::ZERO allowed without multipath (have {path_id:?})" -5589 ); -5590 if is_multipath_negotiated { -5591 debug_assert!( -5592 space_id == SpaceId::Data || path_id == PathId::ZERO, -5593 "path acks must be sent in 1RTT space (have {space_id:?})" -5594 ); -5595 } -5596 -5597 let pns = space.for_path(path_id); -5598 let ranges = pns.pending_acks.ranges(); -5599 debug_assert!(!ranges.is_empty(), "can not send empty ACK range"); -5600 let ecn = if receiving_ecn { -5601 Some(&pns.ecn_counters) -5602 } else { -5603 None -5604 }; -5605 -5606 let delay_micros = pns.pending_acks.ack_delay(now).as_micros() as u64; -5607 // TODO: This should come from `TransportConfig` if that gets configurable. -5608 let ack_delay_exp = TransportParameters::default().ack_delay_exponent; -5609 let delay = delay_micros >> ack_delay_exp.into_inner(); +5558 } +5559 +5560 /// Write pending ACKs into a buffer +5561 fn populate_acks<'a, 'b>( +5562 now: Instant, +5563 receiving_ecn: bool, +5564 path_id: PathId, +5565 space_id: SpaceId, +5566 space: &mut PacketSpace, +5567 is_multipath_negotiated: bool, +5568 builder: &mut PacketBuilder<'a, 'b>, +5569 stats: &mut FrameStats, +5570 ) { +5571 // 0-RTT packets must never carry acks (which would have to be of handshake packets) +5572 debug_assert!(space.crypto.is_some(), "tried to send ACK in 0-RTT"); +5573 +5574 debug_assert!( +5575 is_multipath_negotiated || path_id == PathId::ZERO, +5576 "Only PathId::ZERO allowed without multipath (have {path_id:?})" +5577 ); +5578 if is_multipath_negotiated { +5579 debug_assert!( +5580 space_id == SpaceId::Data || path_id == PathId::ZERO, +5581 "path acks must be sent in 1RTT space (have {space_id:?})" +5582 ); +5583 } +5584 +5585 let pns = space.for_path(path_id); +5586 let ranges = pns.pending_acks.ranges(); +5587 debug_assert!(!ranges.is_empty(), "can not send empty ACK range"); +5588 let ecn = if receiving_ecn { +5589 Some(&pns.ecn_counters) +5590 } else { +5591 None +5592 }; +5593 +5594 let delay_micros = pns.pending_acks.ack_delay(now).as_micros() as u64; +5595 // TODO: This should come from `TransportConfig` if that gets configurable. +5596 let ack_delay_exp = TransportParameters::default().ack_delay_exponent; +5597 let delay = delay_micros >> ack_delay_exp.into_inner(); +5598 +5599 if is_multipath_negotiated && space_id == SpaceId::Data { +5600 if !ranges.is_empty() { +5601 trace!("PATH_ACK {path_id:?} {ranges:?}, Delay = {delay_micros}us"); +5602 let frame = frame::PathAck::encoder(path_id, delay, ranges, ecn); +5603 builder.encode(frame, stats); +5604 } +5605 } else { +5606 trace!("ACK {ranges:?}, Delay = {delay_micros}us"); +5607 builder.encode(frame::Ack::encoder(delay, ranges, ecn), stats); +5608 } +5609 } 5610 -5611 if is_multipath_negotiated && space_id == SpaceId::Data { -5612 if !ranges.is_empty() { -5613 trace!("PATH_ACK {path_id:?} {ranges:?}, Delay = {delay_micros}us"); -5614 builder.encode( -5615 frame::PathAck::encoder(path_id, delay as _, ranges, ecn), -5616 stats, -5617 ); -5618 } -5619 } else { -5620 trace!("ACK {ranges:?}, Delay = {delay_micros}us"); -5621 builder.encode(frame::Ack::encoder(delay as _, ranges, ecn), stats); -5622 } -5623 } -5624 -5625 fn close_common(&mut self) { -5626 trace!("connection closed"); -5627 self.timers.reset(); -5628 } -5629 -5630 fn set_close_timer(&mut self, now: Instant) { -5631 // QUIC-MULTIPATH § 2.6 Connection Closure: draining for 3*PTO with PTO the max of -5632 // the PTO for all paths. -5633 let pto_max = self.pto_max_path(self.highest_space, true); -5634 self.timers.set( -5635 Timer::Conn(ConnTimer::Close), -5636 now + 3 * pto_max, -5637 self.qlog.with_time(now), -5638 ); -5639 } -5640 -5641 /// Handle transport parameters received from the peer -5642 /// -5643 /// *rem_cid* and *loc_cid* are the source and destination CIDs respectively of the -5644 /// *packet into which the transport parameters arrived. -5645 fn handle_peer_params( -5646 &mut self, -5647 params: TransportParameters, -5648 loc_cid: ConnectionId, -5649 rem_cid: ConnectionId, -5650 now: Instant, -5651 ) -> Result<(), TransportError> { -5652 if Some(self.orig_rem_cid) != params.initial_src_cid -5653 || (self.side.is_client() -5654 && (Some(self.initial_dst_cid) != params.original_dst_cid -5655 || self.retry_src_cid != params.retry_src_cid)) -5656 { -5657 return Err(TransportError::TRANSPORT_PARAMETER_ERROR( -5658 "CID authentication failure", -5659 )); -5660 } -5661 if params.initial_max_path_id.is_some() && (loc_cid.is_empty() || rem_cid.is_empty()) { -5662 return Err(TransportError::PROTOCOL_VIOLATION( -5663 "multipath must not use zero-length CIDs", -5664 )); -5665 } -5666 -5667 self.set_peer_params(params); -5668 self.qlog.emit_peer_transport_params_received(self, now); -5669 -5670 Ok(()) -5671 } -5672 -5673 fn set_peer_params(&mut self, params: TransportParameters) { -5674 self.streams.set_params(&params); -5675 self.idle_timeout = -5676 negotiate_max_idle_timeout(self.config.max_idle_timeout, Some(params.max_idle_timeout)); -5677 trace!("negotiated max idle timeout {:?}", self.idle_timeout); -5678 -5679 if let Some(ref info) = params.preferred_address { -5680 // During the handshake PathId::ZERO exists. -5681 self.rem_cids.get_mut(&PathId::ZERO).expect("not yet abandoned").insert(frame::NewConnectionId { -5682 path_id: None, -5683 sequence: 1, -5684 id: info.connection_id, -5685 reset_token: info.stateless_reset_token, -5686 retire_prior_to: 0, -5687 }) -5688 .expect( -5689 "preferred address CID is the first received, and hence is guaranteed to be legal", -5690 ); -5691 let remote = self.path_data(PathId::ZERO).network_path.remote; -5692 self.set_reset_token(PathId::ZERO, remote, info.stateless_reset_token); +5611 fn close_common(&mut self) { +5612 trace!("connection closed"); +5613 self.timers.reset(); +5614 } +5615 +5616 fn set_close_timer(&mut self, now: Instant) { +5617 // QUIC-MULTIPATH § 2.6 Connection Closure: draining for 3*PTO with PTO the max of +5618 // the PTO for all paths. +5619 let pto_max = self.pto_max_path(self.highest_space, true); +5620 self.timers.set( +5621 Timer::Conn(ConnTimer::Close), +5622 now + 3 * pto_max, +5623 self.qlog.with_time(now), +5624 ); +5625 } +5626 +5627 /// Handle transport parameters received from the peer +5628 /// +5629 /// *rem_cid* and *loc_cid* are the source and destination CIDs respectively of the +5630 /// *packet into which the transport parameters arrived. +5631 fn handle_peer_params( +5632 &mut self, +5633 params: TransportParameters, +5634 loc_cid: ConnectionId, +5635 rem_cid: ConnectionId, +5636 now: Instant, +5637 ) -> Result<(), TransportError> { +5638 if Some(self.orig_rem_cid) != params.initial_src_cid +5639 || (self.side.is_client() +5640 && (Some(self.initial_dst_cid) != params.original_dst_cid +5641 || self.retry_src_cid != params.retry_src_cid)) +5642 { +5643 return Err(TransportError::TRANSPORT_PARAMETER_ERROR( +5644 "CID authentication failure", +5645 )); +5646 } +5647 if params.initial_max_path_id.is_some() && (loc_cid.is_empty() || rem_cid.is_empty()) { +5648 return Err(TransportError::PROTOCOL_VIOLATION( +5649 "multipath must not use zero-length CIDs", +5650 )); +5651 } +5652 +5653 self.set_peer_params(params); +5654 self.qlog.emit_peer_transport_params_received(self, now); +5655 +5656 Ok(()) +5657 } +5658 +5659 fn set_peer_params(&mut self, params: TransportParameters) { +5660 self.streams.set_params(&params); +5661 self.idle_timeout = +5662 negotiate_max_idle_timeout(self.config.max_idle_timeout, Some(params.max_idle_timeout)); +5663 trace!("negotiated max idle timeout {:?}", self.idle_timeout); +5664 +5665 if let Some(ref info) = params.preferred_address { +5666 // During the handshake PathId::ZERO exists. +5667 self.rem_cids.get_mut(&PathId::ZERO).expect("not yet abandoned").insert(frame::NewConnectionId { +5668 path_id: None, +5669 sequence: 1, +5670 id: info.connection_id, +5671 reset_token: info.stateless_reset_token, +5672 retire_prior_to: 0, +5673 }) +5674 .expect( +5675 "preferred address CID is the first received, and hence is guaranteed to be legal", +5676 ); +5677 let remote = self.path_data(PathId::ZERO).network_path.remote; +5678 self.set_reset_token(PathId::ZERO, remote, info.stateless_reset_token); +5679 } +5680 self.ack_frequency.peer_max_ack_delay = get_max_ack_delay(&params); +5681 +5682 let mut multipath_enabled = None; +5683 if let (Some(local_max_path_id), Some(remote_max_path_id)) = ( +5684 self.config.get_initial_max_path_id(), +5685 params.initial_max_path_id, +5686 ) { +5687 // multipath is enabled, register the local and remote maximums +5688 self.local_max_path_id = local_max_path_id; +5689 self.remote_max_path_id = remote_max_path_id; +5690 let initial_max_path_id = local_max_path_id.min(remote_max_path_id); +5691 debug!(%initial_max_path_id, "multipath negotiated"); +5692 multipath_enabled = Some(initial_max_path_id); 5693 } -5694 self.ack_frequency.peer_max_ack_delay = get_max_ack_delay(&params); -5695 -5696 let mut multipath_enabled = None; -5697 if let (Some(local_max_path_id), Some(remote_max_path_id)) = ( -5698 self.config.get_initial_max_path_id(), -5699 params.initial_max_path_id, -5700 ) { -5701 // multipath is enabled, register the local and remote maximums -5702 self.local_max_path_id = local_max_path_id; -5703 self.remote_max_path_id = remote_max_path_id; -5704 let initial_max_path_id = local_max_path_id.min(remote_max_path_id); -5705 debug!(%initial_max_path_id, "multipath negotiated"); -5706 multipath_enabled = Some(initial_max_path_id); -5707 } -5708 -5709 if let Some((max_locally_allowed_remote_addresses, max_remotely_allowed_remote_addresses)) = -5710 self.config -5711 .max_remote_nat_traversal_addresses -5712 .zip(params.max_remote_nat_traversal_addresses) -5713 { -5714 if let Some(max_initial_paths) = -5715 multipath_enabled.map(|path_id| path_id.saturating_add(1u8)) -5716 { -5717 let max_local_addresses = max_remotely_allowed_remote_addresses.get(); -5718 let max_remote_addresses = max_locally_allowed_remote_addresses.get(); -5719 self.iroh_hp = -5720 iroh_hp::State::new(max_remote_addresses, max_local_addresses, self.side()); -5721 debug!( -5722 %max_remote_addresses, %max_local_addresses, -5723 "iroh hole punching negotiated" -5724 ); -5725 -5726 match self.side() { -5727 Side::Client => { -5728 if max_initial_paths.as_u32() < max_remote_addresses as u32 + 1 { -5729 // in this case the client might try to open `max_remote_addresses` new -5730 // paths, but the current multipath configuration will not allow it -5731 warn!(%max_initial_paths, %max_remote_addresses, "local client configuration might cause nat traversal issues") -5732 } else if max_local_addresses as u64 -5733 > params.active_connection_id_limit.into_inner() -5734 { -5735 // the server allows us to send at most `params.active_connection_id_limit` -5736 // but they might need at least `max_local_addresses` to effectively send -5737 // `PATH_CHALLENGE` frames to each advertised local address -5738 warn!(%max_local_addresses, remote_cid_limit=%params.active_connection_id_limit.into_inner(), "remote server configuration might cause nat traversal issues") -5739 } -5740 } -5741 Side::Server => { -5742 if (max_initial_paths.as_u32() as u64) < crate::LOC_CID_COUNT { -5743 warn!(%max_initial_paths, local_cid_limit=%crate::LOC_CID_COUNT, "local server configuration might cause nat traversal issues") -5744 } -5745 } -5746 } -5747 } else { -5748 debug!("iroh nat traversal enabled for both endpoints, but multipath is missing") -5749 } -5750 } -5751 -5752 self.peer_params = params; -5753 let peer_max_udp_payload_size = -5754 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX); -5755 self.path_data_mut(PathId::ZERO) -5756 .mtud -5757 .on_peer_max_udp_payload_size_received(peer_max_udp_payload_size); -5758 } -5759 -5760 /// Decrypts a packet, returning the packet number on success -5761 fn decrypt_packet( -5762 &mut self, -5763 now: Instant, -5764 path_id: PathId, -5765 packet: &mut Packet, -5766 ) -> Result<Option<u64>, Option<TransportError>> { -5767 let result = packet_crypto::decrypt_packet_body( -5768 packet, -5769 path_id, -5770 &self.spaces, -5771 self.zero_rtt_crypto.as_ref(), -5772 self.key_phase, -5773 self.prev_crypto.as_ref(), -5774 self.next_crypto.as_ref(), -5775 )?; -5776 -5777 let result = match result { -5778 Some(r) => r, -5779 None => return Ok(None), -5780 }; -5781 -5782 if result.outgoing_key_update_acked { -5783 if let Some(prev) = self.prev_crypto.as_mut() { -5784 prev.end_packet = Some((result.number, now)); -5785 self.set_key_discard_timer(now, packet.header.space()); -5786 } -5787 } -5788 -5789 if result.incoming_key_update { -5790 trace!("key update authenticated"); -5791 self.update_keys(Some((result.number, now)), true); -5792 self.set_key_discard_timer(now, packet.header.space()); -5793 } -5794 -5795 Ok(Some(result.number)) -5796 } -5797 -5798 fn update_keys(&mut self, end_packet: Option<(u64, Instant)>, remote: bool) { -5799 trace!("executing key update"); -5800 // Generate keys for the key phase after the one we're switching to, store them in -5801 // `next_crypto`, make the contents of `next_crypto` current, and move the current keys into -5802 // `prev_crypto`. -5803 let new = self -5804 .crypto -5805 .next_1rtt_keys() -5806 .expect("only called for `Data` packets"); -5807 self.key_phase_size = new -5808 .local -5809 .confidentiality_limit() -5810 .saturating_sub(KEY_UPDATE_MARGIN); -5811 let old = mem::replace( -5812 &mut self.spaces[SpaceId::Data] -5813 .crypto -5814 .as_mut() -5815 .unwrap() // safe because update_keys() can only be triggered by short packets -5816 .packet, -5817 mem::replace(self.next_crypto.as_mut().unwrap(), new), -5818 ); -5819 self.spaces[SpaceId::Data] -5820 .iter_paths_mut() -5821 .for_each(|s| s.sent_with_keys = 0); -5822 self.prev_crypto = Some(PrevCrypto { -5823 crypto: old, -5824 end_packet, -5825 update_unacked: remote, -5826 }); -5827 self.key_phase = !self.key_phase; -5828 } -5829 -5830 fn peer_supports_ack_frequency(&self) -> bool { -5831 self.peer_params.min_ack_delay.is_some() -5832 } -5833 -5834 /// Send an IMMEDIATE_ACK frame to the remote endpoint -5835 /// -5836 /// According to the spec, this will result in an error if the remote endpoint does not support -5837 /// the Acknowledgement Frequency extension -5838 pub(crate) fn immediate_ack(&mut self, path_id: PathId) { -5839 debug_assert_eq!( -5840 self.highest_space, -5841 SpaceId::Data, -5842 "immediate ack must be written in the data space" -5843 ); -5844 self.spaces[self.highest_space] -5845 .for_path(path_id) -5846 .immediate_ack_pending = true; -5847 } -5848 -5849 /// Decodes a packet, returning its decrypted payload, so it can be inspected in tests -5850 #[cfg(test)] -5851 pub(crate) fn decode_packet(&self, event: &ConnectionEvent) -> Option<Vec<u8>> { -5852 let (path_id, first_decode, remaining) = match &event.0 { -5853 ConnectionEventInner::Datagram(DatagramConnectionEvent { -5854 path_id, -5855 first_decode, -5856 remaining, -5857 .. -5858 }) => (path_id, first_decode, remaining), -5859 _ => return None, -5860 }; -5861 -5862 if remaining.is_some() { -5863 panic!("Packets should never be coalesced in tests"); -5864 } -5865 -5866 let decrypted_header = packet_crypto::unprotect_header( -5867 first_decode.clone(), -5868 &self.spaces, -5869 self.zero_rtt_crypto.as_ref(), -5870 self.peer_params.stateless_reset_token, -5871 )?; -5872 -5873 let mut packet = decrypted_header.packet?; -5874 packet_crypto::decrypt_packet_body( -5875 &mut packet, -5876 *path_id, -5877 &self.spaces, -5878 self.zero_rtt_crypto.as_ref(), -5879 self.key_phase, -5880 self.prev_crypto.as_ref(), -5881 self.next_crypto.as_ref(), -5882 ) -5883 .ok()?; -5884 -5885 Some(packet.payload.to_vec()) -5886 } -5887 -5888 /// The number of bytes of packets containing retransmittable frames that have not been -5889 /// acknowledged or declared lost. -5890 #[cfg(test)] -5891 pub(crate) fn bytes_in_flight(&self) -> u64 { -5892 // TODO(@divma): consider including for multipath? -5893 self.path_data(PathId::ZERO).in_flight.bytes -5894 } -5895 -5896 /// Number of bytes worth of non-ack-only packets that may be sent -5897 #[cfg(test)] -5898 pub(crate) fn congestion_window(&self) -> u64 { -5899 let path = self.path_data(PathId::ZERO); -5900 path.congestion -5901 .window() -5902 .saturating_sub(path.in_flight.bytes) -5903 } -5904 -5905 /// Whether no timers but keepalive, idle, rtt, pushnewcid, and key discard are running -5906 #[cfg(test)] -5907 pub(crate) fn is_idle(&self) -> bool { -5908 let current_timers = self.timers.values(); -5909 current_timers -5910 .into_iter() -5911 .filter(|(timer, _)| { -5912 !matches!( -5913 timer, -5914 Timer::Conn(ConnTimer::KeepAlive) -5915 | Timer::PerPath(_, PathTimer::PathKeepAlive) -5916 | Timer::Conn(ConnTimer::PushNewCid) -5917 | Timer::Conn(ConnTimer::KeyDiscard) -5918 ) -5919 }) -5920 .min_by_key(|(_, time)| *time) -5921 .is_none_or(|(timer, _)| timer == Timer::Conn(ConnTimer::Idle)) -5922 } -5923 -5924 /// Whether explicit congestion notification is in use on outgoing packets. -5925 #[cfg(test)] -5926 pub(crate) fn using_ecn(&self) -> bool { -5927 self.path_data(PathId::ZERO).sending_ecn +5694 +5695 if let Some((max_locally_allowed_remote_addresses, max_remotely_allowed_remote_addresses)) = +5696 self.config +5697 .max_remote_nat_traversal_addresses +5698 .zip(params.max_remote_nat_traversal_addresses) +5699 { +5700 if let Some(max_initial_paths) = +5701 multipath_enabled.map(|path_id| path_id.saturating_add(1u8)) +5702 { +5703 let max_local_addresses = max_remotely_allowed_remote_addresses.get(); +5704 let max_remote_addresses = max_locally_allowed_remote_addresses.get(); +5705 self.iroh_hp = +5706 iroh_hp::State::new(max_remote_addresses, max_local_addresses, self.side()); +5707 debug!( +5708 %max_remote_addresses, %max_local_addresses, +5709 "iroh hole punching negotiated" +5710 ); +5711 +5712 match self.side() { +5713 Side::Client => { +5714 if max_initial_paths.as_u32() < max_remote_addresses as u32 + 1 { +5715 // in this case the client might try to open `max_remote_addresses` new +5716 // paths, but the current multipath configuration will not allow it +5717 warn!(%max_initial_paths, %max_remote_addresses, "local client configuration might cause nat traversal issues") +5718 } else if max_local_addresses as u64 +5719 > params.active_connection_id_limit.into_inner() +5720 { +5721 // the server allows us to send at most `params.active_connection_id_limit` +5722 // but they might need at least `max_local_addresses` to effectively send +5723 // `PATH_CHALLENGE` frames to each advertised local address +5724 warn!(%max_local_addresses, remote_cid_limit=%params.active_connection_id_limit.into_inner(), "remote server configuration might cause nat traversal issues") +5725 } +5726 } +5727 Side::Server => { +5728 if (max_initial_paths.as_u32() as u64) < crate::LOC_CID_COUNT { +5729 warn!(%max_initial_paths, local_cid_limit=%crate::LOC_CID_COUNT, "local server configuration might cause nat traversal issues") +5730 } +5731 } +5732 } +5733 } else { +5734 debug!("iroh nat traversal enabled for both endpoints, but multipath is missing") +5735 } +5736 } +5737 +5738 self.peer_params = params; +5739 let peer_max_udp_payload_size = +5740 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX); +5741 self.path_data_mut(PathId::ZERO) +5742 .mtud +5743 .on_peer_max_udp_payload_size_received(peer_max_udp_payload_size); +5744 } +5745 +5746 /// Decrypts a packet, returning the packet number on success +5747 fn decrypt_packet( +5748 &mut self, +5749 now: Instant, +5750 path_id: PathId, +5751 packet: &mut Packet, +5752 ) -> Result<Option<u64>, Option<TransportError>> { +5753 let result = packet_crypto::decrypt_packet_body( +5754 packet, +5755 path_id, +5756 &self.spaces, +5757 self.zero_rtt_crypto.as_ref(), +5758 self.key_phase, +5759 self.prev_crypto.as_ref(), +5760 self.next_crypto.as_ref(), +5761 )?; +5762 +5763 let result = match result { +5764 Some(r) => r, +5765 None => return Ok(None), +5766 }; +5767 +5768 if result.outgoing_key_update_acked { +5769 if let Some(prev) = self.prev_crypto.as_mut() { +5770 prev.end_packet = Some((result.number, now)); +5771 self.set_key_discard_timer(now, packet.header.space()); +5772 } +5773 } +5774 +5775 if result.incoming_key_update { +5776 trace!("key update authenticated"); +5777 self.update_keys(Some((result.number, now)), true); +5778 self.set_key_discard_timer(now, packet.header.space()); +5779 } +5780 +5781 Ok(Some(result.number)) +5782 } +5783 +5784 fn update_keys(&mut self, end_packet: Option<(u64, Instant)>, remote: bool) { +5785 trace!("executing key update"); +5786 // Generate keys for the key phase after the one we're switching to, store them in +5787 // `next_crypto`, make the contents of `next_crypto` current, and move the current keys into +5788 // `prev_crypto`. +5789 let new = self +5790 .crypto +5791 .next_1rtt_keys() +5792 .expect("only called for `Data` packets"); +5793 self.key_phase_size = new +5794 .local +5795 .confidentiality_limit() +5796 .saturating_sub(KEY_UPDATE_MARGIN); +5797 let old = mem::replace( +5798 &mut self.spaces[SpaceId::Data] +5799 .crypto +5800 .as_mut() +5801 .unwrap() // safe because update_keys() can only be triggered by short packets +5802 .packet, +5803 mem::replace(self.next_crypto.as_mut().unwrap(), new), +5804 ); +5805 self.spaces[SpaceId::Data] +5806 .iter_paths_mut() +5807 .for_each(|s| s.sent_with_keys = 0); +5808 self.prev_crypto = Some(PrevCrypto { +5809 crypto: old, +5810 end_packet, +5811 update_unacked: remote, +5812 }); +5813 self.key_phase = !self.key_phase; +5814 } +5815 +5816 fn peer_supports_ack_frequency(&self) -> bool { +5817 self.peer_params.min_ack_delay.is_some() +5818 } +5819 +5820 /// Send an IMMEDIATE_ACK frame to the remote endpoint +5821 /// +5822 /// According to the spec, this will result in an error if the remote endpoint does not support +5823 /// the Acknowledgement Frequency extension +5824 pub(crate) fn immediate_ack(&mut self, path_id: PathId) { +5825 debug_assert_eq!( +5826 self.highest_space, +5827 SpaceId::Data, +5828 "immediate ack must be written in the data space" +5829 ); +5830 self.spaces[self.highest_space] +5831 .for_path(path_id) +5832 .immediate_ack_pending = true; +5833 } +5834 +5835 /// Decodes a packet, returning its decrypted payload, so it can be inspected in tests +5836 #[cfg(test)] +5837 pub(crate) fn decode_packet(&self, event: &ConnectionEvent) -> Option<Vec<u8>> { +5838 let (path_id, first_decode, remaining) = match &event.0 { +5839 ConnectionEventInner::Datagram(DatagramConnectionEvent { +5840 path_id, +5841 first_decode, +5842 remaining, +5843 .. +5844 }) => (path_id, first_decode, remaining), +5845 _ => return None, +5846 }; +5847 +5848 if remaining.is_some() { +5849 panic!("Packets should never be coalesced in tests"); +5850 } +5851 +5852 let decrypted_header = packet_crypto::unprotect_header( +5853 first_decode.clone(), +5854 &self.spaces, +5855 self.zero_rtt_crypto.as_ref(), +5856 self.peer_params.stateless_reset_token, +5857 )?; +5858 +5859 let mut packet = decrypted_header.packet?; +5860 packet_crypto::decrypt_packet_body( +5861 &mut packet, +5862 *path_id, +5863 &self.spaces, +5864 self.zero_rtt_crypto.as_ref(), +5865 self.key_phase, +5866 self.prev_crypto.as_ref(), +5867 self.next_crypto.as_ref(), +5868 ) +5869 .ok()?; +5870 +5871 Some(packet.payload.to_vec()) +5872 } +5873 +5874 /// The number of bytes of packets containing retransmittable frames that have not been +5875 /// acknowledged or declared lost. +5876 #[cfg(test)] +5877 pub(crate) fn bytes_in_flight(&self) -> u64 { +5878 // TODO(@divma): consider including for multipath? +5879 self.path_data(PathId::ZERO).in_flight.bytes +5880 } +5881 +5882 /// Number of bytes worth of non-ack-only packets that may be sent +5883 #[cfg(test)] +5884 pub(crate) fn congestion_window(&self) -> u64 { +5885 let path = self.path_data(PathId::ZERO); +5886 path.congestion +5887 .window() +5888 .saturating_sub(path.in_flight.bytes) +5889 } +5890 +5891 /// Whether no timers but keepalive, idle, rtt, pushnewcid, and key discard are running +5892 #[cfg(test)] +5893 pub(crate) fn is_idle(&self) -> bool { +5894 let current_timers = self.timers.values(); +5895 current_timers +5896 .into_iter() +5897 .filter(|(timer, _)| { +5898 !matches!( +5899 timer, +5900 Timer::Conn(ConnTimer::KeepAlive) +5901 | Timer::PerPath(_, PathTimer::PathKeepAlive) +5902 | Timer::Conn(ConnTimer::PushNewCid) +5903 | Timer::Conn(ConnTimer::KeyDiscard) +5904 ) +5905 }) +5906 .min_by_key(|(_, time)| *time) +5907 .is_none_or(|(timer, _)| timer == Timer::Conn(ConnTimer::Idle)) +5908 } +5909 +5910 /// Whether explicit congestion notification is in use on outgoing packets. +5911 #[cfg(test)] +5912 pub(crate) fn using_ecn(&self) -> bool { +5913 self.path_data(PathId::ZERO).sending_ecn +5914 } +5915 +5916 /// The number of received bytes in the current path +5917 #[cfg(test)] +5918 pub(crate) fn total_recvd(&self) -> u64 { +5919 self.path_data(PathId::ZERO).total_recvd +5920 } +5921 +5922 #[cfg(test)] +5923 pub(crate) fn active_local_cid_seq(&self) -> (u64, u64) { +5924 self.local_cid_state +5925 .get(&PathId::ZERO) +5926 .unwrap() +5927 .active_seq() 5928 } 5929 -5930 /// The number of received bytes in the current path -5931 #[cfg(test)] -5932 pub(crate) fn total_recvd(&self) -> u64 { -5933 self.path_data(PathId::ZERO).total_recvd -5934 } -5935 -5936 #[cfg(test)] -5937 pub(crate) fn active_local_cid_seq(&self) -> (u64, u64) { -5938 self.local_cid_state -5939 .get(&PathId::ZERO) -5940 .unwrap() -5941 .active_seq() -5942 } -5943 -5944 #[cfg(test)] -5945 #[track_caller] -5946 pub(crate) fn active_local_path_cid_seq(&self, path_id: u32) -> (u64, u64) { -5947 self.local_cid_state -5948 .get(&PathId(path_id)) -5949 .unwrap() -5950 .active_seq() -5951 } -5952 -5953 /// Instruct the peer to replace previously issued CIDs by sending a NEW_CONNECTION_ID frame -5954 /// with updated `retire_prior_to` field set to `v` -5955 #[cfg(test)] -5956 pub(crate) fn rotate_local_cid(&mut self, v: u64, now: Instant) { -5957 let n = self -5958 .local_cid_state -5959 .get_mut(&PathId::ZERO) -5960 .unwrap() -5961 .assign_retire_seq(v); -5962 self.endpoint_events -5963 .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n)); -5964 } -5965 -5966 /// Check the current active remote CID sequence for `PathId::ZERO` -5967 #[cfg(test)] -5968 pub(crate) fn active_rem_cid_seq(&self) -> u64 { -5969 self.rem_cids.get(&PathId::ZERO).unwrap().active_seq() +5930 #[cfg(test)] +5931 #[track_caller] +5932 pub(crate) fn active_local_path_cid_seq(&self, path_id: u32) -> (u64, u64) { +5933 self.local_cid_state +5934 .get(&PathId(path_id)) +5935 .unwrap() +5936 .active_seq() +5937 } +5938 +5939 /// Instruct the peer to replace previously issued CIDs by sending a NEW_CONNECTION_ID frame +5940 /// with updated `retire_prior_to` field set to `v` +5941 #[cfg(test)] +5942 pub(crate) fn rotate_local_cid(&mut self, v: u64, now: Instant) { +5943 let n = self +5944 .local_cid_state +5945 .get_mut(&PathId::ZERO) +5946 .unwrap() +5947 .assign_retire_seq(v); +5948 self.endpoint_events +5949 .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n)); +5950 } +5951 +5952 /// Check the current active remote CID sequence for `PathId::ZERO` +5953 #[cfg(test)] +5954 pub(crate) fn active_rem_cid_seq(&self) -> u64 { +5955 self.rem_cids.get(&PathId::ZERO).unwrap().active_seq() +5956 } +5957 +5958 /// Returns the detected maximum udp payload size for the current path +5959 #[cfg(test)] +5960 pub(crate) fn path_mtu(&self, path_id: PathId) -> u16 { +5961 self.path_data(path_id).current_mtu() +5962 } +5963 +5964 /// Triggers path validation on all paths +5965 #[cfg(test)] +5966 pub(crate) fn trigger_path_validation(&mut self) { +5967 for path in self.paths.values_mut() { +5968 path.data.send_new_challenge = true; +5969 } 5970 } 5971 -5972 /// Returns the detected maximum udp payload size for the current path -5973 #[cfg(test)] -5974 pub(crate) fn path_mtu(&self, path_id: PathId) -> u16 { -5975 self.path_data(path_id).current_mtu() -5976 } -5977 -5978 /// Triggers path validation on all paths -5979 #[cfg(test)] -5980 pub(crate) fn trigger_path_validation(&mut self) { -5981 for path in self.paths.values_mut() { -5982 path.data.send_new_challenge = true; -5983 } -5984 } -5985 -5986 /// Whether we have 1-RTT data to send -5987 /// -5988 /// This checks for frames that can only be sent in the data space (1-RTT): -5989 /// - Pending PATH_CHALLENGE frames on the active and previous path if just migrated. -5990 /// - Pending PATH_RESPONSE frames. -5991 /// - Pending data to send in STREAM frames. -5992 /// - Pending DATAGRAM frames to send. -5993 /// -5994 /// See also [`PacketSpace::can_send`] which keeps track of all other frame types that -5995 /// may need to be sent. -5996 fn can_send_1rtt(&self, path_id: PathId, max_size: usize) -> SendableFrames { -5997 let path_exclusive = self.paths.get(&path_id).is_some_and(|path| { -5998 path.data.send_new_challenge -5999 || path -6000 .prev -6001 .as_ref() -6002 .is_some_and(|(_, path)| path.send_new_challenge) -6003 || !path.data.path_responses.is_empty() -6004 }); -6005 let other = self.streams.can_send_stream_data() -6006 || self -6007 .datagrams -6008 .outgoing -6009 .front() -6010 .is_some_and(|x| x.size(true) <= max_size); -6011 SendableFrames { -6012 acks: false, -6013 other, -6014 close: false, -6015 path_exclusive, -6016 } -6017 } -6018 -6019 /// Terminate the connection instantly, without sending a close packet -6020 fn kill(&mut self, reason: ConnectionError) { -6021 self.close_common(); -6022 self.state.move_to_drained(Some(reason)); -6023 self.endpoint_events.push_back(EndpointEventInner::Drained); -6024 } -6025 -6026 /// Storage size required for the largest packet that can be transmitted on all currently -6027 /// available paths +5972 /// Whether we have 1-RTT data to send +5973 /// +5974 /// This checks for frames that can only be sent in the data space (1-RTT): +5975 /// - Pending PATH_CHALLENGE frames on the active and previous path if just migrated. +5976 /// - Pending PATH_RESPONSE frames. +5977 /// - Pending data to send in STREAM frames. +5978 /// - Pending DATAGRAM frames to send. +5979 /// +5980 /// See also [`PacketSpace::can_send`] which keeps track of all other frame types that +5981 /// may need to be sent. +5982 fn can_send_1rtt(&self, path_id: PathId, max_size: usize) -> SendableFrames { +5983 let path_exclusive = self.paths.get(&path_id).is_some_and(|path| { +5984 path.data.send_new_challenge +5985 || path +5986 .prev +5987 .as_ref() +5988 .is_some_and(|(_, path)| path.send_new_challenge) +5989 || !path.data.path_responses.is_empty() +5990 }); +5991 let other = self.streams.can_send_stream_data() +5992 || self +5993 .datagrams +5994 .outgoing +5995 .front() +5996 .is_some_and(|x| x.size(true) <= max_size); +5997 SendableFrames { +5998 acks: false, +5999 other, +6000 close: false, +6001 path_exclusive, +6002 } +6003 } +6004 +6005 /// Terminate the connection instantly, without sending a close packet +6006 fn kill(&mut self, reason: ConnectionError) { +6007 self.close_common(); +6008 self.state.move_to_drained(Some(reason)); +6009 self.endpoint_events.push_back(EndpointEventInner::Drained); +6010 } +6011 +6012 /// Storage size required for the largest packet that can be transmitted on all currently +6013 /// available paths +6014 /// +6015 /// Buffers passed to [`Connection::poll_transmit`] should be at least this large. +6016 /// +6017 /// When multipath is enabled, this value is the minimum MTU across all available paths. +6018 pub fn current_mtu(&self) -> u16 { +6019 self.paths +6020 .iter() +6021 .filter(|&(path_id, _path_state)| !self.abandoned_paths.contains(path_id)) +6022 .map(|(_path_id, path_state)| path_state.data.current_mtu()) +6023 .min() +6024 .expect("There is always at least one available path") +6025 } +6026 +6027 /// Size of non-frame data for a 1-RTT packet 6028 /// -6029 /// Buffers passed to [`Connection::poll_transmit`] should be at least this large. -6030 /// -6031 /// When multipath is enabled, this value is the minimum MTU across all available paths. -6032 pub fn current_mtu(&self) -> u16 { -6033 self.paths -6034 .iter() -6035 .filter(|&(path_id, _path_state)| !self.abandoned_paths.contains(path_id)) -6036 .map(|(_path_id, path_state)| path_state.data.current_mtu()) -6037 .min() -6038 .expect("There is always at least one available path") -6039 } -6040 -6041 /// Size of non-frame data for a 1-RTT packet -6042 /// -6043 /// Quantifies space consumed by the QUIC header and AEAD tag. All other bytes in a packet are -6044 /// frames. Changes if the length of the remote connection ID changes, which is expected to be -6045 /// rare. If `pn` is specified, may additionally change unpredictably due to variations in -6046 /// latency and packet loss. -6047 fn predict_1rtt_overhead(&mut self, pn: u64, path: PathId) -> usize { -6048 let pn_len = PacketNumber::new( -6049 pn, -6050 self.spaces[SpaceId::Data] -6051 .for_path(path) -6052 .largest_acked_packet -6053 .unwrap_or(0), -6054 ) -6055 .len(); -6056 -6057 // 1 byte for flags -6058 1 + self -6059 .rem_cids -6060 .get(&path) -6061 .map(|cids| cids.active().len()) -6062 .unwrap_or(20) // Max CID len in QUIC v1 -6063 + pn_len -6064 + self.tag_len_1rtt() +6029 /// Quantifies space consumed by the QUIC header and AEAD tag. All other bytes in a packet are +6030 /// frames. Changes if the length of the remote connection ID changes, which is expected to be +6031 /// rare. If `pn` is specified, may additionally change unpredictably due to variations in +6032 /// latency and packet loss. +6033 fn predict_1rtt_overhead(&mut self, pn: u64, path: PathId) -> usize { +6034 let pn_len = PacketNumber::new( +6035 pn, +6036 self.spaces[SpaceId::Data] +6037 .for_path(path) +6038 .largest_acked_packet +6039 .unwrap_or(0), +6040 ) +6041 .len(); +6042 +6043 // 1 byte for flags +6044 1 + self +6045 .rem_cids +6046 .get(&path) +6047 .map(|cids| cids.active().len()) +6048 .unwrap_or(20) // Max CID len in QUIC v1 +6049 + pn_len +6050 + self.tag_len_1rtt() +6051 } +6052 +6053 fn predict_1rtt_overhead_no_pn(&self) -> usize { +6054 let pn_len = 4; +6055 +6056 let cid_len = self +6057 .rem_cids +6058 .values() +6059 .map(|cids| cids.active().len()) +6060 .max() +6061 .unwrap_or(20); // Max CID len in QUIC v1 +6062 +6063 // 1 byte for flags +6064 1 + cid_len + pn_len + self.tag_len_1rtt() 6065 } 6066 -6067 fn predict_1rtt_overhead_no_pn(&self) -> usize { -6068 let pn_len = 4; -6069 -6070 let cid_len = self -6071 .rem_cids -6072 .values() -6073 .map(|cids| cids.active().len()) -6074 .max() -6075 .unwrap_or(20); // Max CID len in QUIC v1 -6076 -6077 // 1 byte for flags -6078 1 + cid_len + pn_len + self.tag_len_1rtt() -6079 } -6080 -6081 fn tag_len_1rtt(&self) -> usize { -6082 let key = match self.spaces[SpaceId::Data].crypto.as_ref() { -6083 Some(crypto) => Some(&*crypto.packet.local), -6084 None => self.zero_rtt_crypto.as_ref().map(|x| &*x.packet), -6085 }; -6086 // If neither Data nor 0-RTT keys are available, make a reasonable tag length guess. As of -6087 // this writing, all QUIC cipher suites use 16-byte tags. We could return `None` instead, -6088 // but that would needlessly prevent sending datagrams during 0-RTT. -6089 key.map_or(16, |x| x.tag_len()) +6067 fn tag_len_1rtt(&self) -> usize { +6068 let key = match self.spaces[SpaceId::Data].crypto.as_ref() { +6069 Some(crypto) => Some(&*crypto.packet.local), +6070 None => self.zero_rtt_crypto.as_ref().map(|x| &*x.packet), +6071 }; +6072 // If neither Data nor 0-RTT keys are available, make a reasonable tag length guess. As of +6073 // this writing, all QUIC cipher suites use 16-byte tags. We could return `None` instead, +6074 // but that would needlessly prevent sending datagrams during 0-RTT. +6075 key.map_or(16, |x| x.tag_len()) +6076 } +6077 +6078 /// Mark the path as validated, and enqueue NEW_TOKEN frames to be sent as appropriate +6079 fn on_path_validated(&mut self, path_id: PathId) { +6080 self.path_data_mut(path_id).validated = true; +6081 let ConnectionSide::Server { server_config } = &self.side else { +6082 return; +6083 }; +6084 let network_path = self.path_data(path_id).network_path; +6085 let new_tokens = &mut self.spaces[SpaceId::Data as usize].pending.new_tokens; +6086 new_tokens.clear(); +6087 for _ in 0..server_config.validation_token.sent { +6088 new_tokens.push(network_path); +6089 } 6090 } 6091 -6092 /// Mark the path as validated, and enqueue NEW_TOKEN frames to be sent as appropriate -6093 fn on_path_validated(&mut self, path_id: PathId) { -6094 self.path_data_mut(path_id).validated = true; -6095 let ConnectionSide::Server { server_config } = &self.side else { -6096 return; -6097 }; -6098 let network_path = self.path_data(path_id).network_path; -6099 let new_tokens = &mut self.spaces[SpaceId::Data as usize].pending.new_tokens; -6100 new_tokens.clear(); -6101 for _ in 0..server_config.validation_token.sent { -6102 new_tokens.push(network_path); -6103 } -6104 } -6105 -6106 /// Handle new path status information: PATH_STATUS_AVAILABLE, PATH_STATUS_BACKUP -6107 fn on_path_status(&mut self, path_id: PathId, status: PathStatus, status_seq_no: VarInt) { -6108 if let Some(path) = self.paths.get_mut(&path_id) { -6109 path.data.status.remote_update(status, status_seq_no); -6110 } else { -6111 debug!("PATH_STATUS_AVAILABLE received unknown path {:?}", path_id); -6112 } -6113 self.events.push_back( -6114 PathEvent::RemoteStatus { -6115 id: path_id, -6116 status, -6117 } -6118 .into(), -6119 ); -6120 } -6121 -6122 /// Returns the maximum [`PathId`] to be used for sending in this connection. -6123 /// -6124 /// This is calculated as minimum between the local and remote's maximums when multipath is -6125 /// enabled, or `None` when disabled. -6126 /// -6127 /// For data that's received, we should use [`Self::local_max_path_id`] instead. -6128 /// The reasoning is that the remote might already have updated to its own newer -6129 /// [`Self::max_path_id`] after sending out a `MAX_PATH_ID` frame, but it got re-ordered. -6130 fn max_path_id(&self) -> Option<PathId> { -6131 if self.is_multipath_negotiated() { -6132 Some(self.remote_max_path_id.min(self.local_max_path_id)) -6133 } else { -6134 None -6135 } -6136 } -6137 -6138 /// Add addresses the local endpoint considers are reachable for nat traversal -6139 pub fn add_nat_traversal_address(&mut self, address: SocketAddr) -> Result<(), iroh_hp::Error> { -6140 if let Some(added) = self.iroh_hp.add_local_address(address)? { -6141 self.spaces[SpaceId::Data].pending.add_address.insert(added); -6142 }; -6143 Ok(()) -6144 } -6145 -6146 /// Removes an address the endpoing no longer considers reachable for nat traversal -6147 /// -6148 /// Addresses not present in the set will be silently ignored. -6149 pub fn remove_nat_traversal_address( -6150 &mut self, -6151 address: SocketAddr, -6152 ) -> Result<(), iroh_hp::Error> { -6153 if let Some(removed) = self.iroh_hp.remove_local_address(address)? { -6154 self.spaces[SpaceId::Data] -6155 .pending -6156 .remove_address -6157 .insert(removed); -6158 } -6159 Ok(()) -6160 } -6161 -6162 /// Get the current local nat traversal addresses -6163 pub fn get_local_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> { -6164 self.iroh_hp.get_local_nat_traversal_addresses() -6165 } -6166 -6167 /// Get the currently advertised nat traversal addresses by the server -6168 pub fn get_remote_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> { -6169 Ok(self -6170 .iroh_hp -6171 .client_side()? -6172 .get_remote_nat_traversal_addresses()) -6173 } -6174 -6175 /// Attempts to open a path for nat traversal. -6176 /// -6177 /// `ipv6` indicates if the path should be opened using an IPV6 remote. If the address is -6178 /// ignored, it will return `None`. -6179 /// -6180 /// On success returns the [`PathId`] and remote address of the path, as well as whether the path -6181 /// existed for the adjusted remote. -6182 fn open_nat_traversal_path( -6183 &mut self, -6184 now: Instant, -6185 (ip, port): (IpAddr, u16), -6186 ipv6: bool, -6187 ) -> Result<Option<(PathId, SocketAddr, bool)>, PathError> { -6188 // If this endpoint is an IPv6 endpoint we use IPv6 addresses for all remotes. -6189 let remote = match ip { -6190 IpAddr::V4(addr) if ipv6 => SocketAddr::new(addr.to_ipv6_mapped().into(), port), -6191 IpAddr::V4(addr) => SocketAddr::new(addr.into(), port), -6192 IpAddr::V6(_) if ipv6 => SocketAddr::new(ip, port), -6193 IpAddr::V6(_) => { -6194 trace!("not using IPv6 nat candidate for IPv4 socket"); -6195 return Ok(None); -6196 } -6197 }; -6198 // TODO(matheus23): Probe the correct 4-tuple, instead of only a remote address? -6199 // By specifying None, we do two things: 1. open_path_ensure won't generate two -6200 // paths to the same remote and 2. we let the OS choose which interface to use for -6201 // sending on that path. -6202 let network_path = FourTuple { -6203 remote, -6204 local_ip: None, -6205 }; -6206 match self.open_path_ensure(network_path, PathStatus::Backup, now) { -6207 Ok((path_id, path_was_known)) => { -6208 if path_was_known { -6209 trace!(%path_id, %remote, "nat traversal: path existed for remote"); -6210 } -6211 Ok(Some((path_id, remote, path_was_known))) -6212 } -6213 Err(e) => { -6214 debug!(%remote, %e, "nat traversal: failed to probe remote"); -6215 Err(e) -6216 } -6217 } -6218 } -6219 -6220 /// Initiates a new nat traversal round -6221 /// -6222 /// A nat traversal round involves advertising the client's local addresses in `REACH_OUT` -6223 /// frames, and initiating probing of the known remote addresses. When a new round is -6224 /// initiated, the previous one is cancelled, and paths that have not been opened are closed. -6225 /// -6226 /// Returns the server addresses that are now being probed. -6227 /// If addresses fail due to spurious errors, these might succeed later and not be returned in -6228 /// this set. -6229 pub fn initiate_nat_traversal_round( -6230 &mut self, -6231 now: Instant, -6232 ) -> Result<Vec<SocketAddr>, iroh_hp::Error> { -6233 if self.state.is_closed() { -6234 return Err(iroh_hp::Error::Closed); -6235 } -6236 -6237 let client_state = self.iroh_hp.client_side_mut()?; -6238 let iroh_hp::NatTraversalRound { -6239 new_round, -6240 reach_out_at, -6241 addresses_to_probe, -6242 prev_round_path_ids, -6243 } = client_state.initiate_nat_traversal_round()?; -6244 -6245 self.spaces[SpaceId::Data].pending.reach_out = Some((new_round, reach_out_at)); -6246 -6247 for path_id in prev_round_path_ids { -6248 // TODO(@divma): this sounds reasonable but we need if this actually works for the -6249 // purposes of the protocol -6250 let validated = self -6251 .path(path_id) -6252 .map(|path| path.validated) -6253 .unwrap_or(false); -6254 -6255 if !validated { -6256 let _ = self.close_path( -6257 now, -6258 path_id, -6259 TransportErrorCode::APPLICATION_ABANDON_PATH.into(), -6260 ); -6261 } -6262 } -6263 -6264 let mut err = None; -6265 -6266 let mut path_ids = Vec::with_capacity(addresses_to_probe.len()); -6267 let mut probed_addresses = Vec::with_capacity(addresses_to_probe.len()); -6268 let ipv6 = self -6269 .paths -6270 .values() -6271 .any(|p| p.data.network_path.remote.is_ipv6()); -6272 -6273 for (id, address) in addresses_to_probe { -6274 match self.open_nat_traversal_path(now, address, ipv6) { -6275 Ok(None) => {} -6276 Ok(Some((path_id, remote, path_was_known))) => { -6277 if !path_was_known { -6278 path_ids.push(path_id); -6279 probed_addresses.push(remote); -6280 } -6281 } -6282 Err(e) => { -6283 self.iroh_hp -6284 .client_side_mut() -6285 .expect("validated") -6286 .report_in_continuation(id, e); -6287 err.get_or_insert(e); -6288 } -6289 } -6290 } -6291 -6292 if let Some(err) = err { -6293 // We failed to probe any addresses, bail out -6294 if probed_addresses.is_empty() { -6295 return Err(iroh_hp::Error::Multipath(err)); -6296 } -6297 } -6298 -6299 self.iroh_hp -6300 .client_side_mut() -6301 .expect("connection side validated") -6302 .set_round_path_ids(path_ids); -6303 -6304 Ok(probed_addresses) -6305 } -6306 -6307 /// Attempts to continue a nat traversal round by trying to open paths for pending client probes. -6308 /// -6309 /// If there was nothing to do, it returns `None`. Otherwise it returns whether the path was -6310 /// successfully open. -6311 fn continue_nat_traversal_round(&mut self, now: Instant) -> Option<bool> { -6312 let client_state = self.iroh_hp.client_side_mut().ok()?; -6313 let (id, address) = client_state.continue_nat_traversal_round()?; -6314 let ipv6 = self -6315 .paths -6316 .values() -6317 .any(|p| p.data.network_path.remote.is_ipv6()); -6318 let open_result = self.open_nat_traversal_path(now, address, ipv6); -6319 let client_state = self.iroh_hp.client_side_mut().expect("validated"); -6320 match open_result { -6321 Ok(None) => Some(true), -6322 Ok(Some((path_id, _remote, path_was_known))) => { -6323 if !path_was_known { -6324 client_state.add_round_path_id(path_id); -6325 } -6326 Some(true) -6327 } -6328 Err(e) => { -6329 client_state.report_in_continuation(id, e); -6330 Some(false) -6331 } -6332 } -6333 } -6334} -6335 -6336impl fmt::Debug for Connection { -6337 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -6338 f.debug_struct("Connection") -6339 .field("handshake_cid", &self.handshake_cid) -6340 .finish() -6341 } -6342} -6343 -6344#[derive(Debug, Copy, Clone, PartialEq, Eq)] -6345enum PathBlocked { -6346 No, -6347 AntiAmplification, -6348 Congestion, -6349 Pacing, -6350} -6351 -6352/// Fields of `Connection` specific to it being client-side or server-side -6353enum ConnectionSide { -6354 Client { -6355 /// Sent in every outgoing Initial packet. Always empty after Initial keys are discarded -6356 token: Bytes, -6357 token_store: Arc<dyn TokenStore>, -6358 server_name: String, -6359 }, -6360 Server { -6361 server_config: Arc<ServerConfig>, -6362 }, -6363} +6092 /// Handle new path status information: PATH_STATUS_AVAILABLE, PATH_STATUS_BACKUP +6093 fn on_path_status(&mut self, path_id: PathId, status: PathStatus, status_seq_no: VarInt) { +6094 if let Some(path) = self.paths.get_mut(&path_id) { +6095 path.data.status.remote_update(status, status_seq_no); +6096 } else { +6097 debug!("PATH_STATUS_AVAILABLE received unknown path {:?}", path_id); +6098 } +6099 self.events.push_back( +6100 PathEvent::RemoteStatus { +6101 id: path_id, +6102 status, +6103 } +6104 .into(), +6105 ); +6106 } +6107 +6108 /// Returns the maximum [`PathId`] to be used for sending in this connection. +6109 /// +6110 /// This is calculated as minimum between the local and remote's maximums when multipath is +6111 /// enabled, or `None` when disabled. +6112 /// +6113 /// For data that's received, we should use [`Self::local_max_path_id`] instead. +6114 /// The reasoning is that the remote might already have updated to its own newer +6115 /// [`Self::max_path_id`] after sending out a `MAX_PATH_ID` frame, but it got re-ordered. +6116 fn max_path_id(&self) -> Option<PathId> { +6117 if self.is_multipath_negotiated() { +6118 Some(self.remote_max_path_id.min(self.local_max_path_id)) +6119 } else { +6120 None +6121 } +6122 } +6123 +6124 /// Add addresses the local endpoint considers are reachable for nat traversal +6125 pub fn add_nat_traversal_address(&mut self, address: SocketAddr) -> Result<(), iroh_hp::Error> { +6126 if let Some(added) = self.iroh_hp.add_local_address(address)? { +6127 self.spaces[SpaceId::Data].pending.add_address.insert(added); +6128 }; +6129 Ok(()) +6130 } +6131 +6132 /// Removes an address the endpoing no longer considers reachable for nat traversal +6133 /// +6134 /// Addresses not present in the set will be silently ignored. +6135 pub fn remove_nat_traversal_address( +6136 &mut self, +6137 address: SocketAddr, +6138 ) -> Result<(), iroh_hp::Error> { +6139 if let Some(removed) = self.iroh_hp.remove_local_address(address)? { +6140 self.spaces[SpaceId::Data] +6141 .pending +6142 .remove_address +6143 .insert(removed); +6144 } +6145 Ok(()) +6146 } +6147 +6148 /// Get the current local nat traversal addresses +6149 pub fn get_local_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> { +6150 self.iroh_hp.get_local_nat_traversal_addresses() +6151 } +6152 +6153 /// Get the currently advertised nat traversal addresses by the server +6154 pub fn get_remote_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> { +6155 Ok(self +6156 .iroh_hp +6157 .client_side()? +6158 .get_remote_nat_traversal_addresses()) +6159 } +6160 +6161 /// Attempts to open a path for nat traversal. +6162 /// +6163 /// `ipv6` indicates if the path should be opened using an IPV6 remote. If the address is +6164 /// ignored, it will return `None`. +6165 /// +6166 /// On success returns the [`PathId`] and remote address of the path, as well as whether the path +6167 /// existed for the adjusted remote. +6168 fn open_nat_traversal_path( +6169 &mut self, +6170 now: Instant, +6171 (ip, port): (IpAddr, u16), +6172 ipv6: bool, +6173 ) -> Result<Option<(PathId, SocketAddr, bool)>, PathError> { +6174 // If this endpoint is an IPv6 endpoint we use IPv6 addresses for all remotes. +6175 let remote = match ip { +6176 IpAddr::V4(addr) if ipv6 => SocketAddr::new(addr.to_ipv6_mapped().into(), port), +6177 IpAddr::V4(addr) => SocketAddr::new(addr.into(), port), +6178 IpAddr::V6(_) if ipv6 => SocketAddr::new(ip, port), +6179 IpAddr::V6(_) => { +6180 trace!("not using IPv6 nat candidate for IPv4 socket"); +6181 return Ok(None); +6182 } +6183 }; +6184 // TODO(matheus23): Probe the correct 4-tuple, instead of only a remote address? +6185 // By specifying None, we do two things: 1. open_path_ensure won't generate two +6186 // paths to the same remote and 2. we let the OS choose which interface to use for +6187 // sending on that path. +6188 let network_path = FourTuple { +6189 remote, +6190 local_ip: None, +6191 }; +6192 match self.open_path_ensure(network_path, PathStatus::Backup, now) { +6193 Ok((path_id, path_was_known)) => { +6194 if path_was_known { +6195 trace!(%path_id, %remote, "nat traversal: path existed for remote"); +6196 } +6197 Ok(Some((path_id, remote, path_was_known))) +6198 } +6199 Err(e) => { +6200 debug!(%remote, %e, "nat traversal: failed to probe remote"); +6201 Err(e) +6202 } +6203 } +6204 } +6205 +6206 /// Initiates a new nat traversal round +6207 /// +6208 /// A nat traversal round involves advertising the client's local addresses in `REACH_OUT` +6209 /// frames, and initiating probing of the known remote addresses. When a new round is +6210 /// initiated, the previous one is cancelled, and paths that have not been opened are closed. +6211 /// +6212 /// Returns the server addresses that are now being probed. +6213 /// If addresses fail due to spurious errors, these might succeed later and not be returned in +6214 /// this set. +6215 pub fn initiate_nat_traversal_round( +6216 &mut self, +6217 now: Instant, +6218 ) -> Result<Vec<SocketAddr>, iroh_hp::Error> { +6219 if self.state.is_closed() { +6220 return Err(iroh_hp::Error::Closed); +6221 } +6222 +6223 let client_state = self.iroh_hp.client_side_mut()?; +6224 let iroh_hp::NatTraversalRound { +6225 new_round, +6226 reach_out_at, +6227 addresses_to_probe, +6228 prev_round_path_ids, +6229 } = client_state.initiate_nat_traversal_round()?; +6230 +6231 self.spaces[SpaceId::Data].pending.reach_out = Some((new_round, reach_out_at)); +6232 +6233 for path_id in prev_round_path_ids { +6234 // TODO(@divma): this sounds reasonable but we need if this actually works for the +6235 // purposes of the protocol +6236 let validated = self +6237 .path(path_id) +6238 .map(|path| path.validated) +6239 .unwrap_or(false); +6240 +6241 if !validated { +6242 let _ = self.close_path( +6243 now, +6244 path_id, +6245 TransportErrorCode::APPLICATION_ABANDON_PATH.into(), +6246 ); +6247 } +6248 } +6249 +6250 let mut err = None; +6251 +6252 let mut path_ids = Vec::with_capacity(addresses_to_probe.len()); +6253 let mut probed_addresses = Vec::with_capacity(addresses_to_probe.len()); +6254 let ipv6 = self +6255 .paths +6256 .values() +6257 .any(|p| p.data.network_path.remote.is_ipv6()); +6258 +6259 for (id, address) in addresses_to_probe { +6260 match self.open_nat_traversal_path(now, address, ipv6) { +6261 Ok(None) => {} +6262 Ok(Some((path_id, remote, path_was_known))) => { +6263 if !path_was_known { +6264 path_ids.push(path_id); +6265 probed_addresses.push(remote); +6266 } +6267 } +6268 Err(e) => { +6269 self.iroh_hp +6270 .client_side_mut() +6271 .expect("validated") +6272 .report_in_continuation(id, e); +6273 err.get_or_insert(e); +6274 } +6275 } +6276 } +6277 +6278 if let Some(err) = err { +6279 // We failed to probe any addresses, bail out +6280 if probed_addresses.is_empty() { +6281 return Err(iroh_hp::Error::Multipath(err)); +6282 } +6283 } +6284 +6285 self.iroh_hp +6286 .client_side_mut() +6287 .expect("connection side validated") +6288 .set_round_path_ids(path_ids); +6289 +6290 Ok(probed_addresses) +6291 } +6292 +6293 /// Attempts to continue a nat traversal round by trying to open paths for pending client probes. +6294 /// +6295 /// If there was nothing to do, it returns `None`. Otherwise it returns whether the path was +6296 /// successfully open. +6297 fn continue_nat_traversal_round(&mut self, now: Instant) -> Option<bool> { +6298 let client_state = self.iroh_hp.client_side_mut().ok()?; +6299 let (id, address) = client_state.continue_nat_traversal_round()?; +6300 let ipv6 = self +6301 .paths +6302 .values() +6303 .any(|p| p.data.network_path.remote.is_ipv6()); +6304 let open_result = self.open_nat_traversal_path(now, address, ipv6); +6305 let client_state = self.iroh_hp.client_side_mut().expect("validated"); +6306 match open_result { +6307 Ok(None) => Some(true), +6308 Ok(Some((path_id, _remote, path_was_known))) => { +6309 if !path_was_known { +6310 client_state.add_round_path_id(path_id); +6311 } +6312 Some(true) +6313 } +6314 Err(e) => { +6315 client_state.report_in_continuation(id, e); +6316 Some(false) +6317 } +6318 } +6319 } +6320} +6321 +6322impl fmt::Debug for Connection { +6323 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +6324 f.debug_struct("Connection") +6325 .field("handshake_cid", &self.handshake_cid) +6326 .finish() +6327 } +6328} +6329 +6330#[derive(Debug, Copy, Clone, PartialEq, Eq)] +6331enum PathBlocked { +6332 No, +6333 AntiAmplification, +6334 Congestion, +6335 Pacing, +6336} +6337 +6338/// Fields of `Connection` specific to it being client-side or server-side +6339enum ConnectionSide { +6340 Client { +6341 /// Sent in every outgoing Initial packet. Always empty after Initial keys are discarded +6342 token: Bytes, +6343 token_store: Arc<dyn TokenStore>, +6344 server_name: String, +6345 }, +6346 Server { +6347 server_config: Arc<ServerConfig>, +6348 }, +6349} +6350 +6351impl ConnectionSide { +6352 fn remote_may_migrate(&self, state: &State) -> bool { +6353 match self { +6354 Self::Server { server_config } => server_config.migration, +6355 Self::Client { .. } => { +6356 if let Some(hs) = state.as_handshake() { +6357 hs.allow_server_migration +6358 } else { +6359 false +6360 } +6361 } +6362 } +6363 } 6364 -6365impl ConnectionSide { -6366 fn remote_may_migrate(&self, state: &State) -> bool { -6367 match self { -6368 Self::Server { server_config } => server_config.migration, -6369 Self::Client { .. } => { -6370 if let Some(hs) = state.as_handshake() { -6371 hs.allow_server_migration -6372 } else { -6373 false -6374 } -6375 } -6376 } -6377 } -6378 -6379 fn is_client(&self) -> bool { -6380 self.side().is_client() -6381 } -6382 -6383 fn is_server(&self) -> bool { -6384 self.side().is_server() -6385 } -6386 -6387 fn side(&self) -> Side { -6388 match *self { -6389 Self::Client { .. } => Side::Client, -6390 Self::Server { .. } => Side::Server, -6391 } -6392 } -6393} -6394 -6395impl From<SideArgs> for ConnectionSide { -6396 fn from(side: SideArgs) -> Self { -6397 match side { -6398 SideArgs::Client { -6399 token_store, -6400 server_name, -6401 } => Self::Client { -6402 token: token_store.take(&server_name).unwrap_or_default(), -6403 token_store, -6404 server_name, -6405 }, -6406 SideArgs::Server { -6407 server_config, -6408 pref_addr_cid: _, -6409 path_validated: _, -6410 } => Self::Server { server_config }, -6411 } -6412 } -6413} -6414 -6415/// Parameters to `Connection::new` specific to it being client-side or server-side -6416pub(crate) enum SideArgs { -6417 Client { -6418 token_store: Arc<dyn TokenStore>, -6419 server_name: String, -6420 }, -6421 Server { -6422 server_config: Arc<ServerConfig>, -6423 pref_addr_cid: Option<ConnectionId>, -6424 path_validated: bool, -6425 }, -6426} -6427 -6428impl SideArgs { -6429 pub(crate) fn pref_addr_cid(&self) -> Option<ConnectionId> { +6365 fn is_client(&self) -> bool { +6366 self.side().is_client() +6367 } +6368 +6369 fn is_server(&self) -> bool { +6370 self.side().is_server() +6371 } +6372 +6373 fn side(&self) -> Side { +6374 match *self { +6375 Self::Client { .. } => Side::Client, +6376 Self::Server { .. } => Side::Server, +6377 } +6378 } +6379} +6380 +6381impl From<SideArgs> for ConnectionSide { +6382 fn from(side: SideArgs) -> Self { +6383 match side { +6384 SideArgs::Client { +6385 token_store, +6386 server_name, +6387 } => Self::Client { +6388 token: token_store.take(&server_name).unwrap_or_default(), +6389 token_store, +6390 server_name, +6391 }, +6392 SideArgs::Server { +6393 server_config, +6394 pref_addr_cid: _, +6395 path_validated: _, +6396 } => Self::Server { server_config }, +6397 } +6398 } +6399} +6400 +6401/// Parameters to `Connection::new` specific to it being client-side or server-side +6402pub(crate) enum SideArgs { +6403 Client { +6404 token_store: Arc<dyn TokenStore>, +6405 server_name: String, +6406 }, +6407 Server { +6408 server_config: Arc<ServerConfig>, +6409 pref_addr_cid: Option<ConnectionId>, +6410 path_validated: bool, +6411 }, +6412} +6413 +6414impl SideArgs { +6415 pub(crate) fn pref_addr_cid(&self) -> Option<ConnectionId> { +6416 match *self { +6417 Self::Client { .. } => None, +6418 Self::Server { pref_addr_cid, .. } => pref_addr_cid, +6419 } +6420 } +6421 +6422 pub(crate) fn path_validated(&self) -> bool { +6423 match *self { +6424 Self::Client { .. } => true, +6425 Self::Server { path_validated, .. } => path_validated, +6426 } +6427 } +6428 +6429 pub(crate) fn side(&self) -> Side { 6430 match *self { -6431 Self::Client { .. } => None, -6432 Self::Server { pref_addr_cid, .. } => pref_addr_cid, +6431 Self::Client { .. } => Side::Client, +6432 Self::Server { .. } => Side::Server, 6433 } 6434 } -6435 -6436 pub(crate) fn path_validated(&self) -> bool { -6437 match *self { -6438 Self::Client { .. } => true, -6439 Self::Server { path_validated, .. } => path_validated, -6440 } -6441 } -6442 -6443 pub(crate) fn side(&self) -> Side { -6444 match *self { -6445 Self::Client { .. } => Side::Client, -6446 Self::Server { .. } => Side::Server, -6447 } -6448 } -6449} -6450 -6451/// Reasons why a connection might be lost -6452#[derive(Debug, Error, Clone, PartialEq, Eq)] -6453pub enum ConnectionError { -6454 /// The peer doesn't implement any supported version -6455 #[error("peer doesn't implement any supported version")] -6456 VersionMismatch, -6457 /// The peer violated the QUIC specification as understood by this implementation -6458 #[error(transparent)] -6459 TransportError(#[from] TransportError), -6460 /// The peer's QUIC stack aborted the connection automatically -6461 #[error("aborted by peer: {0}")] -6462 ConnectionClosed(frame::ConnectionClose), -6463 /// The peer closed the connection -6464 #[error("closed by peer: {0}")] -6465 ApplicationClosed(frame::ApplicationClose), -6466 /// The peer is unable to continue processing this connection, usually due to having restarted -6467 #[error("reset by peer")] -6468 Reset, -6469 /// Communication with the peer has lapsed for longer than the negotiated idle timeout -6470 /// -6471 /// If neither side is sending keep-alives, a connection will time out after a long enough idle -6472 /// period even if the peer is still reachable. See also [`TransportConfig::max_idle_timeout()`] -6473 /// and [`TransportConfig::keep_alive_interval()`]. -6474 #[error("timed out")] -6475 TimedOut, -6476 /// The local application closed the connection -6477 #[error("closed")] -6478 LocallyClosed, -6479 /// The connection could not be created because not enough of the CID space is available -6480 /// -6481 /// Try using longer connection IDs. -6482 #[error("CIDs exhausted")] -6483 CidsExhausted, -6484} -6485 -6486impl From<Close> for ConnectionError { -6487 fn from(x: Close) -> Self { -6488 match x { -6489 Close::Connection(reason) => Self::ConnectionClosed(reason), -6490 Close::Application(reason) => Self::ApplicationClosed(reason), -6491 } -6492 } -6493} -6494 -6495// For compatibility with API consumers -6496impl From<ConnectionError> for io::Error { -6497 fn from(x: ConnectionError) -> Self { -6498 use ConnectionError::*; -6499 let kind = match x { -6500 TimedOut => io::ErrorKind::TimedOut, -6501 Reset => io::ErrorKind::ConnectionReset, -6502 ApplicationClosed(_) | ConnectionClosed(_) => io::ErrorKind::ConnectionAborted, -6503 TransportError(_) | VersionMismatch | LocallyClosed | CidsExhausted => { -6504 io::ErrorKind::Other -6505 } -6506 }; -6507 Self::new(kind, x) -6508 } -6509} -6510 -6511/// Errors that might trigger a path being closed -6512// TODO(@divma): maybe needs to be reworked based on what we want to do with the public API -6513#[derive(Debug, Error, PartialEq, Eq, Clone, Copy)] -6514pub enum PathError { -6515 /// The extension was not negotiated with the peer -6516 #[error("multipath extension not negotiated")] -6517 MultipathNotNegotiated, -6518 /// Paths can only be opened client-side -6519 #[error("the server side may not open a path")] -6520 ServerSideNotAllowed, -6521 /// Current limits do not allow us to open more paths -6522 #[error("maximum number of concurrent paths reached")] -6523 MaxPathIdReached, -6524 /// No remote CIDs available to open a new path -6525 #[error("remoted CIDs exhausted")] -6526 RemoteCidsExhausted, -6527 /// Path could not be validated and will be abandoned -6528 #[error("path validation failed")] -6529 ValidationFailed, -6530 /// The remote address for the path is not supported by the endpoint -6531 #[error("invalid remote address")] -6532 InvalidRemoteAddress(SocketAddr), -6533} -6534 -6535/// Errors triggered when abandoning a path -6536#[derive(Debug, Error, Clone, Eq, PartialEq)] -6537pub enum ClosePathError { -6538 /// The path is already closed or was never opened -6539 #[error("closed path")] -6540 ClosedPath, -6541 /// This is the last path, which can not be abandoned -6542 #[error("last open path")] -6543 LastOpenPath, -6544} -6545 -6546#[derive(Debug, Error, Clone, Copy)] -6547#[error("Multipath extension not negotiated")] -6548pub struct MultipathNotNegotiated { -6549 _private: (), -6550} -6551 -6552/// Events of interest to the application -6553#[derive(Debug)] -6554pub enum Event { -6555 /// The connection's handshake data is ready -6556 HandshakeDataReady, -6557 /// The connection was successfully established -6558 Connected, -6559 /// The TLS handshake was confirmed -6560 HandshakeConfirmed, -6561 /// The connection was lost -6562 /// -6563 /// Emitted if the peer closes the connection or an error is encountered. -6564 ConnectionLost { -6565 /// Reason that the connection was closed -6566 reason: ConnectionError, -6567 }, -6568 /// Stream events -6569 Stream(StreamEvent), -6570 /// One or more application datagrams have been received -6571 DatagramReceived, -6572 /// One or more application datagrams have been sent after blocking -6573 DatagramsUnblocked, -6574 /// (Multi)Path events -6575 Path(PathEvent), -6576 /// Iroh's nat traversal events -6577 NatTraversal(iroh_hp::Event), -6578} -6579 -6580impl From<PathEvent> for Event { -6581 fn from(source: PathEvent) -> Self { -6582 Self::Path(source) -6583 } -6584} -6585 -6586fn get_max_ack_delay(params: &TransportParameters) -> Duration { -6587 Duration::from_micros(params.max_ack_delay.0 * 1000) -6588} -6589 -6590// Prevents overflow and improves behavior in extreme circumstances -6591const MAX_BACKOFF_EXPONENT: u32 = 16; -6592 -6593/// Minimal remaining size to allow packet coalescing, excluding cryptographic tag -6594/// -6595/// This must be at least as large as the header for a well-formed empty packet to be coalesced, -6596/// plus some space for frames. We only care about handshake headers because short header packets -6597/// necessarily have smaller headers, and initial packets are only ever the first packet in a -6598/// datagram (because we coalesce in ascending packet space order and the only reason to split a -6599/// packet is when packet space changes). -6600const MIN_PACKET_SPACE: usize = MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE + 32; -6601 -6602/// Largest amount of space that could be occupied by a Handshake or 0-RTT packet's header -6603/// -6604/// Excludes packet-type-specific fields such as packet number or Initial token -6605// https://www.rfc-editor.org/rfc/rfc9000.html#name-0-rtt: flags + version + dcid len + dcid + -6606// scid len + scid + length + pn -6607const MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE: usize = -6608 1 + 4 + 1 + MAX_CID_SIZE + 1 + MAX_CID_SIZE + VarInt::from_u32(u16::MAX as u32).size() + 4; -6609 -6610/// Perform key updates this many packets before the AEAD confidentiality limit. -6611/// -6612/// Chosen arbitrarily, intended to be large enough to prevent spurious connection loss. -6613const KEY_UPDATE_MARGIN: u64 = 10_000; -6614 -6615#[derive(Default)] -6616struct SentFrames { -6617 retransmits: ThinRetransmits, -6618 /// The packet number of the largest acknowledged packet for each path -6619 largest_acked: FxHashMap<PathId, u64>, -6620 stream_frames: StreamMetaVec, -6621 /// Whether the packet contains non-retransmittable frames (like datagrams) -6622 non_retransmits: bool, -6623 /// If the datagram containing these frames should be padded to the min MTU -6624 requires_padding: bool, -6625} -6626 -6627impl SentFrames { -6628 /// Returns whether the packet contains only ACKs -6629 fn is_ack_only(&self, streams: &StreamsState) -> bool { -6630 !self.largest_acked.is_empty() -6631 && !self.non_retransmits -6632 && self.stream_frames.is_empty() -6633 && self.retransmits.is_empty(streams) -6634 } -6635 -6636 fn retransmits_mut(&mut self) -> &mut Retransmits { -6637 self.retransmits.get_or_create() -6638 } -6639 -6640 fn sent(&mut self, frame: frame::EncodableFrame<'_>) { -6641 use frame::EncodableFrame::*; -6642 match frame { -6643 PathAck(path_ack_encoder) => { -6644 if let Some(max) = path_ack_encoder.ranges.max() { -6645 self.largest_acked.insert(path_ack_encoder.path_id, max); -6646 } -6647 } -6648 Ack(ack_encoder) => { -6649 if let Some(max) = ack_encoder.ranges.max() { -6650 self.largest_acked.insert(PathId::ZERO, max); -6651 } -6652 } -6653 Close(_) => { -6654 // TODO(@divma): why not set non_ -6655 } -6656 PathResponse(_) => self.non_retransmits = true, -6657 HandshakeDone(_) => self.retransmits_mut().handshake_done = true, -6658 ReachOut(reach_out) => self -6659 .retransmits_mut() -6660 .reach_out -6661 .get_or_insert_with(|| (reach_out.round, Default::default())) -6662 .1 -6663 .push((reach_out.ip, reach_out.port)), -6664 ObservedAddr(_) => self.retransmits_mut().observed_addr = true, -6665 Ping(_) => self.non_retransmits = true, -6666 ImmediateAck(_) => self.non_retransmits = true, -6667 AckFrequency(_) => self.retransmits_mut().ack_frequency = true, -6668 PathChallenge(_) => self.non_retransmits = true, -6669 Crypto(crypto) => self.retransmits_mut().crypto.push_back(crypto.clone()), -6670 PathAbandon(path_abandon) => { -6671 self.retransmits_mut() -6672 .path_abandon -6673 .entry(path_abandon.path_id) -6674 .or_insert(path_abandon.error_code); -6675 } -6676 PathStatusAvailable(frame::PathStatusAvailable { path_id, .. }) -6677 | PathStatusBackup(frame::PathStatusBackup { path_id, .. }) => { -6678 self.retransmits_mut().path_status.insert(path_id); -6679 } -6680 MaxPathId(_) => self.retransmits_mut().max_path_id = true, -6681 PathsBlocked(_) => self.retransmits_mut().paths_blocked = true, -6682 PathCidsBlocked(path_cids_blocked) => self -6683 .retransmits_mut() -6684 .path_cids_blocked -6685 .push(path_cids_blocked.path_id), -6686 ResetStream(reset) => self -6687 .retransmits_mut() -6688 .reset_stream -6689 .push((reset.id, reset.error_code)), -6690 StopSending(stop_sending) => self.retransmits_mut().stop_sending.push(stop_sending), -6691 NewConnectionId(new_cid) => self.retransmits_mut().new_cids.push(new_cid.issued()), -6692 RetireConnectionId(retire_cid) => self -6693 .retransmits_mut() -6694 .retire_cids -6695 .push((retire_cid.path_id.unwrap_or_default(), retire_cid.sequence)), -6696 Datagram(_) => self.non_retransmits = true, -6697 NewToken(_) => {} -6698 AddAddress(add_address) => { -6699 self.retransmits_mut().add_address.insert(add_address); -6700 } -6701 RemoveAddress(remove_address) => { -6702 self.retransmits_mut().remove_address.insert(remove_address); -6703 } -6704 StreamMeta(stream_meta_encoder) => self.stream_frames.push(stream_meta_encoder.meta), -6705 MaxData(_) => self.retransmits_mut().max_data = true, -6706 MaxStreamData(max) => { -6707 self.retransmits_mut().max_stream_data.insert(max.id); -6708 } -6709 MaxStreams(max_streams) => { -6710 self.retransmits_mut().max_stream_id[max_streams.dir as usize] = true -6711 } -6712 } +6435} +6436 +6437/// Reasons why a connection might be lost +6438#[derive(Debug, Error, Clone, PartialEq, Eq)] +6439pub enum ConnectionError { +6440 /// The peer doesn't implement any supported version +6441 #[error("peer doesn't implement any supported version")] +6442 VersionMismatch, +6443 /// The peer violated the QUIC specification as understood by this implementation +6444 #[error(transparent)] +6445 TransportError(#[from] TransportError), +6446 /// The peer's QUIC stack aborted the connection automatically +6447 #[error("aborted by peer: {0}")] +6448 ConnectionClosed(frame::ConnectionClose), +6449 /// The peer closed the connection +6450 #[error("closed by peer: {0}")] +6451 ApplicationClosed(frame::ApplicationClose), +6452 /// The peer is unable to continue processing this connection, usually due to having restarted +6453 #[error("reset by peer")] +6454 Reset, +6455 /// Communication with the peer has lapsed for longer than the negotiated idle timeout +6456 /// +6457 /// If neither side is sending keep-alives, a connection will time out after a long enough idle +6458 /// period even if the peer is still reachable. See also [`TransportConfig::max_idle_timeout()`] +6459 /// and [`TransportConfig::keep_alive_interval()`]. +6460 #[error("timed out")] +6461 TimedOut, +6462 /// The local application closed the connection +6463 #[error("closed")] +6464 LocallyClosed, +6465 /// The connection could not be created because not enough of the CID space is available +6466 /// +6467 /// Try using longer connection IDs. +6468 #[error("CIDs exhausted")] +6469 CidsExhausted, +6470} +6471 +6472impl From<Close> for ConnectionError { +6473 fn from(x: Close) -> Self { +6474 match x { +6475 Close::Connection(reason) => Self::ConnectionClosed(reason), +6476 Close::Application(reason) => Self::ApplicationClosed(reason), +6477 } +6478 } +6479} +6480 +6481// For compatibility with API consumers +6482impl From<ConnectionError> for io::Error { +6483 fn from(x: ConnectionError) -> Self { +6484 use ConnectionError::*; +6485 let kind = match x { +6486 TimedOut => io::ErrorKind::TimedOut, +6487 Reset => io::ErrorKind::ConnectionReset, +6488 ApplicationClosed(_) | ConnectionClosed(_) => io::ErrorKind::ConnectionAborted, +6489 TransportError(_) | VersionMismatch | LocallyClosed | CidsExhausted => { +6490 io::ErrorKind::Other +6491 } +6492 }; +6493 Self::new(kind, x) +6494 } +6495} +6496 +6497/// Errors that might trigger a path being closed +6498// TODO(@divma): maybe needs to be reworked based on what we want to do with the public API +6499#[derive(Debug, Error, PartialEq, Eq, Clone, Copy)] +6500pub enum PathError { +6501 /// The extension was not negotiated with the peer +6502 #[error("multipath extension not negotiated")] +6503 MultipathNotNegotiated, +6504 /// Paths can only be opened client-side +6505 #[error("the server side may not open a path")] +6506 ServerSideNotAllowed, +6507 /// Current limits do not allow us to open more paths +6508 #[error("maximum number of concurrent paths reached")] +6509 MaxPathIdReached, +6510 /// No remote CIDs available to open a new path +6511 #[error("remoted CIDs exhausted")] +6512 RemoteCidsExhausted, +6513 /// Path could not be validated and will be abandoned +6514 #[error("path validation failed")] +6515 ValidationFailed, +6516 /// The remote address for the path is not supported by the endpoint +6517 #[error("invalid remote address")] +6518 InvalidRemoteAddress(SocketAddr), +6519} +6520 +6521/// Errors triggered when abandoning a path +6522#[derive(Debug, Error, Clone, Eq, PartialEq)] +6523pub enum ClosePathError { +6524 /// The path is already closed or was never opened +6525 #[error("closed path")] +6526 ClosedPath, +6527 /// This is the last path, which can not be abandoned +6528 #[error("last open path")] +6529 LastOpenPath, +6530} +6531 +6532#[derive(Debug, Error, Clone, Copy)] +6533#[error("Multipath extension not negotiated")] +6534pub struct MultipathNotNegotiated { +6535 _private: (), +6536} +6537 +6538/// Events of interest to the application +6539#[derive(Debug)] +6540pub enum Event { +6541 /// The connection's handshake data is ready +6542 HandshakeDataReady, +6543 /// The connection was successfully established +6544 Connected, +6545 /// The TLS handshake was confirmed +6546 HandshakeConfirmed, +6547 /// The connection was lost +6548 /// +6549 /// Emitted if the peer closes the connection or an error is encountered. +6550 ConnectionLost { +6551 /// Reason that the connection was closed +6552 reason: ConnectionError, +6553 }, +6554 /// Stream events +6555 Stream(StreamEvent), +6556 /// One or more application datagrams have been received +6557 DatagramReceived, +6558 /// One or more application datagrams have been sent after blocking +6559 DatagramsUnblocked, +6560 /// (Multi)Path events +6561 Path(PathEvent), +6562 /// Iroh's nat traversal events +6563 NatTraversal(iroh_hp::Event), +6564} +6565 +6566impl From<PathEvent> for Event { +6567 fn from(source: PathEvent) -> Self { +6568 Self::Path(source) +6569 } +6570} +6571 +6572fn get_max_ack_delay(params: &TransportParameters) -> Duration { +6573 Duration::from_micros(params.max_ack_delay.0 * 1000) +6574} +6575 +6576// Prevents overflow and improves behavior in extreme circumstances +6577const MAX_BACKOFF_EXPONENT: u32 = 16; +6578 +6579/// Minimal remaining size to allow packet coalescing, excluding cryptographic tag +6580/// +6581/// This must be at least as large as the header for a well-formed empty packet to be coalesced, +6582/// plus some space for frames. We only care about handshake headers because short header packets +6583/// necessarily have smaller headers, and initial packets are only ever the first packet in a +6584/// datagram (because we coalesce in ascending packet space order and the only reason to split a +6585/// packet is when packet space changes). +6586const MIN_PACKET_SPACE: usize = MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE + 32; +6587 +6588/// Largest amount of space that could be occupied by a Handshake or 0-RTT packet's header +6589/// +6590/// Excludes packet-type-specific fields such as packet number or Initial token +6591// https://www.rfc-editor.org/rfc/rfc9000.html#name-0-rtt: flags + version + dcid len + dcid + +6592// scid len + scid + length + pn +6593const MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE: usize = +6594 1 + 4 + 1 + MAX_CID_SIZE + 1 + MAX_CID_SIZE + VarInt::from_u32(u16::MAX as u32).size() + 4; +6595 +6596/// Perform key updates this many packets before the AEAD confidentiality limit. +6597/// +6598/// Chosen arbitrarily, intended to be large enough to prevent spurious connection loss. +6599const KEY_UPDATE_MARGIN: u64 = 10_000; +6600 +6601#[derive(Default)] +6602struct SentFrames { +6603 retransmits: ThinRetransmits, +6604 /// The packet number of the largest acknowledged packet for each path +6605 largest_acked: FxHashMap<PathId, u64>, +6606 stream_frames: StreamMetaVec, +6607 /// Whether the packet contains non-retransmittable frames (like datagrams) +6608 non_retransmits: bool, +6609 /// If the datagram containing these frames should be padded to the min MTU +6610 requires_padding: bool, +6611} +6612 +6613impl SentFrames { +6614 /// Returns whether the packet contains only ACKs +6615 fn is_ack_only(&self, streams: &StreamsState) -> bool { +6616 !self.largest_acked.is_empty() +6617 && !self.non_retransmits +6618 && self.stream_frames.is_empty() +6619 && self.retransmits.is_empty(streams) +6620 } +6621 +6622 fn retransmits_mut(&mut self) -> &mut Retransmits { +6623 self.retransmits.get_or_create() +6624 } +6625 +6626 fn sent(&mut self, frame: frame::EncodableFrame<'_>) { +6627 use frame::EncodableFrame::*; +6628 match frame { +6629 PathAck(path_ack_encoder) => { +6630 if let Some(max) = path_ack_encoder.ranges.max() { +6631 self.largest_acked.insert(path_ack_encoder.path_id, max); +6632 } +6633 } +6634 Ack(ack_encoder) => { +6635 if let Some(max) = ack_encoder.ranges.max() { +6636 self.largest_acked.insert(PathId::ZERO, max); +6637 } +6638 } +6639 Close(_) => { /* non retransmittable, but after this we don't really care */ } +6640 PathResponse(_) => self.non_retransmits = true, +6641 HandshakeDone(_) => self.retransmits_mut().handshake_done = true, +6642 ReachOut(frame::ReachOut { round, ip, port }) => self +6643 .retransmits_mut() +6644 .reach_out +6645 .get_or_insert_with(|| (round, Vec::new())) +6646 .1 +6647 .push((ip, port)), +6648 ObservedAddr(_) => self.retransmits_mut().observed_addr = true, +6649 Ping(_) => self.non_retransmits = true, +6650 ImmediateAck(_) => self.non_retransmits = true, +6651 AckFrequency(_) => self.retransmits_mut().ack_frequency = true, +6652 PathChallenge(_) => self.non_retransmits = true, +6653 Crypto(crypto) => self.retransmits_mut().crypto.push_back(crypto), +6654 PathAbandon(path_abandon) => { +6655 self.retransmits_mut() +6656 .path_abandon +6657 .entry(path_abandon.path_id) +6658 .or_insert(path_abandon.error_code); +6659 } +6660 PathStatusAvailable(frame::PathStatusAvailable { path_id, .. }) +6661 | PathStatusBackup(frame::PathStatusBackup { path_id, .. }) => { +6662 self.retransmits_mut().path_status.insert(path_id); +6663 } +6664 MaxPathId(_) => self.retransmits_mut().max_path_id = true, +6665 PathsBlocked(_) => self.retransmits_mut().paths_blocked = true, +6666 PathCidsBlocked(path_cids_blocked) => self +6667 .retransmits_mut() +6668 .path_cids_blocked +6669 .push(path_cids_blocked.path_id), +6670 ResetStream(reset) => self +6671 .retransmits_mut() +6672 .reset_stream +6673 .push((reset.id, reset.error_code)), +6674 StopSending(stop_sending) => self.retransmits_mut().stop_sending.push(stop_sending), +6675 NewConnectionId(new_cid) => self.retransmits_mut().new_cids.push(new_cid.issued()), +6676 RetireConnectionId(retire_cid) => self +6677 .retransmits_mut() +6678 .retire_cids +6679 .push((retire_cid.path_id.unwrap_or_default(), retire_cid.sequence)), +6680 Datagram(_) => self.non_retransmits = true, +6681 NewToken(_) => {} +6682 AddAddress(add_address) => { +6683 self.retransmits_mut().add_address.insert(add_address); +6684 } +6685 RemoveAddress(remove_address) => { +6686 self.retransmits_mut().remove_address.insert(remove_address); +6687 } +6688 StreamMeta(stream_meta_encoder) => self.stream_frames.push(stream_meta_encoder.meta), +6689 MaxData(_) => self.retransmits_mut().max_data = true, +6690 MaxStreamData(max) => { +6691 self.retransmits_mut().max_stream_data.insert(max.id); +6692 } +6693 MaxStreams(max_streams) => { +6694 self.retransmits_mut().max_stream_id[max_streams.dir as usize] = true +6695 } +6696 } +6697 } +6698} +6699 +6700/// Compute the negotiated idle timeout based on local and remote max_idle_timeout transport parameters. +6701/// +6702/// 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.> +6703/// +6704/// 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.> +6705/// +6706/// Returns the negotiated idle timeout as a `Duration`, or `None` when both endpoints have opted out of idle timeout. +6707fn negotiate_max_idle_timeout(x: Option<VarInt>, y: Option<VarInt>) -> Option<Duration> { +6708 match (x, y) { +6709 (Some(VarInt(0)) | None, Some(VarInt(0)) | None) => None, +6710 (Some(VarInt(0)) | None, Some(y)) => Some(Duration::from_millis(y.0)), +6711 (Some(x), Some(VarInt(0)) | None) => Some(Duration::from_millis(x.0)), +6712 (Some(x), Some(y)) => Some(Duration::from_millis(cmp::min(x, y).0)), 6713 } 6714} 6715 -6716/// Compute the negotiated idle timeout based on local and remote max_idle_timeout transport parameters. -6717/// -6718/// 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.> -6719/// -6720/// 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.> -6721/// -6722/// Returns the negotiated idle timeout as a `Duration`, or `None` when both endpoints have opted out of idle timeout. -6723fn negotiate_max_idle_timeout(x: Option<VarInt>, y: Option<VarInt>) -> Option<Duration> { -6724 match (x, y) { -6725 (Some(VarInt(0)) | None, Some(VarInt(0)) | None) => None, -6726 (Some(VarInt(0)) | None, Some(y)) => Some(Duration::from_millis(y.0)), -6727 (Some(x), Some(VarInt(0)) | None) => Some(Duration::from_millis(x.0)), -6728 (Some(x), Some(y)) => Some(Duration::from_millis(cmp::min(x, y).0)), -6729 } -6730} -6731 -6732#[cfg(test)] -6733mod tests { -6734 use super::*; -6735 -6736 #[test] -6737 fn negotiate_max_idle_timeout_commutative() { -6738 let test_params = [ -6739 (None, None, None), -6740 (None, Some(VarInt(0)), None), -6741 (None, Some(VarInt(2)), Some(Duration::from_millis(2))), -6742 (Some(VarInt(0)), Some(VarInt(0)), None), -6743 ( -6744 Some(VarInt(2)), -6745 Some(VarInt(0)), -6746 Some(Duration::from_millis(2)), -6747 ), -6748 ( -6749 Some(VarInt(1)), -6750 Some(VarInt(4)), -6751 Some(Duration::from_millis(1)), -6752 ), -6753 ]; -6754 -6755 for (left, right, result) in test_params { -6756 assert_eq!(negotiate_max_idle_timeout(left, right), result); -6757 assert_eq!(negotiate_max_idle_timeout(right, left), result); -6758 } -6759 } -6760} \ No newline at end of file +6716#[cfg(test)] +6717mod tests { +6718 use super::*; +6719 +6720 #[test] +6721 fn negotiate_max_idle_timeout_commutative() { +6722 let test_params = [ +6723 (None, None, None), +6724 (None, Some(VarInt(0)), None), +6725 (None, Some(VarInt(2)), Some(Duration::from_millis(2))), +6726 (Some(VarInt(0)), Some(VarInt(0)), None), +6727 ( +6728 Some(VarInt(2)), +6729 Some(VarInt(0)), +6730 Some(Duration::from_millis(2)), +6731 ), +6732 ( +6733 Some(VarInt(1)), +6734 Some(VarInt(4)), +6735 Some(Duration::from_millis(1)), +6736 ), +6737 ]; +6738 +6739 for (left, right, result) in test_params { +6740 assert_eq!(negotiate_max_idle_timeout(left, right), result); +6741 assert_eq!(negotiate_max_idle_timeout(right, left), result); +6742 } +6743 } +6744}
\ No newline at end of file diff --git a/pr/295/docs/src/iroh_quinn_proto/connection/packet_builder.rs.html b/pr/295/docs/src/iroh_quinn_proto/connection/packet_builder.rs.html index ef7d4a10e..04c4396af 100644 --- a/pr/295/docs/src/iroh_quinn_proto/connection/packet_builder.rs.html +++ b/pr/295/docs/src/iroh_quinn_proto/connection/packet_builder.rs.html @@ -224,11 +224,11 @@ 224 225 pub(super) fn encode<'c>( 226 &mut self, -227 frame: impl Into<EncodableFrame<'c>>, +227 frame: impl Into<EncodableFrame<'c>> + Encodable, 228 stats: &mut FrameStats, 229 ) { -230 let frame = frame.into(); -231 frame.encode(&mut self.frame_space_mut()); +230 frame.encode(&mut self.frame_space_mut()); +231 let frame = frame.into(); 232 stats.record(frame.get_type()); 233 self.qlog.record(&frame); 234 self.sent_frames.sent(frame); diff --git a/pr/295/docs/src/iroh_quinn_proto/frame.rs.html b/pr/295/docs/src/iroh_quinn_proto/frame.rs.html index 4449d3662..74cca39ca 100644 --- a/pr/295/docs/src/iroh_quinn_proto/frame.rs.html +++ b/pr/295/docs/src/iroh_quinn_proto/frame.rs.html @@ -244,2299 +244,2262 @@ 244 } 245} 246 -247impl<'a> Encodable for EncodableFrame<'a> { -248 fn encode<B: BufMut>(&self, buf: &mut B) { -249 match self { -250 EncodableFrame::PathAck(path_ack_encoder) => path_ack_encoder.encode(buf), -251 EncodableFrame::Ack(ack_encoder) => ack_encoder.encode(buf), -252 EncodableFrame::Close(close_encoder) => close_encoder.encode(buf), -253 EncodableFrame::PathResponse(path_response) => path_response.encode(buf), -254 EncodableFrame::HandshakeDone(handshake_done) => handshake_done.encode(buf), -255 EncodableFrame::ReachOut(reach_out) => reach_out.encode(buf), -256 EncodableFrame::ObservedAddr(observed_addr) => observed_addr.encode(buf), -257 EncodableFrame::Ping(ping) => ping.encode(buf), -258 EncodableFrame::ImmediateAck(immediate_ack) => immediate_ack.encode(buf), -259 EncodableFrame::AckFrequency(ack_frequency) => ack_frequency.encode(buf), -260 EncodableFrame::PathChallenge(path_challenge) => path_challenge.encode(buf), -261 EncodableFrame::Crypto(crypto) => crypto.encode(buf), -262 EncodableFrame::PathAbandon(path_abandon) => path_abandon.encode(buf), -263 EncodableFrame::PathStatusAvailable(path_status) => path_status.encode(buf), -264 EncodableFrame::PathStatusBackup(path_status) => path_status.encode(buf), -265 EncodableFrame::MaxPathId(max_path_id) => max_path_id.encode(buf), -266 EncodableFrame::PathsBlocked(paths_blocked) => paths_blocked.encode(buf), -267 EncodableFrame::PathCidsBlocked(path_cids_blocked) => path_cids_blocked.encode(buf), -268 EncodableFrame::ResetStream(reset_stream) => reset_stream.encode(buf), -269 EncodableFrame::StopSending(stop_sending) => stop_sending.encode(buf), -270 EncodableFrame::NewConnectionId(new_connection_id) => new_connection_id.encode(buf), -271 EncodableFrame::RetireConnectionId(retire_cid) => retire_cid.encode(buf), -272 EncodableFrame::Datagram(datagram) => datagram.encode(buf), -273 EncodableFrame::NewToken(new_token) => new_token.encode(buf), -274 EncodableFrame::AddAddress(add_address) => add_address.encode(buf), -275 EncodableFrame::RemoveAddress(remove_address) => remove_address.encode(buf), -276 EncodableFrame::StreamMeta(stream_meta) => stream_meta.encode(buf), -277 EncodableFrame::MaxData(max_data) => max_data.encode(buf), -278 EncodableFrame::MaxStreamData(max_stream_data) => max_stream_data.encode(buf), -279 EncodableFrame::MaxStreams(max_streams) => max_streams.encode(buf), -280 } -281 } -282} -283 -284pub(crate) trait FrameStruct { -285 /// Smallest number of bytes this type of frame is guaranteed to fit within. -286 const SIZE_BOUND: usize; -287} -288 -289/// The type used to refer to [`FrameType`]s in closing and transport errors. -290#[derive(Copy, Clone, Eq, PartialEq, derive_more::Debug, derive_more::Display)] -291pub enum MaybeFrame { -292 /// Not attributed to any particular [`FrameType`]. -293 None, -294 /// Attributed to some frame type this implementation does not recognize. -295 #[display("UNKNOWN{:02x}", _0)] -296 #[debug("Unknown{:02x}", _0)] -297 Unknown(u64), -298 /// Attributed to a specific [`FrameType`], never [`FrameType::Padding`]. -299 Known(FrameType), -300} -301 -302impl MaybeFrame { -303 /// Encoded size of this [`MaybeFrame`]. -304 const fn size(&self) -> usize { -305 match self { -306 Self::None => VarInt(0).size(), -307 Self::Unknown(other) => VarInt(*other).size(), -308 Self::Known(frame_type) => frame_type.size(), -309 } -310 } -311} -312 -313impl Decodable for MaybeFrame { -314 fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> { -315 match FrameType::try_from(buf.get_var()?) { -316 Ok(FrameType::Padding) => Ok(Self::None), -317 Ok(other_frame) => Ok(Self::Known(other_frame)), -318 Err(InvalidFrameId(other)) => Ok(Self::Unknown(other)), -319 } -320 } -321} -322 -323impl Encodable for MaybeFrame { -324 fn encode<B: BufMut>(&self, buf: &mut B) { -325 match self { -326 Self::None => buf.write(0u64), -327 Self::Unknown(frame_id) => buf.write(*frame_id), -328 Self::Known(frame_type) => buf.write(*frame_type), -329 } -330 } -331} -332 -333pub(crate) struct HandshakeDone; -334 -335impl Encodable for HandshakeDone { -336 fn encode<B: BufMut>(&self, buf: &mut B) { -337 FrameType::HandshakeDone.encode(buf); +247pub(crate) trait FrameStruct { +248 /// Smallest number of bytes this type of frame is guaranteed to fit within. +249 const SIZE_BOUND: usize; +250} +251 +252/// The type used to refer to [`FrameType`]s in closing and transport errors. +253#[derive(Copy, Clone, Eq, PartialEq, derive_more::Debug, derive_more::Display)] +254pub enum MaybeFrame { +255 /// Not attributed to any particular [`FrameType`]. +256 None, +257 /// Attributed to some frame type this implementation does not recognize. +258 #[display("UNKNOWN{:02x}", _0)] +259 #[debug("Unknown{:02x}", _0)] +260 Unknown(u64), +261 /// Attributed to a specific [`FrameType`], never [`FrameType::Padding`]. +262 Known(FrameType), +263} +264 +265impl MaybeFrame { +266 /// Encoded size of this [`MaybeFrame`]. +267 const fn size(&self) -> usize { +268 match self { +269 Self::None => VarInt(0).size(), +270 Self::Unknown(other) => VarInt(*other).size(), +271 Self::Known(frame_type) => frame_type.size(), +272 } +273 } +274} +275 +276impl Decodable for MaybeFrame { +277 fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> { +278 match FrameType::try_from(buf.get_var()?) { +279 Ok(FrameType::Padding) => Ok(Self::None), +280 Ok(other_frame) => Ok(Self::Known(other_frame)), +281 Err(InvalidFrameId(other)) => Ok(Self::Unknown(other)), +282 } +283 } +284} +285 +286impl Encodable for MaybeFrame { +287 fn encode<B: BufMut>(&self, buf: &mut B) { +288 match self { +289 Self::None => buf.write(0u64), +290 Self::Unknown(frame_id) => buf.write(*frame_id), +291 Self::Known(frame_type) => buf.write(*frame_type), +292 } +293 } +294} +295 +296pub(crate) struct HandshakeDone; +297 +298impl Encodable for HandshakeDone { +299 fn encode<B: BufMut>(&self, buf: &mut B) { +300 FrameType::HandshakeDone.encode(buf); +301 } +302} +303 +304pub(crate) struct Ping; +305 +306impl Encodable for Ping { +307 fn encode<B: BufMut>(&self, buf: &mut B) { +308 FrameType::Ping.encode(buf); +309 } +310} +311 +312pub(crate) struct ImmediateAck; +313 +314impl Encodable for ImmediateAck { +315 fn encode<B: BufMut>(&self, buf: &mut B) { +316 FrameType::ImmediateAck.encode(buf); +317 } +318} +319 +320#[derive(Debug, Copy, Clone, Eq, PartialEq, derive_more::Display)] +321#[display("STREAM")] +322pub struct StreamInfo(u8); +323 +324impl StreamInfo { +325 const VALUES: RangeInclusive<u64> = RangeInclusive::new(0x08, 0x0f); +326 fn fin(self) -> bool { +327 self.0 & 0x01 != 0 +328 } +329 fn len(self) -> bool { +330 self.0 & 0x02 != 0 +331 } +332 fn off(self) -> bool { +333 self.0 & 0x04 != 0 +334 } +335 +336 const fn to_u64(self) -> u64 { +337 self.0 as u64 338 } 339} 340 -341pub(crate) struct Ping; -342 -343impl Encodable for Ping { -344 fn encode<B: BufMut>(&self, buf: &mut B) { -345 FrameType::Ping.encode(buf); -346 } -347} -348 -349pub(crate) struct ImmediateAck; -350 -351impl Encodable for ImmediateAck { -352 fn encode<B: BufMut>(&self, buf: &mut B) { -353 FrameType::ImmediateAck.encode(buf); +341#[derive(Debug, Copy, Clone, Eq, PartialEq, derive_more::Display)] +342#[display("DATAGRAM")] +343pub struct DatagramInfo(u8); +344 +345impl DatagramInfo { +346 const VALUES: RangeInclusive<u64> = RangeInclusive::new(0x30, 0x31); +347 +348 fn len(self) -> bool { +349 self.0 & 0x01 != 0 +350 } +351 +352 const fn to_u64(self) -> u64 { +353 self.0 as u64 354 } 355} 356 -357#[derive(Debug, Copy, Clone, Eq, PartialEq, derive_more::Display)] -358#[display("STREAM")] -359pub struct StreamInfo(u8); -360 -361impl StreamInfo { -362 const VALUES: RangeInclusive<u64> = RangeInclusive::new(0x08, 0x0f); -363 fn fin(self) -> bool { -364 self.0 & 0x01 != 0 -365 } -366 fn len(self) -> bool { -367 self.0 & 0x02 != 0 -368 } -369 fn off(self) -> bool { -370 self.0 & 0x04 != 0 -371 } -372 -373 const fn to_u64(self) -> u64 { -374 self.0 as u64 -375 } -376} -377 -378#[derive(Debug, Copy, Clone, Eq, PartialEq, derive_more::Display)] -379#[display("DATAGRAM")] -380pub struct DatagramInfo(u8); -381 -382impl DatagramInfo { -383 const VALUES: RangeInclusive<u64> = RangeInclusive::new(0x30, 0x31); -384 -385 fn len(self) -> bool { -386 self.0 & 0x01 != 0 -387 } -388 -389 const fn to_u64(self) -> u64 { -390 self.0 as u64 -391 } -392} -393 -394#[derive(Debug)] -395pub(crate) enum Frame { -396 Padding, -397 Ping, -398 Ack(Ack), -399 PathAck(PathAck), -400 ResetStream(ResetStream), -401 StopSending(StopSending), -402 Crypto(Crypto), -403 NewToken(NewToken), -404 Stream(Stream), -405 MaxData(MaxData), -406 MaxStreamData(MaxStreamData), -407 MaxStreams(MaxStreams), -408 DataBlocked { offset: u64 }, -409 StreamDataBlocked { id: StreamId, offset: u64 }, -410 StreamsBlocked { dir: Dir, limit: u64 }, -411 NewConnectionId(NewConnectionId), -412 RetireConnectionId(RetireConnectionId), -413 PathChallenge(PathChallenge), -414 PathResponse(PathResponse), -415 Close(Close), -416 Datagram(Datagram), -417 AckFrequency(AckFrequency), -418 ImmediateAck, -419 HandshakeDone, -420 ObservedAddr(ObservedAddr), -421 PathAbandon(PathAbandon), -422 PathStatusAvailable(PathStatusAvailable), -423 PathStatusBackup(PathStatusBackup), -424 MaxPathId(MaxPathId), -425 PathsBlocked(PathsBlocked), -426 PathCidsBlocked(PathCidsBlocked), -427 AddAddress(AddAddress), -428 ReachOut(ReachOut), -429 RemoveAddress(RemoveAddress), -430} -431 -432impl fmt::Display for Frame { -433 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -434 // Eventually all our frames will support fmt::Display and be able to be used to log -435 // consistently. For now we fall back to fmt::Debug. -436 match self { -437 Self::Padding => write!(f, "PADDING"), -438 Self::Ping => write!(f, "PING"), -439 Self::PathChallenge(frame) => write!(f, "{frame}"), -440 Self::PathResponse(frame) => write!(f, "{frame}"), -441 Self::ImmediateAck => write!(f, "IMMEDIATE_ACK"), -442 Self::HandshakeDone => write!(f, "HANDSHAKE_DONE"), -443 _ => write!(f, "{self:?}"), -444 } -445 } -446} -447 -448impl Frame { -449 pub(crate) fn ty(&self) -> FrameType { -450 use Frame::*; -451 match &self { -452 Padding => FrameType::Padding, -453 ResetStream(_) => FrameType::ResetStream, -454 Close(self::Close::Connection(_)) => FrameType::ConnectionClose, -455 Close(self::Close::Application(_)) => FrameType::ConnectionClose, -456 MaxData(_) => FrameType::MaxData, -457 MaxStreamData(_) => FrameType::MaxStreamData, -458 MaxStreams(max_streams) => max_streams.get_type(), -459 Ping => FrameType::Ping, -460 DataBlocked { .. } => FrameType::DataBlocked, -461 StreamDataBlocked { .. } => FrameType::StreamDataBlocked, -462 StreamsBlocked { dir: Dir::Bi, .. } => FrameType::StreamsBlockedBidi, -463 StreamsBlocked { dir: Dir::Uni, .. } => FrameType::StreamsBlockedUni, -464 StopSending { .. } => FrameType::StopSending, -465 RetireConnectionId(retire_frame) => retire_frame.get_type(), -466 Ack(ack) => ack.get_type(), -467 PathAck(path_ack) => path_ack.get_type(), -468 Stream(ref x) => { -469 let mut ty = *StreamInfo::VALUES.start() as u8; -470 if x.fin { -471 ty |= 0x01; -472 } -473 if x.offset != 0 { -474 ty |= 0x04; -475 } -476 // TODO(@divma): move all this to getframetype for Stream -477 FrameType::Stream(StreamInfo(ty)) -478 } -479 PathChallenge(_) => FrameType::PathChallenge, -480 PathResponse(_) => FrameType::PathResponse, -481 NewConnectionId(cid) => cid.get_type(), -482 Crypto(_) => FrameType::Crypto, -483 NewToken(_) => FrameType::NewToken, -484 Datagram(_) => FrameType::Datagram(DatagramInfo(*DatagramInfo::VALUES.start() as u8)), -485 AckFrequency(_) => FrameType::AckFrequency, -486 ImmediateAck => FrameType::ImmediateAck, -487 HandshakeDone => FrameType::HandshakeDone, -488 ObservedAddr(ref observed) => observed.get_type(), -489 PathAbandon(_) => FrameType::PathAbandon, -490 PathStatusAvailable(_) => FrameType::PathStatusAvailable, -491 PathStatusBackup(_) => FrameType::PathStatusBackup, -492 MaxPathId(_) => FrameType::MaxPathId, -493 PathsBlocked(_) => FrameType::PathsBlocked, -494 PathCidsBlocked(_) => FrameType::PathCidsBlocked, -495 AddAddress(ref frame) => frame.get_type(), -496 ReachOut(ref frame) => frame.get_type(), -497 RemoveAddress(_) => self::RemoveAddress::TYPE, -498 } -499 } -500 -501 pub(crate) fn is_ack_eliciting(&self) -> bool { -502 !matches!( -503 *self, -504 Self::Ack(_) | Self::PathAck(_) | Self::Padding | Self::Close(_) -505 ) -506 } -507 -508 /// Returns `true` if this frame MUST be sent in 1-RTT space -509 pub(crate) fn is_1rtt(&self) -> bool { -510 // See also https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-4-1: -511 // > All frames defined in this document MUST only be sent in 1-RTT packets. -512 // > If an endpoint receives a multipath-specific frame in a different packet type, it MUST close the -513 // > connection with an error of type PROTOCOL_VIOLATION. -514 -515 self.is_multipath_frame() || self.is_qad_frame() -516 } -517 -518 fn is_qad_frame(&self) -> bool { -519 matches!(*self, Self::ObservedAddr(_)) -520 } -521 -522 fn is_multipath_frame(&self) -> bool { -523 matches!( -524 *self, -525 Self::PathAck(_) -526 | Self::PathAbandon(_) -527 | Self::PathStatusBackup(_) -528 | Self::PathStatusAvailable(_) -529 | Self::MaxPathId(_) -530 | Self::PathsBlocked(_) -531 | Self::PathCidsBlocked(_) -532 | Self::NewConnectionId(NewConnectionId { -533 path_id: Some(_), -534 .. -535 }) -536 | Self::RetireConnectionId(RetireConnectionId { -537 path_id: Some(_), -538 .. -539 }) -540 ) -541 } -542} -543 -544#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display)] -545#[display("PATH_CHALLENGE({_0:08x})")] -546pub(crate) struct PathChallenge(pub(crate) u64); -547 -548impl PathChallenge { -549 pub(crate) const SIZE_BOUND: usize = 9; -550} -551impl Decodable for PathChallenge { -552 fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> { -553 Ok(Self(buf.get()?)) -554 } -555} -556impl Encodable for PathChallenge { -557 fn encode<B: BufMut>(&self, buf: &mut B) { -558 buf.write(FrameType::PathChallenge); -559 buf.write(self.0); -560 } -561} -562 -563#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, derive_more::Display)] -564#[display("PATH_RESPONSE({_0:08x})")] -565pub(crate) struct PathResponse(pub(crate) u64); +357#[derive(Debug)] +358pub(crate) enum Frame { +359 Padding, +360 Ping, +361 Ack(Ack), +362 PathAck(PathAck), +363 ResetStream(ResetStream), +364 StopSending(StopSending), +365 Crypto(Crypto), +366 NewToken(NewToken), +367 Stream(Stream), +368 MaxData(MaxData), +369 MaxStreamData(MaxStreamData), +370 MaxStreams(MaxStreams), +371 DataBlocked { offset: u64 }, +372 StreamDataBlocked { id: StreamId, offset: u64 }, +373 StreamsBlocked { dir: Dir, limit: u64 }, +374 NewConnectionId(NewConnectionId), +375 RetireConnectionId(RetireConnectionId), +376 PathChallenge(PathChallenge), +377 PathResponse(PathResponse), +378 Close(Close), +379 Datagram(Datagram), +380 AckFrequency(AckFrequency), +381 ImmediateAck, +382 HandshakeDone, +383 ObservedAddr(ObservedAddr), +384 PathAbandon(PathAbandon), +385 PathStatusAvailable(PathStatusAvailable), +386 PathStatusBackup(PathStatusBackup), +387 MaxPathId(MaxPathId), +388 PathsBlocked(PathsBlocked), +389 PathCidsBlocked(PathCidsBlocked), +390 AddAddress(AddAddress), +391 ReachOut(ReachOut), +392 RemoveAddress(RemoveAddress), +393} +394 +395impl fmt::Display for Frame { +396 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +397 // Eventually all our frames will support fmt::Display and be able to be used to log +398 // consistently. For now we fall back to fmt::Debug. +399 match self { +400 Self::Padding => write!(f, "PADDING"), +401 Self::Ping => write!(f, "PING"), +402 Self::PathChallenge(frame) => write!(f, "{frame}"), +403 Self::PathResponse(frame) => write!(f, "{frame}"), +404 Self::ImmediateAck => write!(f, "IMMEDIATE_ACK"), +405 Self::HandshakeDone => write!(f, "HANDSHAKE_DONE"), +406 _ => write!(f, "{self:?}"), +407 } +408 } +409} +410 +411impl Frame { +412 pub(crate) fn ty(&self) -> FrameType { +413 use Frame::*; +414 match &self { +415 Padding => FrameType::Padding, +416 ResetStream(_) => FrameType::ResetStream, +417 Close(self::Close::Connection(_)) => FrameType::ConnectionClose, +418 Close(self::Close::Application(_)) => FrameType::ConnectionClose, +419 MaxData(_) => FrameType::MaxData, +420 MaxStreamData(_) => FrameType::MaxStreamData, +421 MaxStreams(max_streams) => max_streams.get_type(), +422 Ping => FrameType::Ping, +423 DataBlocked { .. } => FrameType::DataBlocked, +424 StreamDataBlocked { .. } => FrameType::StreamDataBlocked, +425 StreamsBlocked { dir: Dir::Bi, .. } => FrameType::StreamsBlockedBidi, +426 StreamsBlocked { dir: Dir::Uni, .. } => FrameType::StreamsBlockedUni, +427 StopSending { .. } => FrameType::StopSending, +428 RetireConnectionId(retire_frame) => retire_frame.get_type(), +429 Ack(ack) => ack.get_type(), +430 PathAck(path_ack) => path_ack.get_type(), +431 Stream(ref x) => { +432 let mut ty = *StreamInfo::VALUES.start() as u8; +433 if x.fin { +434 ty |= 0x01; +435 } +436 if x.offset != 0 { +437 ty |= 0x04; +438 } +439 // TODO(@divma): move all this to getframetype for Stream +440 FrameType::Stream(StreamInfo(ty)) +441 } +442 PathChallenge(_) => FrameType::PathChallenge, +443 PathResponse(_) => FrameType::PathResponse, +444 NewConnectionId(cid) => cid.get_type(), +445 Crypto(_) => FrameType::Crypto, +446 NewToken(_) => FrameType::NewToken, +447 Datagram(_) => FrameType::Datagram(DatagramInfo(*DatagramInfo::VALUES.start() as u8)), +448 AckFrequency(_) => FrameType::AckFrequency, +449 ImmediateAck => FrameType::ImmediateAck, +450 HandshakeDone => FrameType::HandshakeDone, +451 ObservedAddr(ref observed) => observed.get_type(), +452 PathAbandon(_) => FrameType::PathAbandon, +453 PathStatusAvailable(_) => FrameType::PathStatusAvailable, +454 PathStatusBackup(_) => FrameType::PathStatusBackup, +455 MaxPathId(_) => FrameType::MaxPathId, +456 PathsBlocked(_) => FrameType::PathsBlocked, +457 PathCidsBlocked(_) => FrameType::PathCidsBlocked, +458 AddAddress(ref frame) => frame.get_type(), +459 ReachOut(ref frame) => frame.get_type(), +460 RemoveAddress(_) => self::RemoveAddress::TYPE, +461 } +462 } +463 +464 pub(crate) fn is_ack_eliciting(&self) -> bool { +465 !matches!( +466 *self, +467 Self::Ack(_) | Self::PathAck(_) | Self::Padding | Self::Close(_) +468 ) +469 } +470 +471 /// Returns `true` if this frame MUST be sent in 1-RTT space +472 pub(crate) fn is_1rtt(&self) -> bool { +473 // See also https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-4-1: +474 // > All frames defined in this document MUST only be sent in 1-RTT packets. +475 // > If an endpoint receives a multipath-specific frame in a different packet type, it MUST close the +476 // > connection with an error of type PROTOCOL_VIOLATION. +477 +478 self.is_multipath_frame() || self.is_qad_frame() +479 } +480 +481 fn is_qad_frame(&self) -> bool { +482 matches!(*self, Self::ObservedAddr(_)) +483 } +484 +485 fn is_multipath_frame(&self) -> bool { +486 matches!( +487 *self, +488 Self::PathAck(_) +489 | Self::PathAbandon(_) +490 | Self::PathStatusBackup(_) +491 | Self::PathStatusAvailable(_) +492 | Self::MaxPathId(_) +493 | Self::PathsBlocked(_) +494 | Self::PathCidsBlocked(_) +495 | Self::NewConnectionId(NewConnectionId { +496 path_id: Some(_), +497 .. +498 }) +499 | Self::RetireConnectionId(RetireConnectionId { +500 path_id: Some(_), +501 .. +502 }) +503 ) +504 } +505} +506 +507#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display)] +508#[display("PATH_CHALLENGE({_0:08x})")] +509pub(crate) struct PathChallenge(pub(crate) u64); +510 +511impl PathChallenge { +512 pub(crate) const SIZE_BOUND: usize = 9; +513} +514impl Decodable for PathChallenge { +515 fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> { +516 Ok(Self(buf.get()?)) +517 } +518} +519impl Encodable for PathChallenge { +520 fn encode<B: BufMut>(&self, buf: &mut B) { +521 buf.write(FrameType::PathChallenge); +522 buf.write(self.0); +523 } +524} +525 +526#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, derive_more::Display)] +527#[display("PATH_RESPONSE({_0:08x})")] +528pub(crate) struct PathResponse(pub(crate) u64); +529 +530impl PathResponse { +531 pub(crate) const SIZE_BOUND: usize = 9; +532} +533 +534impl Decodable for PathResponse { +535 fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> { +536 Ok(Self(buf.get()?)) +537 } +538} +539impl Encodable for PathResponse { +540 fn encode<B: BufMut>(&self, buf: &mut B) { +541 buf.write(FrameType::PathResponse); +542 buf.write(self.0); +543 } +544} +545 +546#[derive(Debug, Clone, Copy)] +547pub(crate) struct MaxData(pub(crate) VarInt); +548 +549impl Decodable for MaxData { +550 fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> { +551 Ok(Self(buf.get()?)) +552 } +553} +554impl Encodable for MaxData { +555 fn encode<B: BufMut>(&self, buf: &mut B) { +556 buf.write(FrameType::MaxData); +557 buf.write(self.0); +558 } +559} +560 +561#[derive(Debug, Clone, Copy)] +562pub(crate) struct MaxStreamData { +563 pub(crate) id: StreamId, +564 pub(crate) offset: u64, +565} 566 -567impl PathResponse { -568 pub(crate) const SIZE_BOUND: usize = 9; -569} -570 -571impl Decodable for PathResponse { -572 fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> { -573 Ok(Self(buf.get()?)) -574 } -575} -576impl Encodable for PathResponse { +567impl Decodable for MaxStreamData { +568 fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> { +569 Ok(Self { +570 id: buf.get()?, +571 offset: buf.get_var()?, +572 }) +573 } +574} +575 +576impl Encodable for MaxStreamData { 577 fn encode<B: BufMut>(&self, buf: &mut B) { -578 buf.write(FrameType::PathResponse); -579 buf.write(self.0); -580 } -581} -582 -583#[derive(Debug, Clone, Copy)] -584pub(crate) struct MaxData(pub(crate) VarInt); -585 -586impl Decodable for MaxData { -587 fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> { -588 Ok(Self(buf.get()?)) -589 } -590} -591impl Encodable for MaxData { -592 fn encode<B: BufMut>(&self, buf: &mut B) { -593 buf.write(FrameType::MaxData); -594 buf.write(self.0); -595 } -596} -597 -598#[derive(Debug, Clone, Copy)] -599pub(crate) struct MaxStreamData { -600 pub(crate) id: StreamId, -601 pub(crate) offset: u64, -602} -603 -604impl Decodable for MaxStreamData { -605 fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> { -606 Ok(Self { -607 id: buf.get()?, -608 offset: buf.get_var()?, -609 }) -610 } -611} -612 -613impl Encodable for MaxStreamData { -614 fn encode<B: BufMut>(&self, buf: &mut B) { -615 buf.write(FrameType::MaxStreamData); -616 buf.write(self.id); -617 buf.write_var(self.offset); -618 } -619} -620 -621#[derive(Debug, Clone, Copy)] -622pub(crate) struct MaxStreams { -623 pub(crate) dir: Dir, -624 pub(crate) count: u64, -625} -626 -627impl MaxStreams { -628 pub(crate) fn get_type(&self) -> FrameType { -629 match self.dir { -630 Dir::Bi => FrameType::MaxStreamsBidi, -631 Dir::Uni => FrameType::MaxStreamsUni, -632 } -633 } -634} -635 -636impl Encodable for MaxStreams { -637 fn encode<B: BufMut>(&self, buf: &mut B) { -638 buf.write(self.get_type()); -639 buf.write_var(self.count); -640 } -641} -642 -643#[derive(Debug, PartialEq, Eq)] -644pub(crate) struct RetireConnectionId { -645 pub(crate) path_id: Option<PathId>, -646 pub(crate) sequence: u64, -647} -648 -649impl RetireConnectionId { -650 /// Maximum size of this frame when the frame type is [`FrameType::RetireConnectionId`] -651 pub(crate) const SIZE_BOUND: usize = { -652 let type_len = FrameType::RetireConnectionId.size(); -653 let seq_max_len = 8usize; -654 type_len + seq_max_len -655 }; -656 -657 /// Maximum size of this frame when the frame type is [`FrameType::PathRetireConnectionId`] -658 pub(crate) const SIZE_BOUND_MULTIPATH: usize = { -659 let type_len = FrameType::PathRetireConnectionId.size(); -660 let path_id_len = VarInt::from_u32(u32::MAX).size(); -661 let seq_max_len = 8usize; -662 type_len + path_id_len + seq_max_len -663 }; -664 -665 /// Decode [`Self`] from the buffer, provided that the frame type has been verified (either -666 /// [`FrameType::PathRetireConnectionId`], or [`FrameType::RetireConnectionId`]) -667 pub(crate) fn decode<R: Buf>(bytes: &mut R, read_path: bool) -> coding::Result<Self> { -668 Ok(Self { -669 path_id: if read_path { Some(bytes.get()?) } else { None }, -670 sequence: bytes.get_var()?, -671 }) -672 } +578 buf.write(FrameType::MaxStreamData); +579 buf.write(self.id); +580 buf.write_var(self.offset); +581 } +582} +583 +584#[derive(Debug, Clone, Copy)] +585pub(crate) struct MaxStreams { +586 pub(crate) dir: Dir, +587 pub(crate) count: u64, +588} +589 +590impl MaxStreams { +591 pub(crate) fn get_type(&self) -> FrameType { +592 match self.dir { +593 Dir::Bi => FrameType::MaxStreamsBidi, +594 Dir::Uni => FrameType::MaxStreamsUni, +595 } +596 } +597} +598 +599impl Encodable for MaxStreams { +600 fn encode<B: BufMut>(&self, buf: &mut B) { +601 buf.write(self.get_type()); +602 buf.write_var(self.count); +603 } +604} +605 +606#[derive(Debug, PartialEq, Eq)] +607pub(crate) struct RetireConnectionId { +608 pub(crate) path_id: Option<PathId>, +609 pub(crate) sequence: u64, +610} +611 +612impl RetireConnectionId { +613 /// Maximum size of this frame when the frame type is [`FrameType::RetireConnectionId`] +614 pub(crate) const SIZE_BOUND: usize = { +615 let type_len = FrameType::RetireConnectionId.size(); +616 let seq_max_len = 8usize; +617 type_len + seq_max_len +618 }; +619 +620 /// Maximum size of this frame when the frame type is [`FrameType::PathRetireConnectionId`] +621 pub(crate) const SIZE_BOUND_MULTIPATH: usize = { +622 let type_len = FrameType::PathRetireConnectionId.size(); +623 let path_id_len = VarInt::from_u32(u32::MAX).size(); +624 let seq_max_len = 8usize; +625 type_len + path_id_len + seq_max_len +626 }; +627 +628 /// Decode [`Self`] from the buffer, provided that the frame type has been verified (either +629 /// [`FrameType::PathRetireConnectionId`], or [`FrameType::RetireConnectionId`]) +630 pub(crate) fn decode<R: Buf>(bytes: &mut R, read_path: bool) -> coding::Result<Self> { +631 Ok(Self { +632 path_id: if read_path { Some(bytes.get()?) } else { None }, +633 sequence: bytes.get_var()?, +634 }) +635 } +636 +637 /// Get the [`FrameType`] for this [`RetireConnectionId`] +638 pub(crate) fn get_type(&self) -> FrameType { +639 if self.path_id.is_some() { +640 FrameType::PathRetireConnectionId +641 } else { +642 FrameType::RetireConnectionId +643 } +644 } +645 +646 /// Returns the maximum encoded size on the wire +647 /// +648 /// `path_retire_cid` determines whether this frame is a multipath frame. This is a rough upper +649 /// estimate, does not squeeze every last byte out. +650 pub(crate) const fn size_bound(path_retire_cid: bool) -> usize { +651 match path_retire_cid { +652 true => Self::SIZE_BOUND_MULTIPATH, +653 false => Self::SIZE_BOUND, +654 } +655 } +656} +657 +658impl Encodable for RetireConnectionId { +659 fn encode<W: BufMut>(&self, buf: &mut W) { +660 buf.write(self.get_type()); +661 if let Some(id) = self.path_id { +662 buf.write(id); +663 } +664 buf.write_var(self.sequence); +665 } +666} +667 +668#[derive(Clone, Debug)] +669pub enum Close { +670 Connection(ConnectionClose), +671 Application(ApplicationClose), +672} 673 -674 /// Get the [`FrameType`] for this [`RetireConnectionId`] -675 pub(crate) fn get_type(&self) -> FrameType { -676 if self.path_id.is_some() { -677 FrameType::PathRetireConnectionId -678 } else { -679 FrameType::RetireConnectionId -680 } -681 } -682 -683 /// Returns the maximum encoded size on the wire -684 /// -685 /// `path_retire_cid` determines whether this frame is a multipath frame. This is a rough upper -686 /// estimate, does not squeeze every last byte out. -687 pub(crate) const fn size_bound(path_retire_cid: bool) -> usize { -688 match path_retire_cid { -689 true => Self::SIZE_BOUND_MULTIPATH, -690 false => Self::SIZE_BOUND, -691 } -692 } -693} -694 -695impl Encodable for RetireConnectionId { -696 fn encode<W: BufMut>(&self, buf: &mut W) { -697 buf.write(self.get_type()); -698 if let Some(id) = self.path_id { -699 buf.write(id); -700 } -701 buf.write_var(self.sequence); -702 } -703} -704 -705#[derive(Clone, Debug)] -706pub enum Close { -707 Connection(ConnectionClose), -708 Application(ApplicationClose), -709} -710 -711impl Close { -712 pub(crate) fn encoder(&self, max_len: usize) -> CloseEncoder<'_> { -713 CloseEncoder { -714 close: self, -715 max_len, -716 } -717 } -718 -719 pub(crate) fn is_transport_layer(&self) -> bool { -720 matches!(*self, Self::Connection(_)) -721 } -722} -723 -724pub(crate) struct CloseEncoder<'a> { -725 pub(crate) close: &'a Close, -726 max_len: usize, -727} -728 -729impl<'a> CloseEncoder<'a> { -730 fn get_type(&self) -> FrameType { -731 match self.close { -732 Close::Connection(_) => FrameType::ConnectionClose, -733 Close::Application(_) => FrameType::ApplicationClose, -734 } -735 } -736} -737 -738impl<'a> Encodable for CloseEncoder<'a> { -739 fn encode<W: BufMut>(&self, out: &mut W) { -740 match self.close { -741 Close::Connection(x) => x.encode(out, self.max_len), -742 Close::Application(x) => x.encode(out, self.max_len), +674impl Close { +675 pub(crate) fn encoder(&self, max_len: usize) -> CloseEncoder<'_> { +676 CloseEncoder { +677 close: self, +678 max_len, +679 } +680 } +681 +682 pub(crate) fn is_transport_layer(&self) -> bool { +683 matches!(*self, Self::Connection(_)) +684 } +685} +686 +687pub(crate) struct CloseEncoder<'a> { +688 pub(crate) close: &'a Close, +689 max_len: usize, +690} +691 +692impl<'a> CloseEncoder<'a> { +693 fn get_type(&self) -> FrameType { +694 match self.close { +695 Close::Connection(_) => FrameType::ConnectionClose, +696 Close::Application(_) => FrameType::ApplicationClose, +697 } +698 } +699} +700 +701impl<'a> Encodable for CloseEncoder<'a> { +702 fn encode<W: BufMut>(&self, out: &mut W) { +703 match self.close { +704 Close::Connection(x) => x.encode(out, self.max_len), +705 Close::Application(x) => x.encode(out, self.max_len), +706 } +707 } +708} +709 +710impl From<TransportError> for Close { +711 fn from(x: TransportError) -> Self { +712 Self::Connection(x.into()) +713 } +714} +715impl From<ConnectionClose> for Close { +716 fn from(x: ConnectionClose) -> Self { +717 Self::Connection(x) +718 } +719} +720impl From<ApplicationClose> for Close { +721 fn from(x: ApplicationClose) -> Self { +722 Self::Application(x) +723 } +724} +725 +726/// Reason given by the transport for closing the connection +727#[derive(Debug, Clone, PartialEq, Eq)] +728pub struct ConnectionClose { +729 /// Class of error as encoded in the specification +730 pub error_code: TransportErrorCode, +731 /// Type of frame that caused the close +732 pub frame_type: MaybeFrame, +733 /// Human-readable reason for the close +734 pub reason: Bytes, +735} +736 +737impl fmt::Display for ConnectionClose { +738 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +739 self.error_code.fmt(f)?; +740 if !self.reason.as_ref().is_empty() { +741 f.write_str(": ")?; +742 f.write_str(&String::from_utf8_lossy(&self.reason))?; 743 } -744 } -745} -746 -747impl From<TransportError> for Close { -748 fn from(x: TransportError) -> Self { -749 Self::Connection(x.into()) -750 } -751} -752impl From<ConnectionClose> for Close { -753 fn from(x: ConnectionClose) -> Self { -754 Self::Connection(x) +744 Ok(()) +745 } +746} +747 +748impl From<TransportError> for ConnectionClose { +749 fn from(x: TransportError) -> Self { +750 Self { +751 error_code: x.code, +752 frame_type: x.frame, +753 reason: x.reason.into(), +754 } 755 } 756} -757impl From<ApplicationClose> for Close { -758 fn from(x: ApplicationClose) -> Self { -759 Self::Application(x) -760 } -761} -762 -763/// Reason given by the transport for closing the connection -764#[derive(Debug, Clone, PartialEq, Eq)] -765pub struct ConnectionClose { -766 /// Class of error as encoded in the specification -767 pub error_code: TransportErrorCode, -768 /// Type of frame that caused the close -769 pub frame_type: MaybeFrame, -770 /// Human-readable reason for the close -771 pub reason: Bytes, -772} -773 -774impl fmt::Display for ConnectionClose { -775 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -776 self.error_code.fmt(f)?; -777 if !self.reason.as_ref().is_empty() { -778 f.write_str(": ")?; -779 f.write_str(&String::from_utf8_lossy(&self.reason))?; -780 } -781 Ok(()) -782 } -783} -784 -785impl From<TransportError> for ConnectionClose { -786 fn from(x: TransportError) -> Self { -787 Self { -788 error_code: x.code, -789 frame_type: x.frame, -790 reason: x.reason.into(), -791 } -792 } -793} -794 -795impl FrameStruct for ConnectionClose { -796 const SIZE_BOUND: usize = 1 + 8 + 8 + 8; -797} -798 -799impl ConnectionClose { -800 pub(crate) fn encode<W: BufMut>(&self, out: &mut W, max_len: usize) { -801 out.write(FrameType::ConnectionClose); // 1 byte -802 out.write(self.error_code); // <= 8 bytes -803 out.write(self.frame_type); // <= 8 bytes -804 let max_len = max_len -805 - 3 -806 - self.frame_type.size() -807 - VarInt::from_u64(self.reason.len() as u64).unwrap().size(); -808 let actual_len = self.reason.len().min(max_len); -809 out.write_var(actual_len as u64); // <= 8 bytes -810 out.put_slice(&self.reason[0..actual_len]); // whatever's left -811 } -812} -813 -814/// Reason given by an application for closing the connection -815#[derive(Debug, Clone, PartialEq, Eq)] -816pub struct ApplicationClose { -817 /// Application-specific reason code -818 pub error_code: VarInt, -819 /// Human-readable reason for the close -820 pub reason: Bytes, -821} -822 -823impl fmt::Display for ApplicationClose { -824 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -825 if !self.reason.as_ref().is_empty() { -826 f.write_str(&String::from_utf8_lossy(&self.reason))?; -827 f.write_str(" (code ")?; -828 self.error_code.fmt(f)?; -829 f.write_str(")")?; -830 } else { -831 self.error_code.fmt(f)?; -832 } -833 Ok(()) -834 } -835} +757 +758impl FrameStruct for ConnectionClose { +759 const SIZE_BOUND: usize = 1 + 8 + 8 + 8; +760} +761 +762impl ConnectionClose { +763 pub(crate) fn encode<W: BufMut>(&self, out: &mut W, max_len: usize) { +764 out.write(FrameType::ConnectionClose); // 1 byte +765 out.write(self.error_code); // <= 8 bytes +766 out.write(self.frame_type); // <= 8 bytes +767 let max_len = max_len +768 - 3 +769 - self.frame_type.size() +770 - VarInt::from_u64(self.reason.len() as u64).unwrap().size(); +771 let actual_len = self.reason.len().min(max_len); +772 out.write_var(actual_len as u64); // <= 8 bytes +773 out.put_slice(&self.reason[0..actual_len]); // whatever's left +774 } +775} +776 +777/// Reason given by an application for closing the connection +778#[derive(Debug, Clone, PartialEq, Eq)] +779pub struct ApplicationClose { +780 /// Application-specific reason code +781 pub error_code: VarInt, +782 /// Human-readable reason for the close +783 pub reason: Bytes, +784} +785 +786impl fmt::Display for ApplicationClose { +787 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +788 if !self.reason.as_ref().is_empty() { +789 f.write_str(&String::from_utf8_lossy(&self.reason))?; +790 f.write_str(" (code ")?; +791 self.error_code.fmt(f)?; +792 f.write_str(")")?; +793 } else { +794 self.error_code.fmt(f)?; +795 } +796 Ok(()) +797 } +798} +799 +800impl FrameStruct for ApplicationClose { +801 const SIZE_BOUND: usize = 1 + 8 + 8; +802} +803 +804impl ApplicationClose { +805 pub(crate) fn encode<W: BufMut>(&self, out: &mut W, max_len: usize) { +806 out.write(FrameType::ApplicationClose); // 1 byte +807 out.write(self.error_code); // <= 8 bytes +808 let max_len = max_len - 3 - VarInt::from_u64(self.reason.len() as u64).unwrap().size(); +809 let actual_len = self.reason.len().min(max_len); +810 out.write_var(actual_len as u64); // <= 8 bytes +811 out.put_slice(&self.reason[0..actual_len]); // whatever's left +812 } +813} +814 +815#[derive(Clone, Eq, PartialEq)] +816pub struct PathAck { +817 pub path_id: PathId, +818 pub largest: u64, +819 pub delay: u64, +820 pub additional: Bytes, +821 pub ecn: Option<EcnCounts>, +822} +823 +824impl fmt::Debug for PathAck { +825 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +826 let mut ranges = "[".to_string(); +827 let mut first = true; +828 for range in self.into_iter() { +829 if !first { +830 ranges.push(','); +831 } +832 write!(ranges, "{range:?}")?; +833 first = false; +834 } +835 ranges.push(']'); 836 -837impl FrameStruct for ApplicationClose { -838 const SIZE_BOUND: usize = 1 + 8 + 8; -839} -840 -841impl ApplicationClose { -842 pub(crate) fn encode<W: BufMut>(&self, out: &mut W, max_len: usize) { -843 out.write(FrameType::ApplicationClose); // 1 byte -844 out.write(self.error_code); // <= 8 bytes -845 let max_len = max_len - 3 - VarInt::from_u64(self.reason.len() as u64).unwrap().size(); -846 let actual_len = self.reason.len().min(max_len); -847 out.write_var(actual_len as u64); // <= 8 bytes -848 out.put_slice(&self.reason[0..actual_len]); // whatever's left -849 } -850} -851 -852#[derive(Clone, Eq, PartialEq)] -853pub struct PathAck { -854 pub path_id: PathId, -855 pub largest: u64, -856 pub delay: u64, -857 pub additional: Bytes, -858 pub ecn: Option<EcnCounts>, -859} -860 -861impl fmt::Debug for PathAck { -862 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -863 let mut ranges = "[".to_string(); -864 let mut first = true; -865 for range in self.into_iter() { -866 if !first { -867 ranges.push(','); -868 } -869 write!(ranges, "{range:?}")?; -870 first = false; -871 } -872 ranges.push(']'); -873 -874 f.debug_struct("PathAck") -875 .field("path_id", &self.path_id) -876 .field("largest", &self.largest) -877 .field("delay", &self.delay) -878 .field("ecn", &self.ecn) -879 .field("ranges", &ranges) -880 .finish() -881 } -882} -883 -884impl<'a> IntoIterator for &'a PathAck { -885 type Item = RangeInclusive<u64>; -886 type IntoIter = AckIter<'a>; -887 -888 fn into_iter(self) -> AckIter<'a> { -889 AckIter::new(self.largest, &self.additional[..]) -890 } -891} -892 -893impl PathAck { -894 pub fn into_ack(self) -> (Ack, PathId) { -895 let ack = Ack { -896 largest: self.largest, -897 delay: self.delay, -898 additional: self.additional, -899 ecn: self.ecn, -900 }; -901 -902 (ack, self.path_id) -903 } -904 -905 fn get_type(&self) -> FrameType { -906 if self.ecn.is_some() { -907 FrameType::PathAckEcn -908 } else { -909 FrameType::PathAck -910 } -911 } -912 -913 pub(crate) fn encoder<'a>( -914 path_id: PathId, -915 delay: u64, -916 ranges: &'a ArrayRangeSet, -917 ecn: Option<&'a EcnCounts>, -918 ) -> PathAckEncoder<'a> { -919 PathAckEncoder { -920 path_id, -921 delay, -922 ranges, -923 ecn, -924 } -925 } -926} -927 -928pub(crate) struct PathAckEncoder<'a> { -929 pub(super) path_id: PathId, -930 pub(super) delay: u64, -931 pub(super) ranges: &'a ArrayRangeSet, -932 pub(super) ecn: Option<&'a EcnCounts>, -933} -934 -935impl<'a> PathAckEncoder<'a> { -936 fn get_type(&self) -> FrameType { -937 match self.ecn.is_some() { -938 true => FrameType::PathAckEcn, -939 false => FrameType::PathAck, -940 } -941 } -942} -943 -944impl<'a> Encodable for PathAckEncoder<'a> { -945 /// Encode [`Self`] into the given buffer -946 /// -947 /// The [`FrameType`] will be either [`FrameType::PathAckEcn`] or [`FrameType::PathAck`] -948 /// depending on whether [`EcnCounts`] are provided. -949 /// -950 /// PANICS: if `ranges` is empty. -951 fn encode<W: BufMut>(&self, buf: &mut W) { -952 let PathAckEncoder { -953 path_id, -954 delay, -955 ranges, -956 ecn, -957 } = self; -958 let mut rest = ranges.iter().rev(); -959 let first = rest -960 .next() -961 .expect("Caller has verified ranges is non empty"); -962 let largest = first.end - 1; -963 let first_size = first.end - first.start; -964 let kind = match ecn.is_some() { -965 true => FrameType::PathAckEcn, -966 false => FrameType::PathAck, -967 }; -968 buf.write(kind); -969 buf.write(*path_id); -970 buf.write_var(largest); -971 buf.write_var(*delay); -972 buf.write_var(ranges.len() as u64 - 1); -973 buf.write_var(first_size - 1); -974 let mut prev = first.start; -975 for block in rest { -976 let size = block.end - block.start; -977 buf.write_var(prev - block.end - 1); -978 buf.write_var(size - 1); -979 prev = block.start; -980 } -981 if let Some(x) = ecn { -982 x.encode(buf) -983 } -984 } -985} -986 -987#[derive(Clone, Eq, PartialEq)] -988pub struct Ack { -989 pub largest: u64, -990 pub delay: u64, -991 pub additional: Bytes, -992 pub ecn: Option<EcnCounts>, -993} -994 -995impl fmt::Debug for Ack { -996 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -997 let mut ranges = "[".to_string(); -998 let mut first = true; -999 for range in self.iter() { -1000 if !first { -1001 ranges.push(','); -1002 } -1003 write!(ranges, "{range:?}").unwrap(); -1004 first = false; -1005 } -1006 ranges.push(']'); -1007 -1008 f.debug_struct("Ack") -1009 .field("largest", &self.largest) -1010 .field("delay", &self.delay) -1011 .field("ecn", &self.ecn) -1012 .field("ranges", &ranges) -1013 .finish() -1014 } +837 f.debug_struct("PathAck") +838 .field("path_id", &self.path_id) +839 .field("largest", &self.largest) +840 .field("delay", &self.delay) +841 .field("ecn", &self.ecn) +842 .field("ranges", &ranges) +843 .finish() +844 } +845} +846 +847impl<'a> IntoIterator for &'a PathAck { +848 type Item = RangeInclusive<u64>; +849 type IntoIter = AckIter<'a>; +850 +851 fn into_iter(self) -> AckIter<'a> { +852 AckIter::new(self.largest, &self.additional[..]) +853 } +854} +855 +856impl PathAck { +857 pub fn into_ack(self) -> (Ack, PathId) { +858 let ack = Ack { +859 largest: self.largest, +860 delay: self.delay, +861 additional: self.additional, +862 ecn: self.ecn, +863 }; +864 +865 (ack, self.path_id) +866 } +867 +868 fn get_type(&self) -> FrameType { +869 if self.ecn.is_some() { +870 FrameType::PathAckEcn +871 } else { +872 FrameType::PathAck +873 } +874 } +875 +876 pub(crate) fn encoder<'a>( +877 path_id: PathId, +878 delay: u64, +879 ranges: &'a ArrayRangeSet, +880 ecn: Option<&'a EcnCounts>, +881 ) -> PathAckEncoder<'a> { +882 PathAckEncoder { +883 path_id, +884 delay, +885 ranges, +886 ecn, +887 } +888 } +889} +890 +891pub(crate) struct PathAckEncoder<'a> { +892 pub(super) path_id: PathId, +893 pub(super) delay: u64, +894 pub(super) ranges: &'a ArrayRangeSet, +895 pub(super) ecn: Option<&'a EcnCounts>, +896} +897 +898impl<'a> PathAckEncoder<'a> { +899 fn get_type(&self) -> FrameType { +900 match self.ecn.is_some() { +901 true => FrameType::PathAckEcn, +902 false => FrameType::PathAck, +903 } +904 } +905} +906 +907impl<'a> Encodable for PathAckEncoder<'a> { +908 /// Encode [`Self`] into the given buffer +909 /// +910 /// The [`FrameType`] will be either [`FrameType::PathAckEcn`] or [`FrameType::PathAck`] +911 /// depending on whether [`EcnCounts`] are provided. +912 /// +913 /// PANICS: if `ranges` is empty. +914 fn encode<W: BufMut>(&self, buf: &mut W) { +915 let PathAckEncoder { +916 path_id, +917 delay, +918 ranges, +919 ecn, +920 } = self; +921 let mut rest = ranges.iter().rev(); +922 let first = rest +923 .next() +924 .expect("Caller has verified ranges is non empty"); +925 let largest = first.end - 1; +926 let first_size = first.end - first.start; +927 let kind = match ecn.is_some() { +928 true => FrameType::PathAckEcn, +929 false => FrameType::PathAck, +930 }; +931 buf.write(kind); +932 buf.write(*path_id); +933 buf.write_var(largest); +934 buf.write_var(*delay); +935 buf.write_var(ranges.len() as u64 - 1); +936 buf.write_var(first_size - 1); +937 let mut prev = first.start; +938 for block in rest { +939 let size = block.end - block.start; +940 buf.write_var(prev - block.end - 1); +941 buf.write_var(size - 1); +942 prev = block.start; +943 } +944 if let Some(x) = ecn { +945 x.encode(buf) +946 } +947 } +948} +949 +950#[derive(Clone, Eq, PartialEq)] +951pub struct Ack { +952 pub largest: u64, +953 pub delay: u64, +954 pub additional: Bytes, +955 pub ecn: Option<EcnCounts>, +956} +957 +958impl fmt::Debug for Ack { +959 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +960 let mut ranges = "[".to_string(); +961 let mut first = true; +962 for range in self.iter() { +963 if !first { +964 ranges.push(','); +965 } +966 write!(ranges, "{range:?}").unwrap(); +967 first = false; +968 } +969 ranges.push(']'); +970 +971 f.debug_struct("Ack") +972 .field("largest", &self.largest) +973 .field("delay", &self.delay) +974 .field("ecn", &self.ecn) +975 .field("ranges", &ranges) +976 .finish() +977 } +978} +979 +980impl<'a> IntoIterator for &'a Ack { +981 type Item = RangeInclusive<u64>; +982 type IntoIter = AckIter<'a>; +983 +984 fn into_iter(self) -> AckIter<'a> { +985 AckIter::new(self.largest, &self.additional[..]) +986 } +987} +988 +989impl Ack { +990 pub(crate) fn encoder<'a>( +991 delay: u64, +992 ranges: &'a ArrayRangeSet, +993 ecn: Option<&'a EcnCounts>, +994 ) -> AckEncoder<'a> { +995 AckEncoder { delay, ranges, ecn } +996 } +997 +998 pub fn iter(&self) -> AckIter<'_> { +999 self.into_iter() +1000 } +1001 +1002 pub(crate) const fn get_type(&self) -> FrameType { +1003 if self.ecn.is_some() { +1004 FrameType::AckEcn +1005 } else { +1006 FrameType::Ack +1007 } +1008 } +1009} +1010 +1011pub(crate) struct AckEncoder<'a> { +1012 pub(crate) delay: u64, +1013 pub(crate) ranges: &'a ArrayRangeSet, +1014 pub(crate) ecn: Option<&'a EcnCounts>, 1015} 1016 -1017impl<'a> IntoIterator for &'a Ack { -1018 type Item = RangeInclusive<u64>; -1019 type IntoIter = AckIter<'a>; -1020 -1021 fn into_iter(self) -> AckIter<'a> { -1022 AckIter::new(self.largest, &self.additional[..]) +1017impl<'a> AckEncoder<'a> { +1018 fn get_type(&self) -> FrameType { +1019 match self.ecn.is_some() { +1020 true => FrameType::AckEcn, +1021 false => FrameType::Ack, +1022 } 1023 } 1024} 1025 -1026impl Ack { -1027 pub(crate) fn encoder<'a>( -1028 delay: u64, -1029 ranges: &'a ArrayRangeSet, -1030 ecn: Option<&'a EcnCounts>, -1031 ) -> AckEncoder<'a> { -1032 AckEncoder { delay, ranges, ecn } -1033 } -1034 -1035 pub fn iter(&self) -> AckIter<'_> { -1036 self.into_iter() -1037 } -1038 -1039 pub(crate) const fn get_type(&self) -> FrameType { -1040 if self.ecn.is_some() { -1041 FrameType::AckEcn -1042 } else { -1043 FrameType::Ack -1044 } -1045 } -1046} -1047 -1048pub(crate) struct AckEncoder<'a> { -1049 pub(crate) delay: u64, -1050 pub(crate) ranges: &'a ArrayRangeSet, -1051 pub(crate) ecn: Option<&'a EcnCounts>, -1052} -1053 -1054impl<'a> AckEncoder<'a> { -1055 fn get_type(&self) -> FrameType { -1056 match self.ecn.is_some() { -1057 true => FrameType::AckEcn, -1058 false => FrameType::Ack, -1059 } -1060 } -1061} -1062 -1063impl<'a> Encodable for AckEncoder<'a> { -1064 fn encode<W: BufMut>(&self, buf: &mut W) { -1065 let AckEncoder { delay, ranges, ecn } = self; -1066 let mut rest = ranges.iter().rev(); -1067 let first = rest.next().unwrap(); -1068 let largest = first.end - 1; -1069 let first_size = first.end - first.start; -1070 let kind = match ecn.is_some() { -1071 true => FrameType::AckEcn, -1072 false => FrameType::Ack, -1073 }; -1074 buf.write(kind); -1075 buf.write_var(largest); -1076 buf.write_var(*delay); -1077 buf.write_var(ranges.len() as u64 - 1); -1078 buf.write_var(first_size - 1); -1079 let mut prev = first.start; -1080 for block in rest { -1081 let size = block.end - block.start; -1082 buf.write_var(prev - block.end - 1); -1083 buf.write_var(size - 1); -1084 prev = block.start; -1085 } -1086 if let Some(x) = ecn { -1087 x.encode(buf) -1088 } -1089 } -1090} -1091 -1092#[derive(Debug, Copy, Clone, Eq, PartialEq)] -1093pub struct EcnCounts { -1094 pub ect0: u64, -1095 pub ect1: u64, -1096 pub ce: u64, -1097} -1098 -1099impl std::ops::AddAssign<EcnCodepoint> for EcnCounts { -1100 fn add_assign(&mut self, rhs: EcnCodepoint) { -1101 match rhs { -1102 EcnCodepoint::Ect0 => { -1103 self.ect0 += 1; -1104 } -1105 EcnCodepoint::Ect1 => { -1106 self.ect1 += 1; -1107 } -1108 EcnCodepoint::Ce => { -1109 self.ce += 1; -1110 } -1111 } -1112 } -1113} -1114 -1115impl EcnCounts { -1116 pub const ZERO: Self = Self { -1117 ect0: 0, -1118 ect1: 0, -1119 ce: 0, -1120 }; -1121} -1122 -1123impl Encodable for EcnCounts { -1124 fn encode<W: BufMut>(&self, out: &mut W) { -1125 out.write_var(self.ect0); -1126 out.write_var(self.ect1); -1127 out.write_var(self.ce); -1128 } -1129} -1130 -1131#[derive(Debug, Clone)] -1132pub(crate) struct Stream { -1133 pub(crate) id: StreamId, -1134 pub(crate) offset: u64, -1135 pub(crate) fin: bool, -1136 pub(crate) data: Bytes, -1137} -1138 -1139impl FrameStruct for Stream { -1140 const SIZE_BOUND: usize = 1 + 8 + 8 + 8; -1141} -1142 -1143/// Metadata from a stream frame -1144#[derive(Debug, Clone)] -1145pub(crate) struct StreamMeta { -1146 pub(crate) id: StreamId, -1147 pub(crate) offsets: Range<u64>, -1148 pub(crate) fin: bool, -1149} -1150 -1151// This manual implementation exists because `Default` is not implemented for `StreamId` -1152impl Default for StreamMeta { -1153 fn default() -> Self { -1154 Self { -1155 id: StreamId(0), -1156 offsets: 0..0, -1157 fin: false, -1158 } -1159 } -1160} -1161 -1162impl StreamMeta { -1163 pub(crate) fn encoder(self, encode_length: bool) -> StreamMetaEncoder { -1164 StreamMetaEncoder { -1165 meta: self, -1166 encode_length, -1167 } -1168 } +1026impl<'a> Encodable for AckEncoder<'a> { +1027 fn encode<W: BufMut>(&self, buf: &mut W) { +1028 let AckEncoder { delay, ranges, ecn } = self; +1029 let mut rest = ranges.iter().rev(); +1030 let first = rest.next().unwrap(); +1031 let largest = first.end - 1; +1032 let first_size = first.end - first.start; +1033 let kind = match ecn.is_some() { +1034 true => FrameType::AckEcn, +1035 false => FrameType::Ack, +1036 }; +1037 buf.write(kind); +1038 buf.write_var(largest); +1039 buf.write_var(*delay); +1040 buf.write_var(ranges.len() as u64 - 1); +1041 buf.write_var(first_size - 1); +1042 let mut prev = first.start; +1043 for block in rest { +1044 let size = block.end - block.start; +1045 buf.write_var(prev - block.end - 1); +1046 buf.write_var(size - 1); +1047 prev = block.start; +1048 } +1049 if let Some(x) = ecn { +1050 x.encode(buf) +1051 } +1052 } +1053} +1054 +1055#[derive(Debug, Copy, Clone, Eq, PartialEq)] +1056pub struct EcnCounts { +1057 pub ect0: u64, +1058 pub ect1: u64, +1059 pub ce: u64, +1060} +1061 +1062impl std::ops::AddAssign<EcnCodepoint> for EcnCounts { +1063 fn add_assign(&mut self, rhs: EcnCodepoint) { +1064 match rhs { +1065 EcnCodepoint::Ect0 => { +1066 self.ect0 += 1; +1067 } +1068 EcnCodepoint::Ect1 => { +1069 self.ect1 += 1; +1070 } +1071 EcnCodepoint::Ce => { +1072 self.ce += 1; +1073 } +1074 } +1075 } +1076} +1077 +1078impl EcnCounts { +1079 pub const ZERO: Self = Self { +1080 ect0: 0, +1081 ect1: 0, +1082 ce: 0, +1083 }; +1084} +1085 +1086impl Encodable for EcnCounts { +1087 fn encode<W: BufMut>(&self, out: &mut W) { +1088 out.write_var(self.ect0); +1089 out.write_var(self.ect1); +1090 out.write_var(self.ce); +1091 } +1092} +1093 +1094#[derive(Debug, Clone)] +1095pub(crate) struct Stream { +1096 pub(crate) id: StreamId, +1097 pub(crate) offset: u64, +1098 pub(crate) fin: bool, +1099 pub(crate) data: Bytes, +1100} +1101 +1102impl FrameStruct for Stream { +1103 const SIZE_BOUND: usize = 1 + 8 + 8 + 8; +1104} +1105 +1106/// Metadata from a stream frame +1107#[derive(Debug, Clone)] +1108pub(crate) struct StreamMeta { +1109 pub(crate) id: StreamId, +1110 pub(crate) offsets: Range<u64>, +1111 pub(crate) fin: bool, +1112} +1113 +1114// This manual implementation exists because `Default` is not implemented for `StreamId` +1115impl Default for StreamMeta { +1116 fn default() -> Self { +1117 Self { +1118 id: StreamId(0), +1119 offsets: 0..0, +1120 fin: false, +1121 } +1122 } +1123} +1124 +1125impl StreamMeta { +1126 pub(crate) fn encoder(self, encode_length: bool) -> StreamMetaEncoder { +1127 StreamMetaEncoder { +1128 meta: self, +1129 encode_length, +1130 } +1131 } +1132 +1133 pub(crate) fn get_type(&self, encode_length: bool) -> StreamInfo { +1134 let mut ty = *StreamInfo::VALUES.start(); +1135 if self.offsets.start != 0 { +1136 ty |= 0x04; +1137 } +1138 if encode_length { +1139 ty |= 0x02; +1140 } +1141 if self.fin { +1142 ty |= 0x01; +1143 } +1144 StreamInfo(ty as u8) +1145 } +1146} +1147 +1148pub(crate) struct StreamMetaEncoder { +1149 pub(crate) meta: StreamMeta, +1150 encode_length: bool, +1151} +1152 +1153impl Encodable for StreamMetaEncoder { +1154 fn encode<W: BufMut>(&self, out: &mut W) { +1155 let StreamMetaEncoder { +1156 meta, +1157 encode_length, +1158 } = self; +1159 out.write_var(meta.get_type(*encode_length).0 as u64); // 1 byte +1160 out.write(meta.id); // <=8 bytes +1161 if meta.offsets.start != 0 { +1162 out.write_var(meta.offsets.start); // <=8 bytes +1163 } +1164 if *encode_length { +1165 out.write_var(meta.offsets.end - meta.offsets.start); // <=8 bytes +1166 } +1167 } +1168} 1169 -1170 pub(crate) fn get_type(&self, encode_length: bool) -> StreamInfo { -1171 let mut ty = *StreamInfo::VALUES.start(); -1172 if self.offsets.start != 0 { -1173 ty |= 0x04; -1174 } -1175 if encode_length { -1176 ty |= 0x02; -1177 } -1178 if self.fin { -1179 ty |= 0x01; -1180 } -1181 StreamInfo(ty as u8) -1182 } -1183} -1184 -1185pub(crate) struct StreamMetaEncoder { -1186 pub(crate) meta: StreamMeta, -1187 encode_length: bool, -1188} -1189 -1190impl Encodable for StreamMetaEncoder { -1191 fn encode<W: BufMut>(&self, out: &mut W) { -1192 let StreamMetaEncoder { -1193 meta, -1194 encode_length, -1195 } = self; -1196 out.write_var(meta.get_type(*encode_length).0 as u64); // 1 byte -1197 out.write(meta.id); // <=8 bytes -1198 if meta.offsets.start != 0 { -1199 out.write_var(meta.offsets.start); // <=8 bytes -1200 } -1201 if *encode_length { -1202 out.write_var(meta.offsets.end - meta.offsets.start); // <=8 bytes -1203 } -1204 } -1205} -1206 -1207/// A vector of [`StreamMeta`] with optimization for the single element case -1208pub(crate) type StreamMetaVec = TinyVec<[StreamMeta; 1]>; -1209 -1210#[derive(Debug, Clone)] -1211pub(crate) struct Crypto { -1212 pub(crate) offset: u64, -1213 pub(crate) data: Bytes, -1214} -1215 -1216impl Crypto { -1217 pub(crate) const SIZE_BOUND: usize = 17; -1218} -1219 -1220impl Encodable for Crypto { -1221 fn encode<W: BufMut>(&self, out: &mut W) { -1222 out.write(FrameType::Crypto); -1223 out.write_var(self.offset); -1224 out.write_var(self.data.len() as u64); -1225 out.put_slice(&self.data); -1226 } -1227} -1228 -1229#[derive(Debug, Clone)] -1230pub(crate) struct NewToken { -1231 pub(crate) token: Bytes, -1232} -1233 -1234impl Encodable for NewToken { -1235 fn encode<W: BufMut>(&self, out: &mut W) { -1236 out.write(FrameType::NewToken); -1237 out.write_var(self.token.len() as u64); -1238 out.put_slice(&self.token); -1239 } -1240} -1241 -1242impl NewToken { -1243 pub(crate) fn size(&self) -> usize { -1244 1 + VarInt::from_u64(self.token.len() as u64).unwrap().size() + self.token.len() -1245 } -1246} -1247 -1248#[derive(Debug, Clone)] -1249pub(crate) struct MaxPathId(pub(crate) PathId); -1250 -1251impl MaxPathId { -1252 pub(crate) const SIZE_BOUND: usize = -1253 FrameType::MaxPathId.size() + VarInt(u32::MAX as u64).size(); -1254} -1255 -1256impl Decodable for MaxPathId { -1257 fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> { -1258 Ok(Self(buf.get()?)) -1259 } -1260} -1261 -1262impl Encodable for MaxPathId { -1263 fn encode<B: BufMut>(&self, buf: &mut B) { -1264 buf.write(FrameType::MaxPathId); -1265 buf.write(self.0); -1266 } -1267} -1268 -1269#[derive(Debug, Clone, PartialEq, Eq)] -1270pub(crate) struct PathsBlocked(pub(crate) PathId); -1271 -1272impl PathsBlocked { -1273 pub(crate) const SIZE_BOUND: usize = -1274 FrameType::PathsBlocked.size() + VarInt(u32::MAX as u64).size(); -1275} -1276 -1277impl Encodable for PathsBlocked { -1278 fn encode<B: BufMut>(&self, buf: &mut B) { -1279 buf.write(FrameType::PathsBlocked); -1280 buf.write(self.0); -1281 } -1282} -1283 -1284impl Decodable for PathsBlocked { -1285 /// Decode [`Self`] from the buffer, provided that the frame type has been verified -1286 fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> { -1287 Ok(Self(buf.get()?)) -1288 } -1289} -1290 -1291#[derive(Debug, Clone, PartialEq, Eq)] -1292pub(crate) struct PathCidsBlocked { -1293 pub(crate) path_id: PathId, -1294 pub(crate) next_seq: VarInt, -1295} -1296 -1297impl PathCidsBlocked { -1298 pub(crate) const SIZE_BOUND: usize = -1299 FrameType::PathCidsBlocked.size() + VarInt(u32::MAX as u64).size() + VarInt::MAX.size(); -1300} -1301 -1302impl Decodable for PathCidsBlocked { -1303 fn decode<R: Buf>(buf: &mut R) -> coding::Result<Self> { -1304 Ok(Self { -1305 path_id: buf.get()?, -1306 next_seq: buf.get()?, -1307 }) -1308 } -1309} -1310 -1311impl Encodable for PathCidsBlocked { -1312 fn encode<W: BufMut>(&self, buf: &mut W) { -1313 buf.write(FrameType::PathCidsBlocked); -1314 buf.write(self.path_id); -1315 buf.write(self.next_seq); -1316 } -1317} -1318 -1319pub(crate) struct Iter { -1320 bytes: Bytes, -1321 last_ty: MaybeFrame, -1322} -1323 -1324impl Iter { -1325 pub(crate) fn new(payload: Bytes) -> Result<Self, TransportError> { -1326 if payload.is_empty() { -1327 // "An endpoint MUST treat receipt of a packet containing no frames as a -1328 // connection error of type PROTOCOL_VIOLATION." -1329 // https://www.rfc-editor.org/rfc/rfc9000.html#name-frames-and-frame-types -1330 return Err(TransportError::PROTOCOL_VIOLATION( -1331 "packet payload is empty", -1332 )); -1333 } -1334 -1335 Ok(Self { -1336 bytes: payload, -1337 last_ty: MaybeFrame::None, -1338 }) -1339 } -1340 -1341 fn take_len(&mut self) -> Result<Bytes, UnexpectedEnd> { -1342 let len = self.bytes.get_var()?; -1343 if len > self.bytes.remaining() as u64 { -1344 return Err(UnexpectedEnd); -1345 } -1346 Ok(self.bytes.split_to(len as usize)) -1347 } -1348 -1349 #[track_caller] -1350 fn try_next(&mut self) -> Result<Frame, IterErr> { -1351 self.last_ty = self.bytes.get()?; -1352 -1353 let ty = match self.last_ty { -1354 MaybeFrame::None => FrameType::Padding, -1355 MaybeFrame::Unknown(_other) => return Err(IterErr::InvalidFrameId), -1356 MaybeFrame::Known(frame_type) => frame_type, -1357 }; -1358 Ok(match ty { -1359 FrameType::Padding => Frame::Padding, -1360 FrameType::ResetStream => Frame::ResetStream(ResetStream { -1361 id: self.bytes.get()?, -1362 error_code: self.bytes.get()?, -1363 final_offset: self.bytes.get()?, -1364 }), -1365 FrameType::ConnectionClose => Frame::Close(Close::Connection(ConnectionClose { -1366 error_code: self.bytes.get()?, -1367 frame_type: self.bytes.get()?, -1368 reason: self.take_len()?, -1369 })), -1370 FrameType::ApplicationClose => Frame::Close(Close::Application(ApplicationClose { -1371 error_code: self.bytes.get()?, -1372 reason: self.take_len()?, -1373 })), -1374 FrameType::MaxData => Frame::MaxData(self.bytes.get()?), -1375 FrameType::MaxStreamData => Frame::MaxStreamData(self.bytes.get()?), -1376 FrameType::MaxStreamsBidi => Frame::MaxStreams(MaxStreams { -1377 dir: Dir::Bi, -1378 count: self.bytes.get_var()?, -1379 }), -1380 FrameType::MaxStreamsUni => Frame::MaxStreams(MaxStreams { -1381 dir: Dir::Uni, -1382 count: self.bytes.get_var()?, -1383 }), -1384 FrameType::Ping => Frame::Ping, -1385 FrameType::DataBlocked => Frame::DataBlocked { -1386 offset: self.bytes.get_var()?, -1387 }, -1388 FrameType::StreamDataBlocked => Frame::StreamDataBlocked { -1389 id: self.bytes.get()?, -1390 offset: self.bytes.get_var()?, -1391 }, -1392 FrameType::StreamsBlockedBidi => Frame::StreamsBlocked { -1393 dir: Dir::Bi, -1394 limit: self.bytes.get_var()?, -1395 }, -1396 FrameType::StreamsBlockedUni => Frame::StreamsBlocked { -1397 dir: Dir::Uni, -1398 limit: self.bytes.get_var()?, -1399 }, -1400 FrameType::StopSending => Frame::StopSending(StopSending { -1401 id: self.bytes.get()?, -1402 error_code: self.bytes.get()?, -1403 }), -1404 FrameType::RetireConnectionId | FrameType::PathRetireConnectionId => { -1405 Frame::RetireConnectionId(RetireConnectionId::decode( -1406 &mut self.bytes, -1407 ty == FrameType::PathRetireConnectionId, -1408 )?) -1409 } -1410 FrameType::Ack | FrameType::AckEcn => { -1411 let largest = self.bytes.get_var()?; -1412 let delay = self.bytes.get_var()?; -1413 let extra_blocks = self.bytes.get_var()? as usize; -1414 let n = scan_ack_blocks(&self.bytes, largest, extra_blocks)?; -1415 Frame::Ack(Ack { -1416 delay, -1417 largest, -1418 additional: self.bytes.split_to(n), -1419 ecn: if ty != FrameType::AckEcn && ty != FrameType::PathAckEcn { -1420 None -1421 } else { -1422 Some(EcnCounts { -1423 ect0: self.bytes.get_var()?, -1424 ect1: self.bytes.get_var()?, -1425 ce: self.bytes.get_var()?, -1426 }) -1427 }, -1428 }) -1429 } -1430 FrameType::PathAck | FrameType::PathAckEcn => { -1431 let path_id = self.bytes.get()?; -1432 let largest = self.bytes.get_var()?; -1433 let delay = self.bytes.get_var()?; -1434 let extra_blocks = self.bytes.get_var()? as usize; -1435 let n = scan_ack_blocks(&self.bytes, largest, extra_blocks)?; -1436 Frame::PathAck(PathAck { -1437 path_id, -1438 delay, -1439 largest, -1440 additional: self.bytes.split_to(n), -1441 ecn: if ty != FrameType::AckEcn && ty != FrameType::PathAckEcn { -1442 None -1443 } else { -1444 Some(EcnCounts { -1445 ect0: self.bytes.get_var()?, -1446 ect1: self.bytes.get_var()?, -1447 ce: self.bytes.get_var()?, -1448 }) -1449 }, -1450 }) -1451 } -1452 FrameType::PathChallenge => Frame::PathChallenge(self.bytes.get()?), -1453 FrameType::PathResponse => Frame::PathResponse(self.bytes.get()?), -1454 FrameType::NewConnectionId | FrameType::PathNewConnectionId => { -1455 let read_path = ty == FrameType::PathNewConnectionId; -1456 Frame::NewConnectionId(NewConnectionId::read(&mut self.bytes, read_path)?) +1170/// A vector of [`StreamMeta`] with optimization for the single element case +1171pub(crate) type StreamMetaVec = TinyVec<[StreamMeta; 1]>; +1172 +1173#[derive(Debug, Clone)] +1174pub(crate) struct Crypto { +1175 pub(crate) offset: u64, +1176 pub(crate) data: Bytes, +1177} +1178 +1179impl Crypto { +1180 pub(crate) const SIZE_BOUND: usize = 17; +1181} +1182 +1183impl Encodable for Crypto { +1184 fn encode<W: BufMut>(&self, out: &mut W) { +1185 out.write(FrameType::Crypto); +1186 out.write_var(self.offset); +1187 out.write_var(self.data.len() as u64); +1188 out.put_slice(&self.data); +1189 } +1190} +1191 +1192#[derive(Debug, Clone)] +1193pub(crate) struct NewToken { +1194 pub(crate) token: Bytes, +1195} +1196 +1197impl Encodable for NewToken { +1198 fn encode<W: BufMut>(&self, out: &mut W) { +1199 out.write(FrameType::NewToken); +1200 out.write_var(self.token.len() as u64); +1201 out.put_slice(&self.token); +1202 } +1203} +1204 +1205impl NewToken { +1206 pub(crate) fn size(&self) -> usize { +1207 1 + VarInt::from_u64(self.token.len() as u64).unwrap().size() + self.token.len() +1208 } +1209} +1210 +1211#[derive(Debug, Clone)] +1212pub(crate) struct MaxPathId(pub(crate) PathId); +1213 +1214impl MaxPathId { +1215 pub(crate) const SIZE_BOUND: usize = +1216 FrameType::MaxPathId.size() + VarInt(u32::MAX as u64).size(); +1217} +1218 +1219impl Decodable for MaxPathId { +1220 fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> { +1221 Ok(Self(buf.get()?)) +1222 } +1223} +1224 +1225impl Encodable for MaxPathId { +1226 fn encode<B: BufMut>(&self, buf: &mut B) { +1227 buf.write(FrameType::MaxPathId); +1228 buf.write(self.0); +1229 } +1230} +1231 +1232#[derive(Debug, Clone, PartialEq, Eq)] +1233pub(crate) struct PathsBlocked(pub(crate) PathId); +1234 +1235impl PathsBlocked { +1236 pub(crate) const SIZE_BOUND: usize = +1237 FrameType::PathsBlocked.size() + VarInt(u32::MAX as u64).size(); +1238} +1239 +1240impl Encodable for PathsBlocked { +1241 fn encode<B: BufMut>(&self, buf: &mut B) { +1242 buf.write(FrameType::PathsBlocked); +1243 buf.write(self.0); +1244 } +1245} +1246 +1247impl Decodable for PathsBlocked { +1248 /// Decode [`Self`] from the buffer, provided that the frame type has been verified +1249 fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> { +1250 Ok(Self(buf.get()?)) +1251 } +1252} +1253 +1254#[derive(Debug, Clone, PartialEq, Eq)] +1255pub(crate) struct PathCidsBlocked { +1256 pub(crate) path_id: PathId, +1257 pub(crate) next_seq: VarInt, +1258} +1259 +1260impl PathCidsBlocked { +1261 pub(crate) const SIZE_BOUND: usize = +1262 FrameType::PathCidsBlocked.size() + VarInt(u32::MAX as u64).size() + VarInt::MAX.size(); +1263} +1264 +1265impl Decodable for PathCidsBlocked { +1266 fn decode<R: Buf>(buf: &mut R) -> coding::Result<Self> { +1267 Ok(Self { +1268 path_id: buf.get()?, +1269 next_seq: buf.get()?, +1270 }) +1271 } +1272} +1273 +1274impl Encodable for PathCidsBlocked { +1275 fn encode<W: BufMut>(&self, buf: &mut W) { +1276 buf.write(FrameType::PathCidsBlocked); +1277 buf.write(self.path_id); +1278 buf.write(self.next_seq); +1279 } +1280} +1281 +1282pub(crate) struct Iter { +1283 bytes: Bytes, +1284 last_ty: MaybeFrame, +1285} +1286 +1287impl Iter { +1288 pub(crate) fn new(payload: Bytes) -> Result<Self, TransportError> { +1289 if payload.is_empty() { +1290 // "An endpoint MUST treat receipt of a packet containing no frames as a +1291 // connection error of type PROTOCOL_VIOLATION." +1292 // https://www.rfc-editor.org/rfc/rfc9000.html#name-frames-and-frame-types +1293 return Err(TransportError::PROTOCOL_VIOLATION( +1294 "packet payload is empty", +1295 )); +1296 } +1297 +1298 Ok(Self { +1299 bytes: payload, +1300 last_ty: MaybeFrame::None, +1301 }) +1302 } +1303 +1304 fn take_len(&mut self) -> Result<Bytes, UnexpectedEnd> { +1305 let len = self.bytes.get_var()?; +1306 if len > self.bytes.remaining() as u64 { +1307 return Err(UnexpectedEnd); +1308 } +1309 Ok(self.bytes.split_to(len as usize)) +1310 } +1311 +1312 #[track_caller] +1313 fn try_next(&mut self) -> Result<Frame, IterErr> { +1314 self.last_ty = self.bytes.get()?; +1315 +1316 let ty = match self.last_ty { +1317 MaybeFrame::None => FrameType::Padding, +1318 MaybeFrame::Unknown(_other) => return Err(IterErr::InvalidFrameId), +1319 MaybeFrame::Known(frame_type) => frame_type, +1320 }; +1321 Ok(match ty { +1322 FrameType::Padding => Frame::Padding, +1323 FrameType::ResetStream => Frame::ResetStream(ResetStream { +1324 id: self.bytes.get()?, +1325 error_code: self.bytes.get()?, +1326 final_offset: self.bytes.get()?, +1327 }), +1328 FrameType::ConnectionClose => Frame::Close(Close::Connection(ConnectionClose { +1329 error_code: self.bytes.get()?, +1330 frame_type: self.bytes.get()?, +1331 reason: self.take_len()?, +1332 })), +1333 FrameType::ApplicationClose => Frame::Close(Close::Application(ApplicationClose { +1334 error_code: self.bytes.get()?, +1335 reason: self.take_len()?, +1336 })), +1337 FrameType::MaxData => Frame::MaxData(self.bytes.get()?), +1338 FrameType::MaxStreamData => Frame::MaxStreamData(self.bytes.get()?), +1339 FrameType::MaxStreamsBidi => Frame::MaxStreams(MaxStreams { +1340 dir: Dir::Bi, +1341 count: self.bytes.get_var()?, +1342 }), +1343 FrameType::MaxStreamsUni => Frame::MaxStreams(MaxStreams { +1344 dir: Dir::Uni, +1345 count: self.bytes.get_var()?, +1346 }), +1347 FrameType::Ping => Frame::Ping, +1348 FrameType::DataBlocked => Frame::DataBlocked { +1349 offset: self.bytes.get_var()?, +1350 }, +1351 FrameType::StreamDataBlocked => Frame::StreamDataBlocked { +1352 id: self.bytes.get()?, +1353 offset: self.bytes.get_var()?, +1354 }, +1355 FrameType::StreamsBlockedBidi => Frame::StreamsBlocked { +1356 dir: Dir::Bi, +1357 limit: self.bytes.get_var()?, +1358 }, +1359 FrameType::StreamsBlockedUni => Frame::StreamsBlocked { +1360 dir: Dir::Uni, +1361 limit: self.bytes.get_var()?, +1362 }, +1363 FrameType::StopSending => Frame::StopSending(StopSending { +1364 id: self.bytes.get()?, +1365 error_code: self.bytes.get()?, +1366 }), +1367 FrameType::RetireConnectionId | FrameType::PathRetireConnectionId => { +1368 Frame::RetireConnectionId(RetireConnectionId::decode( +1369 &mut self.bytes, +1370 ty == FrameType::PathRetireConnectionId, +1371 )?) +1372 } +1373 FrameType::Ack | FrameType::AckEcn => { +1374 let largest = self.bytes.get_var()?; +1375 let delay = self.bytes.get_var()?; +1376 let extra_blocks = self.bytes.get_var()? as usize; +1377 let n = scan_ack_blocks(&self.bytes, largest, extra_blocks)?; +1378 Frame::Ack(Ack { +1379 delay, +1380 largest, +1381 additional: self.bytes.split_to(n), +1382 ecn: if ty != FrameType::AckEcn && ty != FrameType::PathAckEcn { +1383 None +1384 } else { +1385 Some(EcnCounts { +1386 ect0: self.bytes.get_var()?, +1387 ect1: self.bytes.get_var()?, +1388 ce: self.bytes.get_var()?, +1389 }) +1390 }, +1391 }) +1392 } +1393 FrameType::PathAck | FrameType::PathAckEcn => { +1394 let path_id = self.bytes.get()?; +1395 let largest = self.bytes.get_var()?; +1396 let delay = self.bytes.get_var()?; +1397 let extra_blocks = self.bytes.get_var()? as usize; +1398 let n = scan_ack_blocks(&self.bytes, largest, extra_blocks)?; +1399 Frame::PathAck(PathAck { +1400 path_id, +1401 delay, +1402 largest, +1403 additional: self.bytes.split_to(n), +1404 ecn: if ty != FrameType::AckEcn && ty != FrameType::PathAckEcn { +1405 None +1406 } else { +1407 Some(EcnCounts { +1408 ect0: self.bytes.get_var()?, +1409 ect1: self.bytes.get_var()?, +1410 ce: self.bytes.get_var()?, +1411 }) +1412 }, +1413 }) +1414 } +1415 FrameType::PathChallenge => Frame::PathChallenge(self.bytes.get()?), +1416 FrameType::PathResponse => Frame::PathResponse(self.bytes.get()?), +1417 FrameType::NewConnectionId | FrameType::PathNewConnectionId => { +1418 let read_path = ty == FrameType::PathNewConnectionId; +1419 Frame::NewConnectionId(NewConnectionId::read(&mut self.bytes, read_path)?) +1420 } +1421 FrameType::Crypto => Frame::Crypto(Crypto { +1422 offset: self.bytes.get_var()?, +1423 data: self.take_len()?, +1424 }), +1425 FrameType::NewToken => Frame::NewToken(NewToken { +1426 token: self.take_len()?, +1427 }), +1428 FrameType::HandshakeDone => Frame::HandshakeDone, +1429 FrameType::AckFrequency => Frame::AckFrequency(AckFrequency { +1430 sequence: self.bytes.get()?, +1431 ack_eliciting_threshold: self.bytes.get()?, +1432 request_max_ack_delay: self.bytes.get()?, +1433 reordering_threshold: self.bytes.get()?, +1434 }), +1435 FrameType::ImmediateAck => Frame::ImmediateAck, +1436 FrameType::ObservedIpv4Addr | FrameType::ObservedIpv6Addr => { +1437 let is_ipv6 = ty == FrameType::ObservedIpv6Addr; +1438 let observed = ObservedAddr::read(&mut self.bytes, is_ipv6)?; +1439 Frame::ObservedAddr(observed) +1440 } +1441 FrameType::PathAbandon => Frame::PathAbandon(PathAbandon::decode(&mut self.bytes)?), +1442 FrameType::PathStatusAvailable => { +1443 Frame::PathStatusAvailable(PathStatusAvailable::decode(&mut self.bytes)?) +1444 } +1445 FrameType::PathStatusBackup => { +1446 Frame::PathStatusBackup(PathStatusBackup::decode(&mut self.bytes)?) +1447 } +1448 FrameType::MaxPathId => Frame::MaxPathId(MaxPathId::decode(&mut self.bytes)?), +1449 FrameType::PathsBlocked => Frame::PathsBlocked(PathsBlocked::decode(&mut self.bytes)?), +1450 FrameType::PathCidsBlocked => { +1451 Frame::PathCidsBlocked(PathCidsBlocked::decode(&mut self.bytes)?) +1452 } +1453 FrameType::AddIpv4Address | FrameType::AddIpv6Address => { +1454 let is_ipv6 = ty == FrameType::AddIpv6Address; +1455 let add_address = AddAddress::read(&mut self.bytes, is_ipv6)?; +1456 Frame::AddAddress(add_address) 1457 } -1458 FrameType::Crypto => Frame::Crypto(Crypto { -1459 offset: self.bytes.get_var()?, -1460 data: self.take_len()?, -1461 }), -1462 FrameType::NewToken => Frame::NewToken(NewToken { -1463 token: self.take_len()?, -1464 }), -1465 FrameType::HandshakeDone => Frame::HandshakeDone, -1466 FrameType::AckFrequency => Frame::AckFrequency(AckFrequency { -1467 sequence: self.bytes.get()?, -1468 ack_eliciting_threshold: self.bytes.get()?, -1469 request_max_ack_delay: self.bytes.get()?, -1470 reordering_threshold: self.bytes.get()?, -1471 }), -1472 FrameType::ImmediateAck => Frame::ImmediateAck, -1473 FrameType::ObservedIpv4Addr | FrameType::ObservedIpv6Addr => { -1474 let is_ipv6 = ty == FrameType::ObservedIpv6Addr; -1475 let observed = ObservedAddr::read(&mut self.bytes, is_ipv6)?; -1476 Frame::ObservedAddr(observed) -1477 } -1478 FrameType::PathAbandon => Frame::PathAbandon(PathAbandon::decode(&mut self.bytes)?), -1479 FrameType::PathStatusAvailable => { -1480 Frame::PathStatusAvailable(PathStatusAvailable::decode(&mut self.bytes)?) -1481 } -1482 FrameType::PathStatusBackup => { -1483 Frame::PathStatusBackup(PathStatusBackup::decode(&mut self.bytes)?) -1484 } -1485 FrameType::MaxPathId => Frame::MaxPathId(MaxPathId::decode(&mut self.bytes)?), -1486 FrameType::PathsBlocked => Frame::PathsBlocked(PathsBlocked::decode(&mut self.bytes)?), -1487 FrameType::PathCidsBlocked => { -1488 Frame::PathCidsBlocked(PathCidsBlocked::decode(&mut self.bytes)?) -1489 } -1490 FrameType::AddIpv4Address | FrameType::AddIpv6Address => { -1491 let is_ipv6 = ty == FrameType::AddIpv6Address; -1492 let add_address = AddAddress::read(&mut self.bytes, is_ipv6)?; -1493 Frame::AddAddress(add_address) -1494 } -1495 FrameType::ReachOutAtIpv4 | FrameType::ReachOutAtIpv6 => { -1496 let is_ipv6 = ty == FrameType::ReachOutAtIpv6; -1497 let reach_out = ReachOut::read(&mut self.bytes, is_ipv6)?; -1498 Frame::ReachOut(reach_out) -1499 } -1500 FrameType::RemoveAddress => Frame::RemoveAddress(RemoveAddress::read(&mut self.bytes)?), -1501 FrameType::Stream(s) => Frame::Stream(Stream { -1502 id: self.bytes.get()?, -1503 offset: if s.off() { self.bytes.get_var()? } else { 0 }, -1504 fin: s.fin(), -1505 data: if s.len() { -1506 self.take_len()? -1507 } else { -1508 self.take_remaining() -1509 }, -1510 }), -1511 FrameType::Datagram(d) => Frame::Datagram(Datagram { -1512 data: if d.len() { -1513 self.take_len()? -1514 } else { -1515 self.take_remaining() -1516 }, -1517 }), -1518 }) -1519 } -1520 -1521 fn take_remaining(&mut self) -> Bytes { -1522 mem::take(&mut self.bytes) -1523 } -1524} -1525 -1526impl Iterator for Iter { -1527 type Item = Result<Frame, InvalidFrame>; -1528 fn next(&mut self) -> Option<Self::Item> { -1529 if !self.bytes.has_remaining() { -1530 return None; -1531 } -1532 match self.try_next() { -1533 Ok(x) => Some(Ok(x)), -1534 Err(e) => { -1535 // Corrupt frame, skip it and everything that follows -1536 self.bytes.clear(); -1537 Some(Err(InvalidFrame { -1538 ty: self.last_ty, -1539 reason: e.reason(), -1540 })) -1541 } -1542 } -1543 } -1544} -1545 -1546#[derive(Debug)] -1547pub(crate) struct InvalidFrame { -1548 pub(crate) ty: MaybeFrame, -1549 pub(crate) reason: &'static str, -1550} -1551 -1552impl From<InvalidFrame> for TransportError { -1553 fn from(err: InvalidFrame) -> Self { -1554 let mut te = Self::FRAME_ENCODING_ERROR(err.reason); -1555 te.frame = err.ty; -1556 te -1557 } -1558} -1559 -1560/// Validate exactly `n` ACK ranges in `buf` and return the number of bytes they cover -1561fn scan_ack_blocks(mut buf: &[u8], largest: u64, n: usize) -> Result<usize, IterErr> { -1562 let total_len = buf.remaining(); -1563 let first_block = buf.get_var()?; -1564 let mut smallest = largest.checked_sub(first_block).ok_or(IterErr::Malformed)?; -1565 for _ in 0..n { -1566 let gap = buf.get_var()?; -1567 smallest = smallest.checked_sub(gap + 2).ok_or(IterErr::Malformed)?; -1568 let block = buf.get_var()?; -1569 smallest = smallest.checked_sub(block).ok_or(IterErr::Malformed)?; +1458 FrameType::ReachOutAtIpv4 | FrameType::ReachOutAtIpv6 => { +1459 let is_ipv6 = ty == FrameType::ReachOutAtIpv6; +1460 let reach_out = ReachOut::read(&mut self.bytes, is_ipv6)?; +1461 Frame::ReachOut(reach_out) +1462 } +1463 FrameType::RemoveAddress => Frame::RemoveAddress(RemoveAddress::read(&mut self.bytes)?), +1464 FrameType::Stream(s) => Frame::Stream(Stream { +1465 id: self.bytes.get()?, +1466 offset: if s.off() { self.bytes.get_var()? } else { 0 }, +1467 fin: s.fin(), +1468 data: if s.len() { +1469 self.take_len()? +1470 } else { +1471 self.take_remaining() +1472 }, +1473 }), +1474 FrameType::Datagram(d) => Frame::Datagram(Datagram { +1475 data: if d.len() { +1476 self.take_len()? +1477 } else { +1478 self.take_remaining() +1479 }, +1480 }), +1481 }) +1482 } +1483 +1484 fn take_remaining(&mut self) -> Bytes { +1485 mem::take(&mut self.bytes) +1486 } +1487} +1488 +1489impl Iterator for Iter { +1490 type Item = Result<Frame, InvalidFrame>; +1491 fn next(&mut self) -> Option<Self::Item> { +1492 if !self.bytes.has_remaining() { +1493 return None; +1494 } +1495 match self.try_next() { +1496 Ok(x) => Some(Ok(x)), +1497 Err(e) => { +1498 // Corrupt frame, skip it and everything that follows +1499 self.bytes.clear(); +1500 Some(Err(InvalidFrame { +1501 ty: self.last_ty, +1502 reason: e.reason(), +1503 })) +1504 } +1505 } +1506 } +1507} +1508 +1509#[derive(Debug)] +1510pub(crate) struct InvalidFrame { +1511 pub(crate) ty: MaybeFrame, +1512 pub(crate) reason: &'static str, +1513} +1514 +1515impl From<InvalidFrame> for TransportError { +1516 fn from(err: InvalidFrame) -> Self { +1517 let mut te = Self::FRAME_ENCODING_ERROR(err.reason); +1518 te.frame = err.ty; +1519 te +1520 } +1521} +1522 +1523/// Validate exactly `n` ACK ranges in `buf` and return the number of bytes they cover +1524fn scan_ack_blocks(mut buf: &[u8], largest: u64, n: usize) -> Result<usize, IterErr> { +1525 let total_len = buf.remaining(); +1526 let first_block = buf.get_var()?; +1527 let mut smallest = largest.checked_sub(first_block).ok_or(IterErr::Malformed)?; +1528 for _ in 0..n { +1529 let gap = buf.get_var()?; +1530 smallest = smallest.checked_sub(gap + 2).ok_or(IterErr::Malformed)?; +1531 let block = buf.get_var()?; +1532 smallest = smallest.checked_sub(block).ok_or(IterErr::Malformed)?; +1533 } +1534 Ok(total_len - buf.remaining()) +1535} +1536 +1537#[derive(Debug)] +1538enum IterErr { +1539 UnexpectedEnd, +1540 InvalidFrameId, +1541 Malformed, +1542} +1543 +1544impl IterErr { +1545 fn reason(&self) -> &'static str { +1546 use IterErr::*; +1547 match *self { +1548 UnexpectedEnd => "unexpected end", +1549 InvalidFrameId => "invalid frame ID", +1550 Malformed => "malformed", +1551 } +1552 } +1553} +1554 +1555impl From<UnexpectedEnd> for IterErr { +1556 fn from(_: UnexpectedEnd) -> Self { +1557 Self::UnexpectedEnd +1558 } +1559} +1560 +1561#[derive(Debug, Clone)] +1562pub struct AckIter<'a> { +1563 largest: u64, +1564 data: &'a [u8], +1565} +1566 +1567impl<'a> AckIter<'a> { +1568 fn new(largest: u64, data: &'a [u8]) -> Self { +1569 Self { largest, data } 1570 } -1571 Ok(total_len - buf.remaining()) -1572} -1573 -1574#[derive(Debug)] -1575enum IterErr { -1576 UnexpectedEnd, -1577 InvalidFrameId, -1578 Malformed, -1579} -1580 -1581impl IterErr { -1582 fn reason(&self) -> &'static str { -1583 use IterErr::*; -1584 match *self { -1585 UnexpectedEnd => "unexpected end", -1586 InvalidFrameId => "invalid frame ID", -1587 Malformed => "malformed", -1588 } -1589 } -1590} -1591 -1592impl From<UnexpectedEnd> for IterErr { -1593 fn from(_: UnexpectedEnd) -> Self { -1594 Self::UnexpectedEnd -1595 } -1596} -1597 -1598#[derive(Debug, Clone)] -1599pub struct AckIter<'a> { -1600 largest: u64, -1601 data: &'a [u8], -1602} -1603 -1604impl<'a> AckIter<'a> { -1605 fn new(largest: u64, data: &'a [u8]) -> Self { -1606 Self { largest, data } -1607 } +1571} +1572 +1573impl Iterator for AckIter<'_> { +1574 type Item = RangeInclusive<u64>; +1575 fn next(&mut self) -> Option<RangeInclusive<u64>> { +1576 if !self.data.has_remaining() { +1577 return None; +1578 } +1579 let block = self.data.get_var().unwrap(); +1580 let largest = self.largest; +1581 if let Ok(gap) = self.data.get_var() { +1582 self.largest -= block + gap + 2; +1583 } +1584 Some(largest - block..=largest) +1585 } +1586} +1587 +1588#[allow(unreachable_pub)] // fuzzing only +1589#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] +1590#[derive(Debug, Copy, Clone)] +1591pub struct ResetStream { +1592 pub(crate) id: StreamId, +1593 pub(crate) error_code: VarInt, +1594 pub(crate) final_offset: VarInt, +1595} +1596 +1597impl FrameStruct for ResetStream { +1598 const SIZE_BOUND: usize = 1 + 8 + 8 + 8; +1599} +1600 +1601impl Encodable for ResetStream { +1602 fn encode<W: BufMut>(&self, out: &mut W) { +1603 out.write(FrameType::ResetStream); // 1 byte +1604 out.write(self.id); // <= 8 bytes +1605 out.write(self.error_code); // <= 8 bytes +1606 out.write(self.final_offset); // <= 8 bytes +1607 } 1608} 1609 -1610impl Iterator for AckIter<'_> { -1611 type Item = RangeInclusive<u64>; -1612 fn next(&mut self) -> Option<RangeInclusive<u64>> { -1613 if !self.data.has_remaining() { -1614 return None; -1615 } -1616 let block = self.data.get_var().unwrap(); -1617 let largest = self.largest; -1618 if let Ok(gap) = self.data.get_var() { -1619 self.largest -= block + gap + 2; -1620 } -1621 Some(largest - block..=largest) -1622 } -1623} -1624 -1625#[allow(unreachable_pub)] // fuzzing only -1626#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] -1627#[derive(Debug, Copy, Clone)] -1628pub struct ResetStream { -1629 pub(crate) id: StreamId, -1630 pub(crate) error_code: VarInt, -1631 pub(crate) final_offset: VarInt, -1632} -1633 -1634impl FrameStruct for ResetStream { -1635 const SIZE_BOUND: usize = 1 + 8 + 8 + 8; -1636} -1637 -1638impl Encodable for ResetStream { -1639 fn encode<W: BufMut>(&self, out: &mut W) { -1640 out.write(FrameType::ResetStream); // 1 byte -1641 out.write(self.id); // <= 8 bytes -1642 out.write(self.error_code); // <= 8 bytes -1643 out.write(self.final_offset); // <= 8 bytes -1644 } -1645} -1646 -1647#[derive(Debug, Copy, Clone)] -1648pub(crate) struct StopSending { -1649 pub(crate) id: StreamId, -1650 pub(crate) error_code: VarInt, -1651} -1652 -1653impl FrameStruct for StopSending { -1654 const SIZE_BOUND: usize = 1 + 8 + 8; -1655} -1656 -1657impl Encodable for StopSending { -1658 fn encode<W: BufMut>(&self, out: &mut W) { -1659 out.write(FrameType::StopSending); // 1 byte -1660 out.write(self.id); // <= 8 bytes -1661 out.write(self.error_code) // <= 8 bytes -1662 } -1663} -1664 -1665#[derive(Debug, Copy, Clone, PartialEq, Eq)] -1666pub(crate) struct NewConnectionId { -1667 pub(crate) path_id: Option<PathId>, -1668 pub(crate) sequence: u64, -1669 pub(crate) retire_prior_to: u64, -1670 pub(crate) id: ConnectionId, -1671 pub(crate) reset_token: ResetToken, -1672} -1673 -1674impl NewConnectionId { -1675 /// Maximum size of this frame when the frame type is [`FrameType::NewConnectionId`], -1676 pub(crate) const SIZE_BOUND: usize = { -1677 let type_len = FrameType::NewConnectionId.size(); -1678 let seq_max_len = 8usize; -1679 let retire_prior_to_max_len = 8usize; -1680 let cid_len_len = 1; -1681 let cid_len = 160; -1682 let reset_token_len = 16; -1683 type_len + seq_max_len + retire_prior_to_max_len + cid_len_len + cid_len + reset_token_len -1684 }; -1685 -1686 /// Maximum size of this frame when the frame type is [`FrameType::PathNewConnectionId`], -1687 pub(crate) const SIZE_BOUND_MULTIPATH: usize = { -1688 let type_len = FrameType::PathNewConnectionId.size(); -1689 let path_id_len = VarInt::from_u32(u32::MAX).size(); -1690 let seq_max_len = 8usize; -1691 let retire_prior_to_max_len = 8usize; -1692 let cid_len_len = 1; -1693 let cid_len = 160; -1694 let reset_token_len = 16; -1695 type_len -1696 + path_id_len -1697 + seq_max_len -1698 + retire_prior_to_max_len -1699 + cid_len_len -1700 + cid_len -1701 + reset_token_len -1702 }; -1703 -1704 pub(crate) fn get_type(&self) -> FrameType { -1705 if self.path_id.is_some() { -1706 FrameType::PathNewConnectionId -1707 } else { -1708 FrameType::NewConnectionId -1709 } -1710 } -1711 -1712 /// Returns the maximum encoded size on the wire. -1713 /// -1714 /// This is a rough upper estimate, does not squeeze every last byte out. -1715 pub(crate) const fn size_bound(path_new_cid: bool, cid_len: usize) -> usize { -1716 let upper_bound = match path_new_cid { -1717 true => Self::SIZE_BOUND_MULTIPATH, -1718 false => Self::SIZE_BOUND, -1719 }; -1720 // instead of using the maximum cid len, use the provided one -1721 upper_bound - 160 + cid_len -1722 } -1723 -1724 fn read<R: Buf>(bytes: &mut R, read_path: bool) -> Result<Self, IterErr> { -1725 let path_id = if read_path { Some(bytes.get()?) } else { None }; -1726 let sequence = bytes.get_var()?; -1727 let retire_prior_to = bytes.get_var()?; -1728 if retire_prior_to > sequence { -1729 return Err(IterErr::Malformed); -1730 } -1731 let length = bytes.get::<u8>()? as usize; -1732 if length > MAX_CID_SIZE || length == 0 { -1733 return Err(IterErr::Malformed); -1734 } -1735 if length > bytes.remaining() { -1736 return Err(IterErr::UnexpectedEnd); -1737 } -1738 let mut stage = [0; MAX_CID_SIZE]; -1739 bytes.copy_to_slice(&mut stage[0..length]); -1740 let id = ConnectionId::new(&stage[..length]); -1741 if bytes.remaining() < 16 { -1742 return Err(IterErr::UnexpectedEnd); -1743 } -1744 let mut reset_token = [0; RESET_TOKEN_SIZE]; -1745 bytes.copy_to_slice(&mut reset_token); -1746 Ok(Self { -1747 path_id, -1748 sequence, -1749 retire_prior_to, -1750 id, -1751 reset_token: reset_token.into(), -1752 }) -1753 } -1754 -1755 pub(crate) fn issued(&self) -> crate::shared::IssuedCid { -1756 crate::shared::IssuedCid { -1757 path_id: self.path_id.unwrap_or_default(), -1758 sequence: self.sequence, -1759 id: self.id, -1760 reset_token: self.reset_token, -1761 } -1762 } -1763} -1764 -1765impl Encodable for NewConnectionId { -1766 fn encode<W: BufMut>(&self, out: &mut W) { -1767 out.write(self.get_type()); -1768 if let Some(id) = self.path_id { -1769 out.write(id); -1770 } -1771 out.write_var(self.sequence); -1772 out.write_var(self.retire_prior_to); -1773 out.write(self.id.len() as u8); -1774 out.put_slice(&self.id); -1775 out.put_slice(&self.reset_token); -1776 } -1777} -1778 -1779impl FrameStruct for NewConnectionId { -1780 const SIZE_BOUND: usize = 1 + 8 + 8 + 1 + MAX_CID_SIZE + RESET_TOKEN_SIZE; -1781} -1782 -1783/// An unreliable datagram -1784#[derive(Debug, Clone)] -1785pub struct Datagram { -1786 /// Payload -1787 pub data: Bytes, -1788} -1789 -1790impl FrameStruct for Datagram { -1791 const SIZE_BOUND: usize = 1 + 8; -1792} -1793 -1794impl Datagram { -1795 pub(crate) fn size(&self, length: bool) -> usize { -1796 1 + if length { -1797 VarInt::from_u64(self.data.len() as u64).unwrap().size() -1798 } else { -1799 0 -1800 } + self.data.len() -1801 } -1802} -1803 -1804impl Encodable for Datagram { -1805 fn encode<B: BufMut>(&self, out: &mut B) { -1806 // A datagram is encoded only after this is verified. -1807 const ENCODE_LEN: bool = true; -1808 out.write(FrameType::Datagram(DatagramInfo( -1809 *DatagramInfo::VALUES.start() as u8 | u8::from(ENCODE_LEN), -1810 ))); // 1 byte -1811 // Safe to unwrap because we check length sanity before queueing datagrams -1812 out.write(VarInt::from_u64(self.data.len() as u64).unwrap()); // <= 8 bytes -1813 out.put_slice(&self.data); -1814 } -1815} -1816 -1817#[derive(Debug, Copy, Clone, PartialEq, Eq)] -1818pub(crate) struct AckFrequency { -1819 pub(crate) sequence: VarInt, -1820 pub(crate) ack_eliciting_threshold: VarInt, -1821 pub(crate) request_max_ack_delay: VarInt, -1822 pub(crate) reordering_threshold: VarInt, -1823} -1824 -1825impl Encodable for AckFrequency { -1826 fn encode<W: BufMut>(&self, buf: &mut W) { -1827 buf.write(FrameType::AckFrequency); -1828 buf.write(self.sequence); -1829 buf.write(self.ack_eliciting_threshold); -1830 buf.write(self.request_max_ack_delay); -1831 buf.write(self.reordering_threshold); -1832 } -1833} -1834 -1835/* Address Discovery https://datatracker.ietf.org/doc/draft-seemann-quic-address-discovery/ */ -1836 -1837/// Conjunction of the information contained in the address discovery frames -1838/// ([`FrameType::ObservedIpv4Addr`], [`FrameType::ObservedIpv6Addr`]). -1839#[derive(Debug, PartialEq, Eq, Clone)] -1840pub(crate) struct ObservedAddr { -1841 /// Monotonically increasing integer within the same connection. -1842 pub(crate) seq_no: VarInt, -1843 /// Reported observed address. -1844 pub(crate) ip: IpAddr, -1845 /// Reported observed port. -1846 pub(crate) port: u16, -1847} -1848 -1849impl ObservedAddr { -1850 pub(crate) fn new<N: Into<VarInt>>(remote: std::net::SocketAddr, seq_no: N) -> Self { -1851 Self { -1852 ip: remote.ip(), -1853 port: remote.port(), -1854 seq_no: seq_no.into(), -1855 } -1856 } -1857 -1858 /// Get the [`FrameType`] for this frame. -1859 pub(crate) fn get_type(&self) -> FrameType { -1860 if self.ip.is_ipv6() { -1861 FrameType::ObservedIpv6Addr -1862 } else { -1863 FrameType::ObservedIpv4Addr -1864 } -1865 } -1866 -1867 /// Compute the number of bytes needed to encode the frame. -1868 pub(crate) fn size(&self) -> usize { -1869 let type_size = self.get_type().size(); -1870 let req_id_bytes = self.seq_no.size(); -1871 let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 }; -1872 let port_bytes = 2; -1873 type_size + req_id_bytes + ip_bytes + port_bytes -1874 } +1610#[derive(Debug, Copy, Clone)] +1611pub(crate) struct StopSending { +1612 pub(crate) id: StreamId, +1613 pub(crate) error_code: VarInt, +1614} +1615 +1616impl FrameStruct for StopSending { +1617 const SIZE_BOUND: usize = 1 + 8 + 8; +1618} +1619 +1620impl Encodable for StopSending { +1621 fn encode<W: BufMut>(&self, out: &mut W) { +1622 out.write(FrameType::StopSending); // 1 byte +1623 out.write(self.id); // <= 8 bytes +1624 out.write(self.error_code) // <= 8 bytes +1625 } +1626} +1627 +1628#[derive(Debug, Copy, Clone, PartialEq, Eq)] +1629pub(crate) struct NewConnectionId { +1630 pub(crate) path_id: Option<PathId>, +1631 pub(crate) sequence: u64, +1632 pub(crate) retire_prior_to: u64, +1633 pub(crate) id: ConnectionId, +1634 pub(crate) reset_token: ResetToken, +1635} +1636 +1637impl NewConnectionId { +1638 /// Maximum size of this frame when the frame type is [`FrameType::NewConnectionId`], +1639 pub(crate) const SIZE_BOUND: usize = { +1640 let type_len = FrameType::NewConnectionId.size(); +1641 let seq_max_len = 8usize; +1642 let retire_prior_to_max_len = 8usize; +1643 let cid_len_len = 1; +1644 let cid_len = 160; +1645 let reset_token_len = 16; +1646 type_len + seq_max_len + retire_prior_to_max_len + cid_len_len + cid_len + reset_token_len +1647 }; +1648 +1649 /// Maximum size of this frame when the frame type is [`FrameType::PathNewConnectionId`], +1650 pub(crate) const SIZE_BOUND_MULTIPATH: usize = { +1651 let type_len = FrameType::PathNewConnectionId.size(); +1652 let path_id_len = VarInt::from_u32(u32::MAX).size(); +1653 let seq_max_len = 8usize; +1654 let retire_prior_to_max_len = 8usize; +1655 let cid_len_len = 1; +1656 let cid_len = 160; +1657 let reset_token_len = 16; +1658 type_len +1659 + path_id_len +1660 + seq_max_len +1661 + retire_prior_to_max_len +1662 + cid_len_len +1663 + cid_len +1664 + reset_token_len +1665 }; +1666 +1667 pub(crate) fn get_type(&self) -> FrameType { +1668 if self.path_id.is_some() { +1669 FrameType::PathNewConnectionId +1670 } else { +1671 FrameType::NewConnectionId +1672 } +1673 } +1674 +1675 /// Returns the maximum encoded size on the wire. +1676 /// +1677 /// This is a rough upper estimate, does not squeeze every last byte out. +1678 pub(crate) const fn size_bound(path_new_cid: bool, cid_len: usize) -> usize { +1679 let upper_bound = match path_new_cid { +1680 true => Self::SIZE_BOUND_MULTIPATH, +1681 false => Self::SIZE_BOUND, +1682 }; +1683 // instead of using the maximum cid len, use the provided one +1684 upper_bound - 160 + cid_len +1685 } +1686 +1687 fn read<R: Buf>(bytes: &mut R, read_path: bool) -> Result<Self, IterErr> { +1688 let path_id = if read_path { Some(bytes.get()?) } else { None }; +1689 let sequence = bytes.get_var()?; +1690 let retire_prior_to = bytes.get_var()?; +1691 if retire_prior_to > sequence { +1692 return Err(IterErr::Malformed); +1693 } +1694 let length = bytes.get::<u8>()? as usize; +1695 if length > MAX_CID_SIZE || length == 0 { +1696 return Err(IterErr::Malformed); +1697 } +1698 if length > bytes.remaining() { +1699 return Err(IterErr::UnexpectedEnd); +1700 } +1701 let mut stage = [0; MAX_CID_SIZE]; +1702 bytes.copy_to_slice(&mut stage[0..length]); +1703 let id = ConnectionId::new(&stage[..length]); +1704 if bytes.remaining() < 16 { +1705 return Err(IterErr::UnexpectedEnd); +1706 } +1707 let mut reset_token = [0; RESET_TOKEN_SIZE]; +1708 bytes.copy_to_slice(&mut reset_token); +1709 Ok(Self { +1710 path_id, +1711 sequence, +1712 retire_prior_to, +1713 id, +1714 reset_token: reset_token.into(), +1715 }) +1716 } +1717 +1718 pub(crate) fn issued(&self) -> crate::shared::IssuedCid { +1719 crate::shared::IssuedCid { +1720 path_id: self.path_id.unwrap_or_default(), +1721 sequence: self.sequence, +1722 id: self.id, +1723 reset_token: self.reset_token, +1724 } +1725 } +1726} +1727 +1728impl Encodable for NewConnectionId { +1729 fn encode<W: BufMut>(&self, out: &mut W) { +1730 out.write(self.get_type()); +1731 if let Some(id) = self.path_id { +1732 out.write(id); +1733 } +1734 out.write_var(self.sequence); +1735 out.write_var(self.retire_prior_to); +1736 out.write(self.id.len() as u8); +1737 out.put_slice(&self.id); +1738 out.put_slice(&self.reset_token); +1739 } +1740} +1741 +1742impl FrameStruct for NewConnectionId { +1743 const SIZE_BOUND: usize = 1 + 8 + 8 + 1 + MAX_CID_SIZE + RESET_TOKEN_SIZE; +1744} +1745 +1746/// An unreliable datagram +1747#[derive(Debug, Clone)] +1748pub struct Datagram { +1749 /// Payload +1750 pub data: Bytes, +1751} +1752 +1753impl FrameStruct for Datagram { +1754 const SIZE_BOUND: usize = 1 + 8; +1755} +1756 +1757impl Datagram { +1758 pub(crate) fn size(&self, length: bool) -> usize { +1759 1 + if length { +1760 VarInt::from_u64(self.data.len() as u64).unwrap().size() +1761 } else { +1762 0 +1763 } + self.data.len() +1764 } +1765} +1766 +1767impl Encodable for Datagram { +1768 fn encode<B: BufMut>(&self, out: &mut B) { +1769 // A datagram is encoded only after this is verified. +1770 const ENCODE_LEN: bool = true; +1771 out.write(FrameType::Datagram(DatagramInfo( +1772 *DatagramInfo::VALUES.start() as u8 | u8::from(ENCODE_LEN), +1773 ))); // 1 byte +1774 // Safe to unwrap because we check length sanity before queueing datagrams +1775 out.write(VarInt::from_u64(self.data.len() as u64).unwrap()); // <= 8 bytes +1776 out.put_slice(&self.data); +1777 } +1778} +1779 +1780#[derive(Debug, Copy, Clone, PartialEq, Eq)] +1781pub(crate) struct AckFrequency { +1782 pub(crate) sequence: VarInt, +1783 pub(crate) ack_eliciting_threshold: VarInt, +1784 pub(crate) request_max_ack_delay: VarInt, +1785 pub(crate) reordering_threshold: VarInt, +1786} +1787 +1788impl Encodable for AckFrequency { +1789 fn encode<W: BufMut>(&self, buf: &mut W) { +1790 buf.write(FrameType::AckFrequency); +1791 buf.write(self.sequence); +1792 buf.write(self.ack_eliciting_threshold); +1793 buf.write(self.request_max_ack_delay); +1794 buf.write(self.reordering_threshold); +1795 } +1796} +1797 +1798/* Address Discovery https://datatracker.ietf.org/doc/draft-seemann-quic-address-discovery/ */ +1799 +1800/// Conjunction of the information contained in the address discovery frames +1801/// ([`FrameType::ObservedIpv4Addr`], [`FrameType::ObservedIpv6Addr`]). +1802#[derive(Debug, PartialEq, Eq, Clone)] +1803pub(crate) struct ObservedAddr { +1804 /// Monotonically increasing integer within the same connection. +1805 pub(crate) seq_no: VarInt, +1806 /// Reported observed address. +1807 pub(crate) ip: IpAddr, +1808 /// Reported observed port. +1809 pub(crate) port: u16, +1810} +1811 +1812impl ObservedAddr { +1813 pub(crate) fn new<N: Into<VarInt>>(remote: std::net::SocketAddr, seq_no: N) -> Self { +1814 Self { +1815 ip: remote.ip(), +1816 port: remote.port(), +1817 seq_no: seq_no.into(), +1818 } +1819 } +1820 +1821 /// Get the [`FrameType`] for this frame. +1822 pub(crate) fn get_type(&self) -> FrameType { +1823 if self.ip.is_ipv6() { +1824 FrameType::ObservedIpv6Addr +1825 } else { +1826 FrameType::ObservedIpv4Addr +1827 } +1828 } +1829 +1830 /// Compute the number of bytes needed to encode the frame. +1831 pub(crate) fn size(&self) -> usize { +1832 let type_size = self.get_type().size(); +1833 let req_id_bytes = self.seq_no.size(); +1834 let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 }; +1835 let port_bytes = 2; +1836 type_size + req_id_bytes + ip_bytes + port_bytes +1837 } +1838 +1839 /// Reads the frame contents from the buffer. +1840 /// +1841 /// Should only be called when the frame type has been identified as +1842 /// [`FrameType::ObservedIpv4Addr`] or [`FrameType::ObservedIpv6Addr`]. +1843 pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> { +1844 let seq_no = bytes.get()?; +1845 let ip = if is_ipv6 { +1846 IpAddr::V6(bytes.get()?) +1847 } else { +1848 IpAddr::V4(bytes.get()?) +1849 }; +1850 let port = bytes.get()?; +1851 Ok(Self { seq_no, ip, port }) +1852 } +1853 +1854 /// Gives the [`SocketAddr`] reported in the frame. +1855 pub(crate) fn socket_addr(&self) -> SocketAddr { +1856 (self.ip, self.port).into() +1857 } +1858} +1859 +1860impl Encodable for ObservedAddr { +1861 fn encode<W: BufMut>(&self, buf: &mut W) { +1862 buf.write(self.get_type()); +1863 buf.write(self.seq_no); +1864 match self.ip { +1865 IpAddr::V4(ipv4_addr) => { +1866 buf.write(ipv4_addr); +1867 } +1868 IpAddr::V6(ipv6_addr) => { +1869 buf.write(ipv6_addr); +1870 } +1871 } +1872 buf.write::<u16>(self.port); +1873 } +1874} 1875 -1876 /// Reads the frame contents from the buffer. -1877 /// -1878 /// Should only be called when the frame type has been identified as -1879 /// [`FrameType::ObservedIpv4Addr`] or [`FrameType::ObservedIpv6Addr`]. -1880 pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> { -1881 let seq_no = bytes.get()?; -1882 let ip = if is_ipv6 { -1883 IpAddr::V6(bytes.get()?) -1884 } else { -1885 IpAddr::V4(bytes.get()?) -1886 }; -1887 let port = bytes.get()?; -1888 Ok(Self { seq_no, ip, port }) -1889 } -1890 -1891 /// Gives the [`SocketAddr`] reported in the frame. -1892 pub(crate) fn socket_addr(&self) -> SocketAddr { -1893 (self.ip, self.port).into() -1894 } -1895} -1896 -1897impl Encodable for ObservedAddr { -1898 fn encode<W: BufMut>(&self, buf: &mut W) { -1899 buf.write(self.get_type()); -1900 buf.write(self.seq_no); -1901 match self.ip { -1902 IpAddr::V4(ipv4_addr) => { -1903 buf.write(ipv4_addr); -1904 } -1905 IpAddr::V6(ipv6_addr) => { -1906 buf.write(ipv6_addr); -1907 } -1908 } -1909 buf.write::<u16>(self.port); -1910 } -1911} -1912 -1913/* Multipath <https://datatracker.ietf.org/doc/draft-ietf-quic-multipath/> */ -1914 -1915#[derive(Debug, PartialEq, Eq)] -1916pub(crate) struct PathAbandon { -1917 pub(crate) path_id: PathId, -1918 pub(crate) error_code: TransportErrorCode, -1919} -1920 -1921impl PathAbandon { -1922 pub(crate) const SIZE_BOUND: usize = FrameType::PathAbandon.size() + 8 + 8; -1923} -1924 -1925impl Encodable for PathAbandon { -1926 fn encode<W: BufMut>(&self, buf: &mut W) { -1927 buf.write(FrameType::PathAbandon); -1928 buf.write(self.path_id); -1929 buf.write(self.error_code); +1876/* Multipath <https://datatracker.ietf.org/doc/draft-ietf-quic-multipath/> */ +1877 +1878#[derive(Debug, PartialEq, Eq)] +1879pub(crate) struct PathAbandon { +1880 pub(crate) path_id: PathId, +1881 pub(crate) error_code: TransportErrorCode, +1882} +1883 +1884impl PathAbandon { +1885 pub(crate) const SIZE_BOUND: usize = FrameType::PathAbandon.size() + 8 + 8; +1886} +1887 +1888impl Encodable for PathAbandon { +1889 fn encode<W: BufMut>(&self, buf: &mut W) { +1890 buf.write(FrameType::PathAbandon); +1891 buf.write(self.path_id); +1892 buf.write(self.error_code); +1893 } +1894} +1895 +1896impl Decodable for PathAbandon { +1897 fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> { +1898 Ok(Self { +1899 path_id: bytes.get()?, +1900 error_code: bytes.get()?, +1901 }) +1902 } +1903} +1904 +1905#[derive(Debug, PartialEq, Eq)] +1906pub(crate) struct PathStatusAvailable { +1907 pub(crate) path_id: PathId, +1908 pub(crate) status_seq_no: VarInt, +1909} +1910 +1911impl PathStatusAvailable { +1912 const TYPE: FrameType = FrameType::PathStatusAvailable; +1913 pub(crate) const SIZE_BOUND: usize = FrameType::PathStatusAvailable.size() + 8 + 8; +1914} +1915 +1916impl Encodable for PathStatusAvailable { +1917 fn encode<W: BufMut>(&self, buf: &mut W) { +1918 buf.write(Self::TYPE); +1919 buf.write(self.path_id); +1920 buf.write(self.status_seq_no); +1921 } +1922} +1923 +1924impl Decodable for PathStatusAvailable { +1925 fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> { +1926 Ok(Self { +1927 path_id: bytes.get()?, +1928 status_seq_no: bytes.get()?, +1929 }) 1930 } 1931} 1932 -1933impl Decodable for PathAbandon { -1934 fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> { -1935 Ok(Self { -1936 path_id: bytes.get()?, -1937 error_code: bytes.get()?, -1938 }) -1939 } -1940} -1941 -1942#[derive(Debug, PartialEq, Eq)] -1943pub(crate) struct PathStatusAvailable { -1944 pub(crate) path_id: PathId, -1945 pub(crate) status_seq_no: VarInt, -1946} -1947 -1948impl PathStatusAvailable { -1949 const TYPE: FrameType = FrameType::PathStatusAvailable; -1950 pub(crate) const SIZE_BOUND: usize = FrameType::PathStatusAvailable.size() + 8 + 8; -1951} -1952 -1953impl Encodable for PathStatusAvailable { -1954 fn encode<W: BufMut>(&self, buf: &mut W) { -1955 buf.write(Self::TYPE); -1956 buf.write(self.path_id); -1957 buf.write(self.status_seq_no); -1958 } -1959} -1960 -1961impl Decodable for PathStatusAvailable { -1962 fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> { -1963 Ok(Self { -1964 path_id: bytes.get()?, -1965 status_seq_no: bytes.get()?, -1966 }) -1967 } -1968} -1969 -1970#[derive(Debug, PartialEq, Eq)] -1971pub(crate) struct PathStatusBackup { -1972 pub(crate) path_id: PathId, -1973 pub(crate) status_seq_no: VarInt, -1974} -1975 -1976impl PathStatusBackup { -1977 const TYPE: FrameType = FrameType::PathStatusBackup; -1978} -1979 -1980impl Encodable for PathStatusBackup { -1981 fn encode<W: BufMut>(&self, buf: &mut W) { -1982 buf.write(Self::TYPE); -1983 buf.write(self.path_id); -1984 buf.write(self.status_seq_no); +1933#[derive(Debug, PartialEq, Eq)] +1934pub(crate) struct PathStatusBackup { +1935 pub(crate) path_id: PathId, +1936 pub(crate) status_seq_no: VarInt, +1937} +1938 +1939impl PathStatusBackup { +1940 const TYPE: FrameType = FrameType::PathStatusBackup; +1941} +1942 +1943impl Encodable for PathStatusBackup { +1944 fn encode<W: BufMut>(&self, buf: &mut W) { +1945 buf.write(Self::TYPE); +1946 buf.write(self.path_id); +1947 buf.write(self.status_seq_no); +1948 } +1949} +1950 +1951impl Decodable for PathStatusBackup { +1952 fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> { +1953 Ok(Self { +1954 path_id: bytes.get()?, +1955 status_seq_no: bytes.get()?, +1956 }) +1957 } +1958} +1959 +1960/* Nat traversal frames */ +1961 +1962/// Conjunction of the information contained in the add address frames +1963/// ([`FrameType::AddIpv4Address`], [`FrameType::AddIpv6Address`]). +1964#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)] +1965// TODO(@divma): remove +1966#[allow(dead_code)] +1967pub(crate) struct AddAddress { +1968 /// Monotonically increasing integer within the same connection +1969 // TODO(@divma): both assumed, the draft has no mention of this but it's standard +1970 pub(crate) seq_no: VarInt, +1971 /// Address to include in the known set +1972 pub(crate) ip: IpAddr, +1973 /// Port to use with this address +1974 pub(crate) port: u16, +1975} +1976 +1977// TODO(@divma): remove +1978#[allow(dead_code)] +1979impl AddAddress { +1980 /// Smallest number of bytes this type of frame is guaranteed to fit within. +1981 pub(crate) const SIZE_BOUND: usize = Self { +1982 ip: IpAddr::V6(std::net::Ipv6Addr::LOCALHOST), +1983 port: u16::MAX, +1984 seq_no: VarInt::MAX, 1985 } -1986} +1986 .size(); 1987 -1988impl Decodable for PathStatusBackup { -1989 fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> { -1990 Ok(Self { -1991 path_id: bytes.get()?, -1992 status_seq_no: bytes.get()?, -1993 }) -1994 } -1995} -1996 -1997/* Nat traversal frames */ -1998 -1999/// Conjunction of the information contained in the add address frames -2000/// ([`FrameType::AddIpv4Address`], [`FrameType::AddIpv6Address`]). -2001#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)] -2002// TODO(@divma): remove -2003#[allow(dead_code)] -2004pub(crate) struct AddAddress { -2005 /// Monotonically increasing integer within the same connection -2006 // TODO(@divma): both assumed, the draft has no mention of this but it's standard -2007 pub(crate) seq_no: VarInt, -2008 /// Address to include in the known set -2009 pub(crate) ip: IpAddr, -2010 /// Port to use with this address -2011 pub(crate) port: u16, -2012} -2013 -2014// TODO(@divma): remove -2015#[allow(dead_code)] -2016impl AddAddress { -2017 /// Smallest number of bytes this type of frame is guaranteed to fit within. -2018 pub(crate) const SIZE_BOUND: usize = Self { -2019 ip: IpAddr::V6(std::net::Ipv6Addr::LOCALHOST), -2020 port: u16::MAX, -2021 seq_no: VarInt::MAX, -2022 } -2023 .size(); +1988 pub(crate) const fn new((ip, port): (IpAddr, u16), seq_no: VarInt) -> Self { +1989 Self { ip, port, seq_no } +1990 } +1991 +1992 /// Get the [`FrameType`] for this frame. +1993 pub(crate) const fn get_type(&self) -> FrameType { +1994 if self.ip.is_ipv6() { +1995 FrameType::AddIpv6Address +1996 } else { +1997 FrameType::AddIpv4Address +1998 } +1999 } +2000 +2001 /// Compute the number of bytes needed to encode the frame +2002 pub(crate) const fn size(&self) -> usize { +2003 let type_size = self.get_type().size(); +2004 let seq_no_bytes = self.seq_no.size(); +2005 let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 }; +2006 let port_bytes = 2; +2007 type_size + seq_no_bytes + ip_bytes + port_bytes +2008 } +2009 +2010 /// Read the frame contents from the buffer +2011 /// +2012 /// Should only be called when the frame type has been identified as +2013 /// [`FrameType::AddIpv4Address`] or [`FrameType::AddIpv6Address`]. +2014 pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> { +2015 let seq_no = bytes.get()?; +2016 let ip = if is_ipv6 { +2017 IpAddr::V6(bytes.get()?) +2018 } else { +2019 IpAddr::V4(bytes.get()?) +2020 }; +2021 let port = bytes.get()?; +2022 Ok(Self { seq_no, ip, port }) +2023 } 2024 -2025 pub(crate) const fn new((ip, port): (IpAddr, u16), seq_no: VarInt) -> Self { -2026 Self { ip, port, seq_no } -2027 } -2028 -2029 /// Get the [`FrameType`] for this frame. -2030 pub(crate) const fn get_type(&self) -> FrameType { -2031 if self.ip.is_ipv6() { -2032 FrameType::AddIpv6Address -2033 } else { -2034 FrameType::AddIpv4Address -2035 } -2036 } -2037 -2038 /// Compute the number of bytes needed to encode the frame -2039 pub(crate) const fn size(&self) -> usize { -2040 let type_size = self.get_type().size(); -2041 let seq_no_bytes = self.seq_no.size(); -2042 let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 }; -2043 let port_bytes = 2; -2044 type_size + seq_no_bytes + ip_bytes + port_bytes -2045 } -2046 -2047 /// Read the frame contents from the buffer -2048 /// -2049 /// Should only be called when the frame type has been identified as -2050 /// [`FrameType::AddIpv4Address`] or [`FrameType::AddIpv6Address`]. -2051 pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> { -2052 let seq_no = bytes.get()?; -2053 let ip = if is_ipv6 { -2054 IpAddr::V6(bytes.get()?) -2055 } else { -2056 IpAddr::V4(bytes.get()?) -2057 }; -2058 let port = bytes.get()?; -2059 Ok(Self { seq_no, ip, port }) -2060 } -2061 -2062 /// Give the [`SocketAddr`] encoded in the frame -2063 pub(crate) fn socket_addr(&self) -> SocketAddr { -2064 self.ip_port().into() -2065 } -2066 -2067 pub(crate) fn ip_port(&self) -> (IpAddr, u16) { -2068 (self.ip, self.port) -2069 } -2070} -2071 -2072impl Encodable for AddAddress { -2073 fn encode<W: BufMut>(&self, buf: &mut W) { -2074 buf.write(self.get_type()); -2075 buf.write(self.seq_no); -2076 match self.ip { -2077 IpAddr::V4(ipv4_addr) => { -2078 buf.write(ipv4_addr); -2079 } -2080 IpAddr::V6(ipv6_addr) => { -2081 buf.write(ipv6_addr); -2082 } -2083 } -2084 buf.write::<u16>(self.port); -2085 } -2086} -2087 -2088/// Conjunction of the information contained in the reach out frames -2089/// ([`FrameType::ReachOutAtIpv4`], [`FrameType::ReachOutAtIpv6`]) -2090#[derive(Debug, PartialEq, Eq, Clone)] -2091// TODO(@divma): remove -2092#[allow(dead_code)] -2093pub(crate) struct ReachOut { -2094 /// The sequence number of the NAT Traversal attempts -2095 pub(crate) round: VarInt, -2096 /// Address to use -2097 pub(crate) ip: IpAddr, -2098 /// Port to use with this address -2099 pub(crate) port: u16, -2100} -2101 -2102// TODO(@divma): remove -2103#[allow(dead_code)] -2104impl ReachOut { -2105 /// Smallest number of bytes this type of frame is guaranteed to fit within -2106 pub(crate) const SIZE_BOUND: usize = Self { -2107 round: VarInt::MAX, -2108 ip: IpAddr::V6(std::net::Ipv6Addr::LOCALHOST), -2109 port: u16::MAX, -2110 } -2111 .size(); +2025 /// Give the [`SocketAddr`] encoded in the frame +2026 pub(crate) fn socket_addr(&self) -> SocketAddr { +2027 self.ip_port().into() +2028 } +2029 +2030 pub(crate) fn ip_port(&self) -> (IpAddr, u16) { +2031 (self.ip, self.port) +2032 } +2033} +2034 +2035impl Encodable for AddAddress { +2036 fn encode<W: BufMut>(&self, buf: &mut W) { +2037 buf.write(self.get_type()); +2038 buf.write(self.seq_no); +2039 match self.ip { +2040 IpAddr::V4(ipv4_addr) => { +2041 buf.write(ipv4_addr); +2042 } +2043 IpAddr::V6(ipv6_addr) => { +2044 buf.write(ipv6_addr); +2045 } +2046 } +2047 buf.write::<u16>(self.port); +2048 } +2049} +2050 +2051/// Conjunction of the information contained in the reach out frames +2052/// ([`FrameType::ReachOutAtIpv4`], [`FrameType::ReachOutAtIpv6`]) +2053#[derive(Debug, PartialEq, Eq, Clone)] +2054// TODO(@divma): remove +2055#[allow(dead_code)] +2056pub(crate) struct ReachOut { +2057 /// The sequence number of the NAT Traversal attempts +2058 pub(crate) round: VarInt, +2059 /// Address to use +2060 pub(crate) ip: IpAddr, +2061 /// Port to use with this address +2062 pub(crate) port: u16, +2063} +2064 +2065// TODO(@divma): remove +2066#[allow(dead_code)] +2067impl ReachOut { +2068 /// Smallest number of bytes this type of frame is guaranteed to fit within +2069 pub(crate) const SIZE_BOUND: usize = Self { +2070 round: VarInt::MAX, +2071 ip: IpAddr::V6(std::net::Ipv6Addr::LOCALHOST), +2072 port: u16::MAX, +2073 } +2074 .size(); +2075 +2076 pub(crate) const fn new(round: VarInt, (ip, port): (IpAddr, u16)) -> Self { +2077 Self { round, ip, port } +2078 } +2079 +2080 /// Get the [`FrameType`] for this frame +2081 pub(crate) const fn get_type(&self) -> FrameType { +2082 if self.ip.is_ipv6() { +2083 FrameType::ReachOutAtIpv6 +2084 } else { +2085 FrameType::ReachOutAtIpv4 +2086 } +2087 } +2088 +2089 /// Compute the number of bytes needed to encode the frame +2090 pub(crate) const fn size(&self) -> usize { +2091 let type_size = self.get_type().size(); +2092 let round_bytes = self.round.size(); +2093 let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 }; +2094 let port_bytes = 2; +2095 type_size + round_bytes + ip_bytes + port_bytes +2096 } +2097 +2098 /// Read the frame contents from the buffer +2099 /// +2100 /// Should only be called when the frame type has been identified as +2101 /// [`FrameType::ReachOutAtIpv4`] or [`FrameType::ReachOutAtIpv6`]. +2102 pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> { +2103 let round = bytes.get()?; +2104 let ip = if is_ipv6 { +2105 IpAddr::V6(bytes.get()?) +2106 } else { +2107 IpAddr::V4(bytes.get()?) +2108 }; +2109 let port = bytes.get()?; +2110 Ok(Self { round, ip, port }) +2111 } 2112 -2113 pub(crate) const fn new(round: VarInt, (ip, port): (IpAddr, u16)) -> Self { -2114 Self { round, ip, port } -2115 } -2116 -2117 /// Get the [`FrameType`] for this frame -2118 pub(crate) const fn get_type(&self) -> FrameType { -2119 if self.ip.is_ipv6() { -2120 FrameType::ReachOutAtIpv6 -2121 } else { -2122 FrameType::ReachOutAtIpv4 -2123 } -2124 } -2125 -2126 /// Compute the number of bytes needed to encode the frame -2127 pub(crate) const fn size(&self) -> usize { -2128 let type_size = self.get_type().size(); -2129 let round_bytes = self.round.size(); -2130 let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 }; -2131 let port_bytes = 2; -2132 type_size + round_bytes + ip_bytes + port_bytes -2133 } +2113 /// Give the [`SocketAddr`] encoded in the frame +2114 pub(crate) fn socket_addr(&self) -> SocketAddr { +2115 (self.ip, self.port).into() +2116 } +2117} +2118 +2119impl Encodable for ReachOut { +2120 fn encode<W: BufMut>(&self, buf: &mut W) { +2121 buf.write(self.get_type()); +2122 buf.write(self.round); +2123 match self.ip { +2124 IpAddr::V4(ipv4_addr) => { +2125 buf.write(ipv4_addr); +2126 } +2127 IpAddr::V6(ipv6_addr) => { +2128 buf.write(ipv6_addr); +2129 } +2130 } +2131 buf.write::<u16>(self.port); +2132 } +2133} 2134 -2135 /// Read the frame contents from the buffer -2136 /// -2137 /// Should only be called when the frame type has been identified as -2138 /// [`FrameType::ReachOutAtIpv4`] or [`FrameType::ReachOutAtIpv6`]. -2139 pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> { -2140 let round = bytes.get()?; -2141 let ip = if is_ipv6 { -2142 IpAddr::V6(bytes.get()?) -2143 } else { -2144 IpAddr::V4(bytes.get()?) -2145 }; -2146 let port = bytes.get()?; -2147 Ok(Self { round, ip, port }) -2148 } +2135/// Frame signaling an address is no longer being advertised +2136#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)] +2137// TODO(@divma): remove +2138#[allow(dead_code)] +2139pub(crate) struct RemoveAddress { +2140 /// The sequence number of the address advertisement to be removed +2141 pub(crate) seq_no: VarInt, +2142} +2143 +2144// TODO(@divma): remove +2145#[allow(dead_code)] +2146impl RemoveAddress { +2147 /// [`FrameType`] of this frame +2148 pub(crate) const TYPE: FrameType = FrameType::RemoveAddress; 2149 -2150 /// Give the [`SocketAddr`] encoded in the frame -2151 pub(crate) fn socket_addr(&self) -> SocketAddr { -2152 (self.ip, self.port).into() -2153 } -2154} -2155 -2156impl Encodable for ReachOut { -2157 fn encode<W: BufMut>(&self, buf: &mut W) { -2158 buf.write(self.get_type()); -2159 buf.write(self.round); -2160 match self.ip { -2161 IpAddr::V4(ipv4_addr) => { -2162 buf.write(ipv4_addr); -2163 } -2164 IpAddr::V6(ipv6_addr) => { -2165 buf.write(ipv6_addr); -2166 } -2167 } -2168 buf.write::<u16>(self.port); -2169 } -2170} -2171 -2172/// Frame signaling an address is no longer being advertised -2173#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)] -2174// TODO(@divma): remove -2175#[allow(dead_code)] -2176pub(crate) struct RemoveAddress { -2177 /// The sequence number of the address advertisement to be removed -2178 pub(crate) seq_no: VarInt, -2179} -2180 -2181// TODO(@divma): remove -2182#[allow(dead_code)] -2183impl RemoveAddress { -2184 /// [`FrameType`] of this frame -2185 pub(crate) const TYPE: FrameType = FrameType::RemoveAddress; -2186 -2187 /// Smallest number of bytes this type of frame is guaranteed to fit within -2188 pub(crate) const SIZE_BOUND: usize = Self::new(VarInt::MAX).size(); -2189 -2190 pub(crate) const fn new(seq_no: VarInt) -> Self { -2191 Self { seq_no } -2192 } -2193 -2194 /// Compute the number of bytes needed to encode the frame -2195 pub(crate) const fn size(&self) -> usize { -2196 let type_size = Self::TYPE.size(); -2197 let seq_no_bytes = self.seq_no.size(); -2198 type_size + seq_no_bytes -2199 } -2200 -2201 /// Read the frame contents from the buffer -2202 /// -2203 /// Should only be called when the frame type has been identified as -2204 /// [`FrameType::RemoveAddress`]. -2205 pub(crate) fn read<R: Buf>(bytes: &mut R) -> coding::Result<Self> { -2206 Ok(Self { -2207 seq_no: bytes.get()?, -2208 }) -2209 } -2210} -2211 -2212impl Encodable for RemoveAddress { -2213 fn encode<W: BufMut>(&self, buf: &mut W) { -2214 buf.write(Self::TYPE); -2215 buf.write(self.seq_no); -2216 } -2217} -2218 -2219#[cfg(test)] -2220mod test { -2221 use super::*; -2222 use crate::coding::Encodable; -2223 use assert_matches::assert_matches; -2224 -2225 #[test] -2226 fn frame_type() { -2227 assert_eq!( -2228 FrameType::try_from(FrameType::Padding.to_u64()), -2229 Ok(FrameType::Padding), -2230 ); -2231 -2232 assert_eq!( -2233 FrameType::try_from(FrameType::Datagram(DatagramInfo(0x30)).to_u64()), -2234 Ok(FrameType::Datagram(DatagramInfo(0x30))), -2235 ); -2236 -2237 assert_eq!( -2238 FrameType::try_from(FrameType::Stream(StreamInfo(0x08)).to_u64()), -2239 Ok(FrameType::Stream(StreamInfo(0x08))), -2240 ); -2241 } -2242 -2243 #[track_caller] -2244 fn frames(buf: Vec<u8>) -> Vec<Frame> { -2245 Iter::new(Bytes::from(buf)) -2246 .unwrap() -2247 .collect::<Result<Vec<_>, _>>() -2248 .unwrap() -2249 } -2250 -2251 #[test] -2252 fn ack_coding() { -2253 const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14]; -2254 let mut ranges = ArrayRangeSet::new(); -2255 for &packet in PACKETS { -2256 ranges.insert(packet..packet + 1); -2257 } -2258 let mut buf = Vec::new(); -2259 const ECN: EcnCounts = EcnCounts { -2260 ect0: 42, -2261 ect1: 24, -2262 ce: 12, -2263 }; -2264 Ack::encoder(42, &ranges, Some(&ECN)).encode(&mut buf); -2265 let frames = frames(buf); -2266 assert_eq!(frames.len(), 1); -2267 match frames[0] { -2268 Frame::Ack(ref ack) => { -2269 let mut packets = ack.iter().flatten().collect::<Vec<_>>(); -2270 packets.sort_unstable(); -2271 assert_eq!(&packets[..], PACKETS); -2272 assert_eq!(ack.ecn, Some(ECN)); -2273 } -2274 ref x => panic!("incorrect frame {x:?}"), -2275 } -2276 } -2277 -2278 #[test] -2279 #[allow(clippy::range_plus_one)] -2280 fn path_ack_coding() { -2281 const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14]; -2282 let mut ranges = ArrayRangeSet::new(); -2283 for &packet in PACKETS { -2284 ranges.insert(packet..packet + 1); -2285 } -2286 let mut buf = Vec::new(); -2287 const ECN: EcnCounts = EcnCounts { -2288 ect0: 42, -2289 ect1: 24, -2290 ce: 12, -2291 }; -2292 const PATH_ID: PathId = PathId::MAX; -2293 PathAck::encoder(PATH_ID, 42, &ranges, Some(&ECN)).encode(&mut buf); -2294 let frames = frames(buf); -2295 assert_eq!(frames.len(), 1); -2296 match frames[0] { -2297 Frame::PathAck(ref ack) => { -2298 assert_eq!(ack.path_id, PATH_ID); -2299 let mut packets = ack.into_iter().flatten().collect::<Vec<_>>(); -2300 packets.sort_unstable(); -2301 assert_eq!(&packets[..], PACKETS); -2302 assert_eq!(ack.ecn, Some(ECN)); -2303 } -2304 ref x => panic!("incorrect frame {x:?}"), -2305 } -2306 } -2307 -2308 #[test] -2309 fn ack_frequency_coding() { -2310 let mut buf = Vec::new(); -2311 let original = AckFrequency { -2312 sequence: VarInt(42), -2313 ack_eliciting_threshold: VarInt(20), -2314 request_max_ack_delay: VarInt(50_000), -2315 reordering_threshold: VarInt(1), -2316 }; -2317 original.encode(&mut buf); -2318 let frames = frames(buf); -2319 assert_eq!(frames.len(), 1); -2320 match &frames[0] { -2321 Frame::AckFrequency(decoded) => assert_eq!(decoded, &original), -2322 x => panic!("incorrect frame {x:?}"), -2323 } -2324 } -2325 -2326 #[test] -2327 fn immediate_ack_coding() { -2328 let mut buf = Vec::new(); -2329 FrameType::ImmediateAck.encode(&mut buf); -2330 let frames = frames(buf); -2331 assert_eq!(frames.len(), 1); -2332 assert_matches!(&frames[0], Frame::ImmediateAck); -2333 } -2334 -2335 /// Test that encoding and decoding [`ObservedAddr`] produces the same result. -2336 #[test] -2337 fn test_observed_addr_roundrip() { -2338 let observed_addr = ObservedAddr { -2339 seq_no: VarInt(42), -2340 ip: std::net::Ipv4Addr::LOCALHOST.into(), -2341 port: 4242, -2342 }; -2343 let mut buf = Vec::with_capacity(observed_addr.size()); -2344 observed_addr.encode(&mut buf); -2345 -2346 assert_eq!( -2347 observed_addr.size(), -2348 buf.len(), -2349 "expected written bytes and actual size differ" -2350 ); -2351 -2352 let mut decoded = frames(buf); -2353 assert_eq!(decoded.len(), 1); -2354 match decoded.pop().expect("non empty") { -2355 Frame::ObservedAddr(decoded) => assert_eq!(decoded, observed_addr), -2356 x => panic!("incorrect frame {x:?}"), -2357 } -2358 } -2359 -2360 #[test] -2361 fn test_path_abandon_roundtrip() { -2362 let abandon = PathAbandon { -2363 path_id: PathId(42), -2364 error_code: TransportErrorCode::NO_ERROR, -2365 }; -2366 let mut buf = Vec::new(); -2367 abandon.encode(&mut buf); -2368 -2369 let mut decoded = frames(buf); -2370 assert_eq!(decoded.len(), 1); -2371 match decoded.pop().expect("non empty") { -2372 Frame::PathAbandon(decoded) => assert_eq!(decoded, abandon), -2373 x => panic!("incorrect frame {x:?}"), -2374 } -2375 } -2376 -2377 #[test] -2378 fn test_path_status_available_roundtrip() { -2379 let path_status_available = PathStatusAvailable { -2380 path_id: PathId(42), -2381 status_seq_no: VarInt(73), +2150 /// Smallest number of bytes this type of frame is guaranteed to fit within +2151 pub(crate) const SIZE_BOUND: usize = Self::new(VarInt::MAX).size(); +2152 +2153 pub(crate) const fn new(seq_no: VarInt) -> Self { +2154 Self { seq_no } +2155 } +2156 +2157 /// Compute the number of bytes needed to encode the frame +2158 pub(crate) const fn size(&self) -> usize { +2159 let type_size = Self::TYPE.size(); +2160 let seq_no_bytes = self.seq_no.size(); +2161 type_size + seq_no_bytes +2162 } +2163 +2164 /// Read the frame contents from the buffer +2165 /// +2166 /// Should only be called when the frame type has been identified as +2167 /// [`FrameType::RemoveAddress`]. +2168 pub(crate) fn read<R: Buf>(bytes: &mut R) -> coding::Result<Self> { +2169 Ok(Self { +2170 seq_no: bytes.get()?, +2171 }) +2172 } +2173} +2174 +2175impl Encodable for RemoveAddress { +2176 fn encode<W: BufMut>(&self, buf: &mut W) { +2177 buf.write(Self::TYPE); +2178 buf.write(self.seq_no); +2179 } +2180} +2181 +2182#[cfg(test)] +2183mod test { +2184 use super::*; +2185 use crate::coding::Encodable; +2186 use assert_matches::assert_matches; +2187 +2188 #[test] +2189 fn frame_type() { +2190 assert_eq!( +2191 FrameType::try_from(FrameType::Padding.to_u64()), +2192 Ok(FrameType::Padding), +2193 ); +2194 +2195 assert_eq!( +2196 FrameType::try_from(FrameType::Datagram(DatagramInfo(0x30)).to_u64()), +2197 Ok(FrameType::Datagram(DatagramInfo(0x30))), +2198 ); +2199 +2200 assert_eq!( +2201 FrameType::try_from(FrameType::Stream(StreamInfo(0x08)).to_u64()), +2202 Ok(FrameType::Stream(StreamInfo(0x08))), +2203 ); +2204 } +2205 +2206 #[track_caller] +2207 fn frames(buf: Vec<u8>) -> Vec<Frame> { +2208 Iter::new(Bytes::from(buf)) +2209 .unwrap() +2210 .collect::<Result<Vec<_>, _>>() +2211 .unwrap() +2212 } +2213 +2214 #[test] +2215 fn ack_coding() { +2216 const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14]; +2217 let mut ranges = ArrayRangeSet::new(); +2218 for &packet in PACKETS { +2219 ranges.insert(packet..packet + 1); +2220 } +2221 let mut buf = Vec::new(); +2222 const ECN: EcnCounts = EcnCounts { +2223 ect0: 42, +2224 ect1: 24, +2225 ce: 12, +2226 }; +2227 Ack::encoder(42, &ranges, Some(&ECN)).encode(&mut buf); +2228 let frames = frames(buf); +2229 assert_eq!(frames.len(), 1); +2230 match frames[0] { +2231 Frame::Ack(ref ack) => { +2232 let mut packets = ack.iter().flatten().collect::<Vec<_>>(); +2233 packets.sort_unstable(); +2234 assert_eq!(&packets[..], PACKETS); +2235 assert_eq!(ack.ecn, Some(ECN)); +2236 } +2237 ref x => panic!("incorrect frame {x:?}"), +2238 } +2239 } +2240 +2241 #[test] +2242 #[allow(clippy::range_plus_one)] +2243 fn path_ack_coding() { +2244 const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14]; +2245 let mut ranges = ArrayRangeSet::new(); +2246 for &packet in PACKETS { +2247 ranges.insert(packet..packet + 1); +2248 } +2249 let mut buf = Vec::new(); +2250 const ECN: EcnCounts = EcnCounts { +2251 ect0: 42, +2252 ect1: 24, +2253 ce: 12, +2254 }; +2255 const PATH_ID: PathId = PathId::MAX; +2256 PathAck::encoder(PATH_ID, 42, &ranges, Some(&ECN)).encode(&mut buf); +2257 let frames = frames(buf); +2258 assert_eq!(frames.len(), 1); +2259 match frames[0] { +2260 Frame::PathAck(ref ack) => { +2261 assert_eq!(ack.path_id, PATH_ID); +2262 let mut packets = ack.into_iter().flatten().collect::<Vec<_>>(); +2263 packets.sort_unstable(); +2264 assert_eq!(&packets[..], PACKETS); +2265 assert_eq!(ack.ecn, Some(ECN)); +2266 } +2267 ref x => panic!("incorrect frame {x:?}"), +2268 } +2269 } +2270 +2271 #[test] +2272 fn ack_frequency_coding() { +2273 let mut buf = Vec::new(); +2274 let original = AckFrequency { +2275 sequence: VarInt(42), +2276 ack_eliciting_threshold: VarInt(20), +2277 request_max_ack_delay: VarInt(50_000), +2278 reordering_threshold: VarInt(1), +2279 }; +2280 original.encode(&mut buf); +2281 let frames = frames(buf); +2282 assert_eq!(frames.len(), 1); +2283 match &frames[0] { +2284 Frame::AckFrequency(decoded) => assert_eq!(decoded, &original), +2285 x => panic!("incorrect frame {x:?}"), +2286 } +2287 } +2288 +2289 #[test] +2290 fn immediate_ack_coding() { +2291 let mut buf = Vec::new(); +2292 FrameType::ImmediateAck.encode(&mut buf); +2293 let frames = frames(buf); +2294 assert_eq!(frames.len(), 1); +2295 assert_matches!(&frames[0], Frame::ImmediateAck); +2296 } +2297 +2298 /// Test that encoding and decoding [`ObservedAddr`] produces the same result. +2299 #[test] +2300 fn test_observed_addr_roundrip() { +2301 let observed_addr = ObservedAddr { +2302 seq_no: VarInt(42), +2303 ip: std::net::Ipv4Addr::LOCALHOST.into(), +2304 port: 4242, +2305 }; +2306 let mut buf = Vec::with_capacity(observed_addr.size()); +2307 observed_addr.encode(&mut buf); +2308 +2309 assert_eq!( +2310 observed_addr.size(), +2311 buf.len(), +2312 "expected written bytes and actual size differ" +2313 ); +2314 +2315 let mut decoded = frames(buf); +2316 assert_eq!(decoded.len(), 1); +2317 match decoded.pop().expect("non empty") { +2318 Frame::ObservedAddr(decoded) => assert_eq!(decoded, observed_addr), +2319 x => panic!("incorrect frame {x:?}"), +2320 } +2321 } +2322 +2323 #[test] +2324 fn test_path_abandon_roundtrip() { +2325 let abandon = PathAbandon { +2326 path_id: PathId(42), +2327 error_code: TransportErrorCode::NO_ERROR, +2328 }; +2329 let mut buf = Vec::new(); +2330 abandon.encode(&mut buf); +2331 +2332 let mut decoded = frames(buf); +2333 assert_eq!(decoded.len(), 1); +2334 match decoded.pop().expect("non empty") { +2335 Frame::PathAbandon(decoded) => assert_eq!(decoded, abandon), +2336 x => panic!("incorrect frame {x:?}"), +2337 } +2338 } +2339 +2340 #[test] +2341 fn test_path_status_available_roundtrip() { +2342 let path_status_available = PathStatusAvailable { +2343 path_id: PathId(42), +2344 status_seq_no: VarInt(73), +2345 }; +2346 let mut buf = Vec::new(); +2347 path_status_available.encode(&mut buf); +2348 +2349 let mut decoded = frames(buf); +2350 assert_eq!(decoded.len(), 1); +2351 match decoded.pop().expect("non empty") { +2352 Frame::PathStatusAvailable(decoded) => assert_eq!(decoded, path_status_available), +2353 x => panic!("incorrect frame {x:?}"), +2354 } +2355 } +2356 +2357 #[test] +2358 fn test_path_status_backup_roundtrip() { +2359 let path_status_backup = PathStatusBackup { +2360 path_id: PathId(42), +2361 status_seq_no: VarInt(73), +2362 }; +2363 let mut buf = Vec::new(); +2364 path_status_backup.encode(&mut buf); +2365 +2366 let mut decoded = frames(buf); +2367 assert_eq!(decoded.len(), 1); +2368 match decoded.pop().expect("non empty") { +2369 Frame::PathStatusBackup(decoded) => assert_eq!(decoded, path_status_backup), +2370 x => panic!("incorrect frame {x:?}"), +2371 } +2372 } +2373 +2374 #[test] +2375 fn test_path_new_connection_id_roundtrip() { +2376 let cid = NewConnectionId { +2377 path_id: Some(PathId(22)), +2378 sequence: 31, +2379 retire_prior_to: 13, +2380 id: ConnectionId::new(&[0xAB; 8]), +2381 reset_token: ResetToken::from([0xCD; crate::RESET_TOKEN_SIZE]), 2382 }; 2383 let mut buf = Vec::new(); -2384 path_status_available.encode(&mut buf); +2384 cid.encode(&mut buf); 2385 2386 let mut decoded = frames(buf); 2387 assert_eq!(decoded.len(), 1); 2388 match decoded.pop().expect("non empty") { -2389 Frame::PathStatusAvailable(decoded) => assert_eq!(decoded, path_status_available), +2389 Frame::NewConnectionId(decoded) => assert_eq!(decoded, cid), 2390 x => panic!("incorrect frame {x:?}"), 2391 } 2392 } 2393 2394 #[test] -2395 fn test_path_status_backup_roundtrip() { -2396 let path_status_backup = PathStatusBackup { -2397 path_id: PathId(42), -2398 status_seq_no: VarInt(73), +2395 fn test_path_retire_connection_id_roundtrip() { +2396 let retire_cid = RetireConnectionId { +2397 path_id: Some(PathId(22)), +2398 sequence: 31, 2399 }; 2400 let mut buf = Vec::new(); -2401 path_status_backup.encode(&mut buf); +2401 retire_cid.encode(&mut buf); 2402 2403 let mut decoded = frames(buf); 2404 assert_eq!(decoded.len(), 1); 2405 match decoded.pop().expect("non empty") { -2406 Frame::PathStatusBackup(decoded) => assert_eq!(decoded, path_status_backup), +2406 Frame::RetireConnectionId(decoded) => assert_eq!(decoded, retire_cid), 2407 x => panic!("incorrect frame {x:?}"), 2408 } 2409 } 2410 2411 #[test] -2412 fn test_path_new_connection_id_roundtrip() { -2413 let cid = NewConnectionId { -2414 path_id: Some(PathId(22)), -2415 sequence: 31, -2416 retire_prior_to: 13, -2417 id: ConnectionId::new(&[0xAB; 8]), -2418 reset_token: ResetToken::from([0xCD; crate::RESET_TOKEN_SIZE]), -2419 }; -2420 let mut buf = Vec::new(); -2421 cid.encode(&mut buf); +2412 fn test_paths_blocked_path_cids_blocked_roundtrip() { +2413 let mut buf = Vec::new(); +2414 +2415 let frame0 = PathsBlocked(PathId(22)); +2416 frame0.encode(&mut buf); +2417 let frame1 = PathCidsBlocked { +2418 path_id: PathId(23), +2419 next_seq: VarInt(32), +2420 }; +2421 frame1.encode(&mut buf); 2422 2423 let mut decoded = frames(buf); -2424 assert_eq!(decoded.len(), 1); +2424 assert_eq!(decoded.len(), 2); 2425 match decoded.pop().expect("non empty") { -2426 Frame::NewConnectionId(decoded) => assert_eq!(decoded, cid), +2426 Frame::PathCidsBlocked(decoded) => assert_eq!(decoded, frame1), 2427 x => panic!("incorrect frame {x:?}"), 2428 } -2429 } -2430 -2431 #[test] -2432 fn test_path_retire_connection_id_roundtrip() { -2433 let retire_cid = RetireConnectionId { -2434 path_id: Some(PathId(22)), -2435 sequence: 31, -2436 }; -2437 let mut buf = Vec::new(); -2438 retire_cid.encode(&mut buf); -2439 -2440 let mut decoded = frames(buf); -2441 assert_eq!(decoded.len(), 1); -2442 match decoded.pop().expect("non empty") { -2443 Frame::RetireConnectionId(decoded) => assert_eq!(decoded, retire_cid), -2444 x => panic!("incorrect frame {x:?}"), -2445 } -2446 } -2447 -2448 #[test] -2449 fn test_paths_blocked_path_cids_blocked_roundtrip() { -2450 let mut buf = Vec::new(); +2429 match decoded.pop().expect("non empty") { +2430 Frame::PathsBlocked(decoded) => assert_eq!(decoded, frame0), +2431 x => panic!("incorrect frame {x:?}"), +2432 } +2433 } +2434 +2435 /// Test that encoding and decoding [`AddAddress`] produces the same result +2436 #[test] +2437 fn test_add_address_roundrip() { +2438 let add_address = AddAddress { +2439 seq_no: VarInt(42), +2440 ip: std::net::Ipv4Addr::LOCALHOST.into(), +2441 port: 4242, +2442 }; +2443 let mut buf = Vec::with_capacity(add_address.size()); +2444 add_address.encode(&mut buf); +2445 +2446 assert_eq!( +2447 add_address.size(), +2448 buf.len(), +2449 "expected written bytes and actual size differ" +2450 ); 2451 -2452 let frame0 = PathsBlocked(PathId(22)); -2453 frame0.encode(&mut buf); -2454 let frame1 = PathCidsBlocked { -2455 path_id: PathId(23), -2456 next_seq: VarInt(32), -2457 }; -2458 frame1.encode(&mut buf); +2452 let mut decoded = frames(buf); +2453 assert_eq!(decoded.len(), 1); +2454 match decoded.pop().expect("non empty") { +2455 Frame::AddAddress(decoded) => assert_eq!(decoded, add_address), +2456 x => panic!("incorrect frame {x:?}"), +2457 } +2458 } 2459 -2460 let mut decoded = frames(buf); -2461 assert_eq!(decoded.len(), 2); -2462 match decoded.pop().expect("non empty") { -2463 Frame::PathCidsBlocked(decoded) => assert_eq!(decoded, frame1), -2464 x => panic!("incorrect frame {x:?}"), -2465 } -2466 match decoded.pop().expect("non empty") { -2467 Frame::PathsBlocked(decoded) => assert_eq!(decoded, frame0), -2468 x => panic!("incorrect frame {x:?}"), -2469 } -2470 } -2471 -2472 /// Test that encoding and decoding [`AddAddress`] produces the same result -2473 #[test] -2474 fn test_add_address_roundrip() { -2475 let add_address = AddAddress { -2476 seq_no: VarInt(42), -2477 ip: std::net::Ipv4Addr::LOCALHOST.into(), -2478 port: 4242, -2479 }; -2480 let mut buf = Vec::with_capacity(add_address.size()); -2481 add_address.encode(&mut buf); -2482 -2483 assert_eq!( -2484 add_address.size(), -2485 buf.len(), -2486 "expected written bytes and actual size differ" -2487 ); -2488 -2489 let mut decoded = frames(buf); -2490 assert_eq!(decoded.len(), 1); -2491 match decoded.pop().expect("non empty") { -2492 Frame::AddAddress(decoded) => assert_eq!(decoded, add_address), -2493 x => panic!("incorrect frame {x:?}"), -2494 } -2495 } -2496 -2497 /// Test that encoding and decoding [`AddAddress`] produces the same result -2498 #[test] -2499 fn test_reach_out_roundrip() { -2500 let reach_out = ReachOut { -2501 round: VarInt(42), -2502 ip: std::net::Ipv6Addr::LOCALHOST.into(), -2503 port: 4242, -2504 }; -2505 let mut buf = Vec::with_capacity(reach_out.size()); -2506 reach_out.encode(&mut buf); -2507 -2508 assert_eq!( -2509 reach_out.size(), -2510 buf.len(), -2511 "expected written bytes and actual size differ" -2512 ); -2513 -2514 let mut decoded = frames(buf); -2515 assert_eq!(decoded.len(), 1); -2516 match decoded.pop().expect("non empty") { -2517 Frame::ReachOut(decoded) => assert_eq!(decoded, reach_out), -2518 x => panic!("incorrect frame {x:?}"), -2519 } -2520 } -2521 -2522 /// Test that encoding and decoding [`RemoveAddress`] produces the same result -2523 #[test] -2524 fn test_remove_address_roundrip() { -2525 let remove_addr = RemoveAddress::new(VarInt(10)); -2526 let mut buf = Vec::with_capacity(remove_addr.size()); -2527 remove_addr.encode(&mut buf); -2528 -2529 assert_eq!( -2530 remove_addr.size(), -2531 buf.len(), -2532 "expected written bytes and actual size differ" -2533 ); -2534 -2535 let mut decoded = frames(buf); -2536 assert_eq!(decoded.len(), 1); -2537 match decoded.pop().expect("non empty") { -2538 Frame::RemoveAddress(decoded) => assert_eq!(decoded, remove_addr), -2539 x => panic!("incorrect frame {x:?}"), -2540 } -2541 } -2542}
\ No newline at end of file +2460 /// Test that encoding and decoding [`AddAddress`] produces the same result +2461 #[test] +2462 fn test_reach_out_roundrip() { +2463 let reach_out = ReachOut { +2464 round: VarInt(42), +2465 ip: std::net::Ipv6Addr::LOCALHOST.into(), +2466 port: 4242, +2467 }; +2468 let mut buf = Vec::with_capacity(reach_out.size()); +2469 reach_out.encode(&mut buf); +2470 +2471 assert_eq!( +2472 reach_out.size(), +2473 buf.len(), +2474 "expected written bytes and actual size differ" +2475 ); +2476 +2477 let mut decoded = frames(buf); +2478 assert_eq!(decoded.len(), 1); +2479 match decoded.pop().expect("non empty") { +2480 Frame::ReachOut(decoded) => assert_eq!(decoded, reach_out), +2481 x => panic!("incorrect frame {x:?}"), +2482 } +2483 } +2484 +2485 /// Test that encoding and decoding [`RemoveAddress`] produces the same result +2486 #[test] +2487 fn test_remove_address_roundrip() { +2488 let remove_addr = RemoveAddress::new(VarInt(10)); +2489 let mut buf = Vec::with_capacity(remove_addr.size()); +2490 remove_addr.encode(&mut buf); +2491 +2492 assert_eq!( +2493 remove_addr.size(), +2494 buf.len(), +2495 "expected written bytes and actual size differ" +2496 ); +2497 +2498 let mut decoded = frames(buf); +2499 assert_eq!(decoded.len(), 1); +2500 match decoded.pop().expect("non empty") { +2501 Frame::RemoveAddress(decoded) => assert_eq!(decoded, remove_addr), +2502 x => panic!("incorrect frame {x:?}"), +2503 } +2504 } +2505} \ No newline at end of file