Network System 0.1.1
High-performance modular networking library for scalable client-server applications
Loading...
Searching...
No Matches
pmtud_controller.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2024, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
6
7#include <algorithm>
8
10{
11
12auto pmtud_state_to_string(pmtud_state state) noexcept -> const char*
13{
14 switch (state)
15 {
17 return "disabled";
19 return "base";
21 return "searching";
23 return "search_complete";
25 return "error";
26 }
27 return "unknown";
28}
29
34
36 : config_(config)
37 , current_mtu_(config_.min_mtu)
38 , search_low_(config_.min_mtu)
39 , search_high_(config_.max_probe_mtu)
40{
41}
42
57
65
77
79 std::chrono::steady_clock::time_point now) const noexcept -> bool
80{
81 // Don't probe if disabled or probe already in flight
82 if (state_ == pmtud_state::disabled || probe_in_flight_)
83 {
84 return false;
85 }
86
87 // In base state, always ready to start probing
88 if (state_ == pmtud_state::base)
89 {
90 return true;
91 }
92
93 // In searching state, check if enough time has passed
94 if (state_ == pmtud_state::searching)
95 {
96 auto elapsed = now - last_probe_time_;
97 return elapsed >= config_.probe_interval;
98 }
99
100 // In search_complete state, check if re-validation is needed
101 if (state_ == pmtud_state::search_complete)
102 {
103 auto elapsed = now - search_complete_time_;
104 return elapsed >= config_.confirmation_interval;
105 }
106
107 // In error state, check if enough time for recovery
108 if (state_ == pmtud_state::error)
109 {
110 auto elapsed = now - last_probe_time_;
111 return elapsed >= config_.probe_timeout;
112 }
113
114 return false;
115}
116
117auto pmtud_controller::probe_size() const noexcept -> std::optional<size_t>
118{
120 {
121 return std::nullopt;
122 }
123
125 {
126 return probing_mtu_ > 0 ? std::optional<size_t>(probing_mtu_) : std::nullopt;
127 }
128
129 // For re-validation in search_complete state
131 {
132 return current_mtu_;
133 }
134
135 return std::nullopt;
136}
137
139 size_t size, std::chrono::steady_clock::time_point sent_time)
140{
141 probing_mtu_ = size;
142 last_probe_time_ = sent_time;
143 probe_in_flight_ = true;
144 ++probe_count_;
145}
146
148{
149 probe_in_flight_ = false;
151
152 // Handle based on current state
154 {
155 // Successful probe - update validated MTU
156 if (size > current_mtu_)
157 {
158 current_mtu_ = size;
159 search_low_ = size;
160 }
161
162 // Check if we've found the maximum
164 {
166 }
167 else
168 {
169 // Continue searching
171 probe_count_ = 0;
173 }
174 }
176 {
177 // Re-validation successful
178 search_complete_time_ = std::chrono::steady_clock::now();
179 }
180 else if (state_ == pmtud_state::error)
181 {
182 // Recovery successful
184 probe_count_ = 0;
186 }
187}
188
190{
191 probe_in_flight_ = false;
193
194 // Check for black hole
196 {
198 return;
199 }
200
202 {
204 {
205 // Too many failures at this size - reduce search range
206 search_high_ = size;
207 probe_count_ = 0;
208
209 // Check if we've converged
211 {
213 }
214 else
215 {
217 }
218 }
219 // Otherwise, will retry the same size on next should_probe()
220 }
222 {
223 // Re-validation failed - MTU may have decreased
224 // Fall back to error state and restart search
229 probe_count_ = 0;
230 }
231}
232
233void pmtud_controller::on_packet_too_big(size_t reported_mtu)
234{
235 // RFC 8899: ICMP PTB should trigger immediate MTU reduction
236 if (reported_mtu >= config_.min_mtu && reported_mtu < current_mtu_)
237 {
238 current_mtu_ = reported_mtu;
239 search_high_ = reported_mtu;
240
242 {
243 // Need to restart search with new upper bound
245 probe_count_ = 0;
247 }
248 }
249 else if (reported_mtu < config_.min_mtu)
250 {
251 // PTB reports MTU below QUIC minimum - possible black hole
253 }
254}
255
257 -> std::optional<std::chrono::steady_clock::time_point>
258{
260 {
261 return std::nullopt;
262 }
263
265 {
266 // Timeout for probe in flight
268 }
269
271 {
273 }
274
276 {
278 }
279
281 {
283 }
284
285 return std::nullopt;
286}
287
288void pmtud_controller::on_timeout(std::chrono::steady_clock::time_point now)
289{
291 {
292 return;
293 }
294
296 {
297 // Probe timeout - treat as loss
298 auto elapsed = now - last_probe_time_;
299 if (elapsed >= config_.probe_timeout)
300 {
302 }
303 }
304}
305
314
315auto pmtud_controller::calculate_next_probe_size() const noexcept -> size_t
316{
317 // Binary search - probe the midpoint
318 size_t mid = search_low_ + (search_high_ - search_low_) / 2;
319
320 // Round up to ensure we make progress
321 if (mid == search_low_ && search_high_ > search_low_)
322 {
324 }
325
326 return std::min(mid, search_high_);
327}
328
330{
332 search_complete_time_ = std::chrono::steady_clock::now();
333 probing_mtu_ = 0;
334 probe_count_ = 0;
335}
336
338{
339 // Black hole detected - reset to minimum MTU
344 probing_mtu_ = 0;
345 probe_count_ = 0;
347 probe_in_flight_ = false;
348}
349
350} // namespace kcenon::network::protocols::quic
Path MTU Discovery controller for QUIC (RFC 8899 DPLPMTUD)
void complete_search()
Transition to search_complete state.
std::chrono::steady_clock::time_point last_probe_time_
Time of last probe sent.
void on_packet_too_big(size_t reported_mtu)
Handle ICMP Packet Too Big message.
pmtud_controller()
Default constructor with default configuration.
size_t search_high_
Upper bound for binary search (target MTU)
void on_probe_sent(size_t size, std::chrono::steady_clock::time_point sent_time)
Record that a probe was sent.
void enable()
Enable PMTUD and start probing.
size_t search_low_
Lower bound for binary search (known good MTU)
size_t probe_count_
Number of probes sent at current size.
static constexpr size_t kBlackHoleThreshold
Threshold for black hole detection.
auto next_timeout() const noexcept -> std::optional< std::chrono::steady_clock::time_point >
Get next timeout deadline for PMTUD.
bool probe_in_flight_
Whether a probe is currently in flight.
void on_probe_acked(size_t size)
Handle probe acknowledgment.
void on_probe_lost(size_t size)
Handle probe loss.
size_t consecutive_failures_
Number of consecutive probe failures (for black hole detection)
std::chrono::steady_clock::time_point search_complete_time_
Time when search was completed (for re-validation)
auto should_probe(std::chrono::steady_clock::time_point now) const noexcept -> bool
Check if a probe should be sent.
size_t probing_mtu_
Current probe size being tested.
auto calculate_next_probe_size() const noexcept -> size_t
Calculate next probe size using binary search.
auto probe_size() const noexcept -> std::optional< size_t >
Get the size for next probe packet.
void on_timeout(std::chrono::steady_clock::time_point now)
Handle timeout event.
void handle_black_hole()
Handle black hole detection.
tracing_config config
Definition exporters.cpp:29
pmtud_state
States for DPLPMTUD state machine (RFC 8899 Section 5.2)
@ search_complete
Maximum MTU found and validated.
@ base
Using BASE_PLPMTU (minimum MTU)
@ searching
Binary search for larger MTU.
@ error
Black hole detected, reset to base.
auto pmtud_state_to_string(pmtud_state state) noexcept -> const char *
Convert PMTUD state to string.
Configuration for PMTUD controller.
std::chrono::seconds probe_timeout
Timeout for probe packets before considering them lost.
std::chrono::milliseconds probe_interval
Interval between probe attempts during search.
size_t max_probes
Maximum number of probes before giving up at current size.
size_t probe_step
Step size for probing (binary search uses mid-point)
size_t max_probe_mtu
Maximum MTU to probe (typical Ethernet is 1500)
std::chrono::seconds confirmation_interval
Interval for re-validation after search is complete.
size_t min_mtu
Minimum MTU (RFC 9000 requires 1200 bytes for QUIC)