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
10 changes: 10 additions & 0 deletions apps/src/bin/quiche-server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,16 @@ fn handle_path_events(client: &mut Client) {
peer_addr
);
},

quiche::PathEvent::PmtuUpdated { local, peer, pmtu } => info!(
"{} Path ({}, {}) validated PMTU {}",
client.conn.trace_id(),
local,
peer,
pmtu
),

_ => (),
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions apps/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,11 @@ pub fn connect(
},

quiche::PathEvent::PeerMigrated(..) => unreachable!(),

quiche::PathEvent::PmtuUpdated { local, peer, pmtu } =>
info!("Path ({local}, {peer}) validated PMTU {pmtu}"),

_ => (),
}
}

Expand Down
8 changes: 8 additions & 0 deletions quiche/include/quiche.h
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,7 @@ enum quiche_path_event_type {
QUICHE_PATH_EVENT_CLOSED,
QUICHE_PATH_EVENT_REUSED_SOURCE_CONNECTION_ID,
QUICHE_PATH_EVENT_PEER_MIGRATED,
QUICHE_PATH_EVENT_PMTU_UPDATED,
};

typedef struct quiche_path_event quiche_path_event;
Expand Down Expand Up @@ -930,6 +931,13 @@ void quiche_path_event_peer_migrated(const quiche_path_event *ev,
struct sockaddr_storage *local, socklen_t *local_len,
struct sockaddr_storage *peer, socklen_t *peer_len);

// Should be called if the quiche_path_event_type(...) returns QUICHE_PATH_EVENT_PMTU_UPDATED.
// Sets "pmtu" to the current validated PMTU limit for normal application traffic.
void quiche_path_event_pmtu_updated(const quiche_path_event *ev,
struct sockaddr_storage *local, socklen_t *local_len,
struct sockaddr_storage *peer, socklen_t *peer_len,
size_t *pmtu);

// Frees the path event object.
void quiche_path_event_free(quiche_path_event *ev);

Expand Down
70 changes: 70 additions & 0 deletions quiche/src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1865,6 +1865,8 @@ pub extern "C" fn quiche_path_event_type(ev: &PathEvent) -> u32 {
PathEvent::ReusedSourceConnectionId { .. } => 4,

PathEvent::PeerMigrated { .. } => 5,

PathEvent::PmtuUpdated { .. } => 6,
}
}

Expand Down Expand Up @@ -1970,6 +1972,27 @@ pub extern "C" fn quiche_path_event_peer_migrated(
}
}

#[no_mangle]
pub extern "C" fn quiche_path_event_pmtu_updated(
ev: &PathEvent, local_addr: &mut sockaddr_storage,
local_addr_len: &mut socklen_t, peer_addr: &mut sockaddr_storage,
peer_addr_len: &mut socklen_t, pmtu: &mut size_t,
) {
match ev {
PathEvent::PmtuUpdated {
local,
peer,
pmtu: value,
} => {
*local_addr_len = std_addr_to_c(local, local_addr);
*peer_addr_len = std_addr_to_c(peer, peer_addr);
*pmtu = *value;
},

_ => unreachable!(),
}
}

