Skip to content

Commit

Permalink
Implement PLPMTUD
Browse files Browse the repository at this point in the history
Sponsored by Stormshield
  • Loading branch information
aochagavia committed Mar 21, 2023
1 parent 82cf8f1 commit 6fe50b5
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 65 deletions.
40 changes: 32 additions & 8 deletions quinn-proto/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use crate::{
cid_generator::{ConnectionIdGenerator, RandomConnectionIdGenerator},
congestion,
crypto::{self, HandshakeTokenKey, HmacKey},
VarInt, VarIntBoundsExceeded, DEFAULT_SUPPORTED_VERSIONS, INITIAL_MAX_UDP_PAYLOAD_SIZE,
VarInt, VarIntBoundsExceeded, DEFAULT_PLPMTUD_INTERVAL, DEFAULT_SUPPORTED_VERSIONS,
INITIAL_MAX_UDP_PAYLOAD_SIZE,
};

/// Parameters governing the core QUIC state machine
Expand Down Expand Up @@ -37,6 +38,7 @@ pub struct TransportConfig {
pub(crate) time_threshold: f32,
pub(crate) initial_rtt: Duration,
pub(crate) initial_max_udp_payload_size: u16,
pub(crate) plpmtud_interval: Option<Duration>,

pub(crate) persistent_congestion_threshold: u32,
pub(crate) keep_alive_interval: Option<Duration>,
Expand Down Expand Up @@ -154,23 +156,44 @@ impl TransportConfig {
self
}

/// UDP payload size that the network must be capable of carrying
///
/// Effective max UDP payload size may change over the life of the connection in the future due
/// to path MTU detection, but will never fall below this value.
/// The initial value to be used as the maximum UDP payload size before running PLPMTUD (see
/// [`TransportConfig::plpmtud_interval`]).
///
/// Must be at least 1200, which is the default, and known to be safe for typical internet
/// applications. Larger values are more efficient, but increase the risk of unpredictable
/// catastrophic packet loss due to exceeding the network path's IP MTU.
/// catastrophic packet loss due to exceeding the network path's IP MTU. If the provided value
/// is higher than what the network path actually supports, packet loss will eventually trigger
/// black hole detection and bring it down to 1200.
///
/// Real-world MTUs can vary according to ISP, VPN, and properties of intermediate network links
/// outside of either endpoint's control. Extreme caution should be used when raising this value
/// for connections outside of private networks where these factors are fully controlled.
/// outside of either endpoint's control. Caution should be used when raising this value for
/// connections outside of private networks where these factors are fully controlled.
pub fn initial_max_udp_payload_size(&mut self, value: u16) -> &mut Self {
self.initial_max_udp_payload_size = value.max(INITIAL_MAX_UDP_PAYLOAD_SIZE);
self
}

/// Specifies the time to wait after completing PLPMTUD before starting a new MTU discovery
/// round. Defaults to 600 seconds, as recommended by
/// [RFC 8899](https://www.rfc-editor.org/rfc/rfc8899).
///
/// PLPMTUD performs a binary search, trying to find the highest packet size that is still
/// supported by the current network path. The lower bound of the search is equal to
/// `initial_max_udp_payload_size` in the initial PLPMTUD round, and is equal to the currently
/// discovered MTU in subsequent runs. The upper bound is always the minimum value between
/// [`EndpointConfig::max_udp_payload_size`] and the peer's `MAX_UDP_PAYLOAD_SIZE` transport
/// parameter, which we receive during the handshake.
///
/// If, at some point, the network path no longer accepts packets of the detected size, packet
/// loss will eventually trigger black hole detection and reset the detected MTU to 1200. In
/// that case, the timer for the next PLPMTUD round will be reset as well.
///
/// Setting this value to `None` disables PLPMTUD altogether.
pub fn plpmtud_interval(&mut self, value: Option<Duration>) -> &mut Self {
self.plpmtud_interval = value;
self
}

/// Number of consecutive PTOs after which network is considered to be experiencing persistent congestion.
pub fn persistent_congestion_threshold(&mut self, value: u32) -> &mut Self {
self.persistent_congestion_threshold = value;
Expand Down Expand Up @@ -267,6 +290,7 @@ impl Default for TransportConfig {
time_threshold: 9.0 / 8.0,
initial_rtt: Duration::from_millis(333), // per spec, intentionally distinct from EXPECTED_RTT
initial_max_udp_payload_size: INITIAL_MAX_UDP_PAYLOAD_SIZE,
plpmtud_interval: Some(DEFAULT_PLPMTUD_INTERVAL),

persistent_congestion_threshold: 3,
keep_alive_interval: None,
Expand Down
6 changes: 4 additions & 2 deletions quinn-proto/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ impl Connection {
config.congestion_controller_factory.build(now),
config.initial_max_udp_payload_size,
max_udp_payload_size,
config.plpmtud_interval,
now,
path_validated,
),
Expand Down Expand Up @@ -779,7 +780,7 @@ impl Connection {
let probe_size = match self
.path
.mtud
.poll_transmit(self.spaces[space_id].next_packet_number)
.poll_transmit(now, self.spaces[space_id].next_packet_number)
{
Some(next_probe_size) => next_probe_size,
None => return None,
Expand Down Expand Up @@ -1473,7 +1474,7 @@ impl Connection {
self.streams.retransmit(frame);
}
self.spaces[pn_space].pending |= info.retransmits;
self.path.mtud.lost_packet(*packet, info.size);
self.path.mtud.lost_packet(now, *packet, info.size);
}
// Don't apply congestion penalty for lost ack-only packets
let lost_ack_eliciting = old_bytes_in_flight != self.in_flight.bytes;
Expand Down Expand Up @@ -2726,6 +2727,7 @@ impl Connection {
.min(peer_max_udp_payload_size)
.min(local_max_udp_payload_size),
local_max_udp_payload_size.min(peer_max_udp_payload_size),
self.config.plpmtud_interval,
now,
false,
)
Expand Down
Loading

0 comments on commit 6fe50b5

Please sign in to comment.