Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 18 additions & 47 deletions quinn-proto/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4279,44 +4279,21 @@ impl Connection {
.get_mut(&path_id)
.expect("payload is processed only after the path becomes known");

match path.data.challenges_sent.get(&response.0) {
// Response to an on-path PathChallenge
Some(info) if info.remote == remote && path.data.remote == remote => {
let sent_instant = info.sent_instant;
use paths::OnPathResponseReceived::*;
match path.data.on_path_response_received(now, response.0, remote) {
OnPath { was_open } => {
// TODO(@divma): reset timers using the remaining off-path challenges
self.timers.stop(
Timer::PerPath(path_id, PathTimer::PathValidation),
self.qlog.with_time(now),
);
self.timers.stop(
Timer::PerPath(path_id, PathTimer::PathChallengeLost),
self.qlog.with_time(now),
);
if !path.data.validated {
trace!("new path validated");
}
self.timers.stop(
Timer::PerPath(path_id, PathTimer::PathOpen),
self.qlog.with_time(now),
);
// Clear any other on-path sent challenge.
path.data
.challenges_sent
.retain(|_token, info| info.remote != remote);
path.data.send_new_challenge = false;
path.data.validated = true;

// This RTT can only be used for the initial RTT, not as a normal
// sample: https://www.rfc-editor.org/rfc/rfc9002#section-6.2.2-2.
let rtt = now.saturating_duration_since(sent_instant);
path.data.rtt.reset_initial_rtt(rtt);

self.events
.push_back(Event::Path(PathEvent::Opened { id: path_id }));
// mark the path as open from the application perspective now that Opened
// event has been queued
if !std::mem::replace(&mut path.data.open, true) {
trace!("path opened");
use PathTimer::*;
let qlog = self.qlog.with_time(now);

self.timers
.stop(Timer::PerPath(path_id, PathValidation), qlog.clone());
self.timers
.stop(Timer::PerPath(path_id, PathChallengeLost), qlog.clone());
self.timers.stop(Timer::PerPath(path_id, PathOpen), qlog);
if !was_open {
self.events
.push_back(Event::Path(PathEvent::Opened { id: path_id }));
if let Some(observed) = path.data.last_observed_addr_report.as_ref()
{
self.events.push_back(Event::Path(PathEvent::ObservedAddr {
Expand All @@ -4330,19 +4307,13 @@ impl Connection {
prev.send_new_challenge = false;
}
}
// Response to an off-path PathChallenge
Some(info) if info.remote == remote => {
OffPath => {
debug!("Response to off-path PathChallenge!");
path.data
.challenges_sent
.retain(|_token, info| info.remote != remote);
}
// Response to a PathChallenge we recognize, but from an invalid remote
Some(info) => {
debug!(%response, from=%remote, expected=%info.remote, "ignoring invalid PATH_RESPONSE")
Invalid { expected } => {
debug!(%response, from=%remote, %expected, "ignoring invalid PATH_RESPONSE")
}
// Response to an unknown PathChallenge
None => debug!(%response, "ignoring invalid PATH_RESPONSE"),
Unknown => debug!(%response, "ignoring invalid PATH_RESPONSE"),
}
}
Frame::MaxData(bytes) => {
Expand Down
57 changes: 57 additions & 0 deletions quinn-proto/src/connection/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,49 @@ impl PathData {
}
}

/// Handle receiving a PATH_RESPONSE.
pub(super) fn on_path_response_received(
&mut self,
now: Instant,
token: u64,
remote: SocketAddr,
) -> OnPathResponseReceived {
match self.challenges_sent.get(&token) {
// Response to an on-path PathChallenge
Some(info) if info.remote == remote && self.remote == remote => {
let sent_instant = info.sent_instant;
if !std::mem::replace(&mut self.validated, true) {
trace!("new path validated");
}
// Clear any other on-path sent challenge.
self.challenges_sent
.retain(|_token, info| info.remote != remote);

self.send_new_challenge = false;

// This RTT can only be used for the initial RTT, not as a normal
// sample: https://www.rfc-editor.org/rfc/rfc9002#section-6.2.2-2.
let rtt = now.saturating_duration_since(sent_instant);
self.rtt.reset_initial_rtt(rtt);

let was_open = std::mem::replace(&mut self.open, true);
OnPathResponseReceived::OnPath { was_open }
}
// Response to an off-path PathChallenge
Some(info) if info.remote == remote => {
self.challenges_sent
.retain(|_token, info| info.remote != remote);
OnPathResponseReceived::OffPath
}
// Response to a PathChallenge we recognize, but from an invalid remote
Some(info) => OnPathResponseReceived::Invalid {
expected: info.remote,
},
// Response to an unknown PathChallenge
None => OnPathResponseReceived::Unknown,
}
}

#[cfg(feature = "qlog")]
pub(super) fn qlog_recovery_metrics(
&mut self,
Expand Down Expand Up @@ -469,6 +512,20 @@ impl PathData {
}
}

pub(super) enum OnPathResponseReceived {
/// This response validates the path on its current remote address.
OnPath { was_open: bool },
/// This response is valid, but it's for a remote other than the path's current remote address.
OffPath,
/// The received token is unknown.
Unknown,
/// The response is invalid.
Invalid {
/// The remote that was expected for this token.
expected: SocketAddr,
},
}

/// Congestion metrics as described in [`recovery_metrics_updated`].
///
/// [`recovery_metrics_updated`]: https://datatracker.ietf.org/doc/html/draft-ietf-quic-qlog-quic-events.html#name-recovery_metrics_updated
Expand Down
1 change: 1 addition & 0 deletions quinn-proto/src/connection/qlog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ impl QlogSink {
}

/// A [`QlogSink`] with a `now` timestamp.
#[derive(Clone)]
pub(super) struct QlogSinkWithTime<'a> {
#[cfg(feature = "qlog")]
sink: &'a QlogSink,
Expand Down
Loading