#[no_mangle]
pub extern "C" fn quiche_path_event_free(ev: *mut PathEvent) {
if !ev.is_null() {
Expand Down Expand Up @@ -2197,6 +2220,53 @@ mod tests {
#[cfg(windows)]
use windows_sys::Win32::Networking::WinSock::inet_ntop;

#[test]
fn pmtu_updated_path_event() {
let local = "127.0.0.1:8080".parse().unwrap();
let peer = "127.0.0.2:443".parse().unwrap();

let event = PathEvent::PmtuUpdated {
local,
peer,
pmtu: 1400,
};
assert_eq!(quiche_path_event_type(&event), 6);

let mut local_out: sockaddr_storage = unsafe { std::mem::zeroed() };
let mut peer_out: sockaddr_storage = unsafe { std::mem::zeroed() };
let mut local_len = 0;
let mut peer_len = 0;
let mut pmtu = usize::MAX;

quiche_path_event_pmtu_updated(
&event,
&mut local_out,
&mut local_len,
&mut peer_out,
&mut peer_len,
&mut pmtu,
);
assert_eq!(pmtu, 1400);
assert_eq!(
unsafe {
std_addr_from_c(
&*(&local_out as *const _ as *const sockaddr),
local_len,
)
},
local
);
assert_eq!(
unsafe {
std_addr_from_c(
&*(&peer_out as *const _ as *const sockaddr),
peer_len,
)
},
peer
);
}

#[test]
fn addr_v4() {
let addr = "127.0.0.1:8080".parse().unwrap();
Expand Down
79 changes: 63 additions & 16 deletions quiche/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,9 @@ impl Config {

/// Configures whether to do path MTU discovery.
///
/// PMTUD-driven packet limit updates are reported to the application
/// through [`PathEvent::PmtuUpdated`].
///
/// The default value is `false`.
pub fn discover_pmtu(&mut self, discover: bool) {
self.pmtud = discover;
Expand Down Expand Up @@ -3531,24 +3534,29 @@ impl<F: BufFactory> Connection<F> {

// Process acked frames. Note that several packets from several paths
// might have been acked by the received packet.
for (_, p) in self.paths.iter_mut() {
let (paths, path_events) = self.paths.iter_mut_and_events();
for (_, p) in paths {
while let Some(acked) = p.recovery.next_acked_frame(epoch) {
match acked {
frame::Frame::Ping {
mtu_probe: Some(mtu_probe),
} => {
trace!(
"{} pmtud probe acked; probe size {:?}",
self.trace_id,
mtu_probe
);

let local = p.local_addr();
let peer = p.peer_addr();
if let Some(pmtud) = p.pmtud.as_mut() {
trace!(
"{} pmtud probe acked; probe size {:?}",
self.trace_id,
mtu_probe
);
let old_pmtu = pmtud.get_current_mtu();
let current_mtu = pmtud.successful_probe(mtu_probe);
let new_pmtu = pmtud.get_current_mtu();

// Update the datagram size only after validating
// the MTU.
if let Some(current_mtu) =
pmtud.successful_probe(mtu_probe)
{
if let Some(current_mtu) = current_mtu {
qlog_with_type!(
EventType::QuicEventType(
QuicEventType::MtuUpdated
Expand Down Expand Up @@ -3577,6 +3585,12 @@ impl<F: BufFactory> Connection<F> {
p.recovery
.pmtud_update_max_datagram_size(current_mtu);
}

if let Some(event) =
path::pmtu_event(local, peer, old_pmtu, new_pmtu)
{
path_events.push_back(event);
}
}
},

Expand Down Expand Up @@ -4137,7 +4151,8 @@ impl<F: BufFactory> Connection<F> {
let crypto_ctx = &mut self.crypto_ctx[epoch];

// Process lost frames. There might be several paths having lost frames.
for (_, p) in self.paths.iter_mut() {
let (paths, path_events) = self.paths.iter_mut_and_events();
for (_, p) in paths {
while let Some(lost) = p.recovery.next_lost_frame(epoch) {
match lost {
frame::Frame::CryptoHeader { offset, length } => {
Expand Down Expand Up @@ -4301,9 +4316,22 @@ impl<F: BufFactory> Connection<F> {
frame::Frame::Ping { mtu_probe } => {
// Ping frames are not retransmitted.
if let Some(failed_probe) = mtu_probe {
trace!("pmtud probe dropped: {failed_probe}");

let local = p.local_addr();
let peer = p.peer_addr();
if let Some(pmtud) = p.pmtud.as_mut() {
trace!("pmtud probe dropped: {failed_probe}");
let old_pmtu = pmtud.get_current_mtu();
pmtud.failed_probe(failed_probe);
let new_pmtu = pmtud.get_current_mtu();

if let Some(event) = path::pmtu_event(
local, peer, old_pmtu, new_pmtu,
) {
p.recovery
.pmtud_update_max_datagram_size(new_pmtu);
path_events.push_back(event);
}
}
}
},
Expand Down Expand Up @@ -7702,13 +7730,32 @@ impl<F: BufFactory> Connection<F> {
/// Revalidates the PMTU for the active path by sending a new probe packet
/// of PMTU size. If the probe is dropped PMTUD will restart and find a new
/// valid PMTU.
///
/// If revalidation invalidates a previously discovered larger size, a
/// [`PathEvent::PmtuUpdated`] event is queued with QUIC's minimum packet
/// size. Further events report larger sizes as probes validate them.
#[inline]
pub fn revalidate_pmtu(&mut self) {
if let Ok(active_path) = self.paths.get_active_mut() {
if let Some(pmtud) = active_path.pmtud.as_mut() {
pmtud.revalidate_pmtu();
}
}
let Ok(active_path) = self.paths.get_active_mut() else {
return;
};

let local = active_path.local_addr();
let peer = active_path.peer_addr();
let Some(pmtud) = active_path.pmtud.as_mut() else {
return;
};

let old_pmtu = pmtud.get_current_mtu();
pmtud.revalidate_pmtu();

let Some(event) =
path::pmtu_event(local, peer, old_pmtu, pmtud.get_current_mtu())
else {
return;
};

self.paths.notify_event(event);
Comment thread
jrouviere marked this conversation as resolved.
}
Comment thread
kkelkar marked this conversation as resolved.

/// Returns true if the connection handshake is complete.
Expand Down
Loading
Loading