/src/openssl32/ssl/quic/quic_fc.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved. |
3 | | * |
4 | | * Licensed under the Apache License 2.0 (the "License"). You may not use |
5 | | * this file except in compliance with the License. You can obtain a copy |
6 | | * in the file LICENSE in the source distribution or at |
7 | | * https://www.openssl.org/source/license.html |
8 | | */ |
9 | | |
10 | | #include "internal/quic_fc.h" |
11 | | #include "internal/quic_error.h" |
12 | | #include "internal/common.h" |
13 | | #include "internal/safe_math.h" |
14 | | #include <assert.h> |
15 | | |
16 | | OSSL_SAFE_MATH_UNSIGNED(uint64_t, uint64_t) |
17 | | |
18 | | /* |
19 | | * TX Flow Controller (TXFC) |
20 | | * ========================= |
21 | | */ |
22 | | |
23 | | int ossl_quic_txfc_init(QUIC_TXFC *txfc, QUIC_TXFC *conn_txfc) |
24 | 83.2k | { |
25 | 83.2k | if (conn_txfc != NULL && conn_txfc->parent != NULL) |
26 | 0 | return 0; |
27 | | |
28 | 83.2k | txfc->swm = 0; |
29 | 83.2k | txfc->cwm = 0; |
30 | 83.2k | txfc->parent = conn_txfc; |
31 | 83.2k | txfc->has_become_blocked = 0; |
32 | 83.2k | return 1; |
33 | 83.2k | } |
34 | | |
35 | | QUIC_TXFC *ossl_quic_txfc_get_parent(QUIC_TXFC *txfc) |
36 | 0 | { |
37 | 0 | return txfc->parent; |
38 | 0 | } |
39 | | |
40 | | int ossl_quic_txfc_bump_cwm(QUIC_TXFC *txfc, uint64_t cwm) |
41 | 43.2k | { |
42 | 43.2k | if (cwm <= txfc->cwm) |
43 | 17.2k | return 0; |
44 | | |
45 | 26.0k | txfc->cwm = cwm; |
46 | 26.0k | return 1; |
47 | 43.2k | } |
48 | | |
49 | | uint64_t ossl_quic_txfc_get_credit_local(QUIC_TXFC *txfc, uint64_t consumed) |
50 | 38.6k | { |
51 | 38.6k | assert((txfc->swm + consumed) <= txfc->cwm); |
52 | 38.6k | return txfc->cwm - (consumed + txfc->swm); |
53 | 38.6k | } |
54 | | |
55 | | uint64_t ossl_quic_txfc_get_credit(QUIC_TXFC *txfc, uint64_t consumed) |
56 | 17.6k | { |
57 | 17.6k | uint64_t r, conn_r; |
58 | | |
59 | 17.6k | r = ossl_quic_txfc_get_credit_local(txfc, 0); |
60 | | |
61 | 17.6k | if (txfc->parent != NULL) { |
62 | 17.6k | assert(txfc->parent->parent == NULL); |
63 | 17.6k | conn_r = ossl_quic_txfc_get_credit_local(txfc->parent, consumed); |
64 | 17.6k | if (conn_r < r) |
65 | 9.14k | r = conn_r; |
66 | 17.6k | } |
67 | | |
68 | 17.6k | return r; |
69 | 17.6k | } |
70 | | |
71 | | int ossl_quic_txfc_consume_credit_local(QUIC_TXFC *txfc, uint64_t num_bytes) |
72 | 3.35k | { |
73 | 3.35k | int ok = 1; |
74 | 3.35k | uint64_t credit = ossl_quic_txfc_get_credit_local(txfc, 0); |
75 | | |
76 | 3.35k | if (num_bytes > credit) { |
77 | 0 | ok = 0; |
78 | 0 | num_bytes = credit; |
79 | 0 | } |
80 | | |
81 | 3.35k | if (num_bytes > 0 && num_bytes == credit) |
82 | 368 | txfc->has_become_blocked = 1; |
83 | | |
84 | 3.35k | txfc->swm += num_bytes; |
85 | 3.35k | return ok; |
86 | 3.35k | } |
87 | | |
88 | | int ossl_quic_txfc_consume_credit(QUIC_TXFC *txfc, uint64_t num_bytes) |
89 | 1.67k | { |
90 | 1.67k | int ok = ossl_quic_txfc_consume_credit_local(txfc, num_bytes); |
91 | | |
92 | 1.67k | if (txfc->parent != NULL) { |
93 | 1.67k | assert(txfc->parent->parent == NULL); |
94 | 1.67k | if (!ossl_quic_txfc_consume_credit_local(txfc->parent, num_bytes)) |
95 | 0 | return 0; |
96 | 1.67k | } |
97 | | |
98 | 1.67k | return ok; |
99 | 1.67k | } |
100 | | |
101 | | int ossl_quic_txfc_has_become_blocked(QUIC_TXFC *txfc, int clear) |
102 | 0 | { |
103 | 0 | int r = txfc->has_become_blocked; |
104 | |
|
105 | 0 | if (clear) |
106 | 0 | txfc->has_become_blocked = 0; |
107 | |
|
108 | 0 | return r; |
109 | 0 | } |
110 | | |
111 | | uint64_t ossl_quic_txfc_get_cwm(QUIC_TXFC *txfc) |
112 | 6.08k | { |
113 | 6.08k | return txfc->cwm; |
114 | 6.08k | } |
115 | | |
116 | | uint64_t ossl_quic_txfc_get_swm(QUIC_TXFC *txfc) |
117 | 35.3k | { |
118 | 35.3k | return txfc->swm; |
119 | 35.3k | } |
120 | | |
121 | | /* |
122 | | * RX Flow Controller (RXFC) |
123 | | * ========================= |
124 | | */ |
125 | | |
126 | | int ossl_quic_rxfc_init(QUIC_RXFC *rxfc, QUIC_RXFC *conn_rxfc, |
127 | | uint64_t initial_window_size, |
128 | | uint64_t max_window_size, |
129 | | OSSL_TIME (*now)(void *now_arg), |
130 | | void *now_arg) |
131 | 193k | { |
132 | 193k | if (conn_rxfc != NULL && conn_rxfc->parent != NULL) |
133 | 0 | return 0; |
134 | | |
135 | 193k | rxfc->swm = 0; |
136 | 193k | rxfc->cwm = initial_window_size; |
137 | 193k | rxfc->rwm = 0; |
138 | 193k | rxfc->esrwm = 0; |
139 | 193k | rxfc->hwm = 0; |
140 | 193k | rxfc->cur_window_size = initial_window_size; |
141 | 193k | rxfc->max_window_size = max_window_size; |
142 | 193k | rxfc->parent = conn_rxfc; |
143 | 193k | rxfc->error_code = 0; |
144 | 193k | rxfc->has_cwm_changed = 0; |
145 | 193k | rxfc->epoch_start = ossl_time_zero(); |
146 | 193k | rxfc->now = now; |
147 | 193k | rxfc->now_arg = now_arg; |
148 | 193k | rxfc->is_fin = 0; |
149 | 193k | rxfc->standalone = 0; |
150 | 193k | return 1; |
151 | 193k | } |
152 | | |
153 | | int ossl_quic_rxfc_init_standalone(QUIC_RXFC *rxfc, |
154 | | uint64_t initial_window_size, |
155 | | OSSL_TIME (*now)(void *arg), |
156 | | void *now_arg) |
157 | 110k | { |
158 | 110k | if (!ossl_quic_rxfc_init(rxfc, NULL, |
159 | 110k | initial_window_size, initial_window_size, |
160 | 110k | now, now_arg)) |
161 | 0 | return 0; |
162 | | |
163 | 110k | rxfc->standalone = 1; |
164 | 110k | return 1; |
165 | 110k | } |
166 | | |
167 | | QUIC_RXFC *ossl_quic_rxfc_get_parent(QUIC_RXFC *rxfc) |
168 | 0 | { |
169 | 0 | return rxfc->parent; |
170 | 0 | } |
171 | | |
172 | | void ossl_quic_rxfc_set_max_window_size(QUIC_RXFC *rxfc, |
173 | | size_t max_window_size) |
174 | 0 | { |
175 | 0 | rxfc->max_window_size = max_window_size; |
176 | 0 | } |
177 | | |
178 | | static void rxfc_start_epoch(QUIC_RXFC *rxfc) |
179 | 29.4k | { |
180 | 29.4k | rxfc->epoch_start = rxfc->now(rxfc->now_arg); |
181 | 29.4k | rxfc->esrwm = rxfc->rwm; |
182 | 29.4k | } |
183 | | |
184 | | static int on_rx_controlled_bytes(QUIC_RXFC *rxfc, uint64_t num_bytes) |
185 | 50.5k | { |
186 | 50.5k | int ok = 1; |
187 | 50.5k | uint64_t credit = rxfc->cwm - rxfc->swm; |
188 | | |
189 | 50.5k | if (num_bytes > credit) { |
190 | 246 | ok = 0; |
191 | 246 | num_bytes = credit; |
192 | 246 | rxfc->error_code = QUIC_ERR_FLOW_CONTROL_ERROR; |
193 | 246 | } |
194 | | |
195 | 50.5k | rxfc->swm += num_bytes; |
196 | 50.5k | return ok; |
197 | 50.5k | } |
198 | | |
199 | | int ossl_quic_rxfc_on_rx_stream_frame(QUIC_RXFC *rxfc, uint64_t end, int is_fin) |
200 | 130k | { |
201 | 130k | uint64_t delta; |
202 | | |
203 | 130k | if (!rxfc->standalone && rxfc->parent == NULL) |
204 | 0 | return 0; |
205 | | |
206 | 130k | if (rxfc->is_fin && ((is_fin && rxfc->hwm != end) || end > rxfc->hwm)) { |
207 | | /* Stream size cannot change after the stream is finished */ |
208 | 169 | rxfc->error_code = QUIC_ERR_FINAL_SIZE_ERROR; |
209 | 169 | return 1; /* not a caller error */ |
210 | 169 | } |
211 | | |
212 | 130k | if (is_fin) |
213 | 27.7k | rxfc->is_fin = 1; |
214 | | |
215 | 130k | if (end > rxfc->hwm) { |
216 | 44.8k | delta = end - rxfc->hwm; |
217 | 44.8k | rxfc->hwm = end; |
218 | | |
219 | 44.8k | on_rx_controlled_bytes(rxfc, delta); /* result ignored */ |
220 | 44.8k | if (rxfc->parent != NULL) |
221 | 5.63k | on_rx_controlled_bytes(rxfc->parent, delta); /* result ignored */ |
222 | 85.7k | } else if (end < rxfc->hwm && is_fin) { |
223 | 10 | rxfc->error_code = QUIC_ERR_FINAL_SIZE_ERROR; |
224 | 10 | return 1; /* not a caller error */ |
225 | 10 | } |
226 | | |
227 | 130k | return 1; |
228 | 130k | } |
229 | | |
230 | | /* threshold = 3/4 */ |
231 | 37.6k | #define WINDOW_THRESHOLD_NUM 3 |
232 | 37.6k | #define WINDOW_THRESHOLD_DEN 4 |
233 | | |
234 | | static int rxfc_cwm_bump_desired(QUIC_RXFC *rxfc) |
235 | 37.6k | { |
236 | 37.6k | int err = 0; |
237 | 37.6k | uint64_t window_rem = rxfc->cwm - rxfc->rwm; |
238 | 37.6k | uint64_t threshold |
239 | 37.6k | = safe_muldiv_uint64_t(rxfc->cur_window_size, |
240 | 37.6k | WINDOW_THRESHOLD_NUM, WINDOW_THRESHOLD_DEN, &err); |
241 | | |
242 | 37.6k | if (err) |
243 | | /* |
244 | | * Extremely large window should never occur, but if it does, just use |
245 | | * 1/2 as the threshold. |
246 | | */ |
247 | 0 | threshold = rxfc->cur_window_size / 2; |
248 | | |
249 | | /* |
250 | | * No point emitting a new MAX_STREAM_DATA frame if the stream has a final |
251 | | * size. |
252 | | */ |
253 | 37.6k | return !rxfc->is_fin && window_rem <= threshold; |
254 | 37.6k | } |
255 | | |
256 | | static int rxfc_should_bump_window_size(QUIC_RXFC *rxfc, OSSL_TIME rtt) |
257 | 0 | { |
258 | | /* |
259 | | * dt: time since start of epoch |
260 | | * b: bytes of window consumed since start of epoch |
261 | | * dw: proportion of window consumed since start of epoch |
262 | | * T_window: time it will take to use up the entire window, based on dt, dw |
263 | | * RTT: The current estimated RTT. |
264 | | * |
265 | | * b = rwm - esrwm |
266 | | * dw = b / window_size |
267 | | * T_window = dt / dw |
268 | | * T_window = dt / (b / window_size) |
269 | | * T_window = (dt * window_size) / b |
270 | | * |
271 | | * We bump the window size if T_window < 4 * RTT. |
272 | | * |
273 | | * We leave the division by b on the LHS to reduce the risk of overflowing |
274 | | * our 64-bit nanosecond representation, which will afford plenty of |
275 | | * precision left over after the division anyway. |
276 | | */ |
277 | 0 | uint64_t b = rxfc->rwm - rxfc->esrwm; |
278 | 0 | OSSL_TIME now, dt, t_window; |
279 | |
|
280 | 0 | if (b == 0) |
281 | 0 | return 0; |
282 | | |
283 | 0 | now = rxfc->now(rxfc->now_arg); |
284 | 0 | dt = ossl_time_subtract(now, rxfc->epoch_start); |
285 | 0 | t_window = ossl_time_muldiv(dt, rxfc->cur_window_size, b); |
286 | |
|
287 | 0 | return ossl_time_compare(t_window, ossl_time_multiply(rtt, 4)) < 0; |
288 | 0 | } |
289 | | |
290 | | static void rxfc_adjust_window_size(QUIC_RXFC *rxfc, uint64_t min_window_size, |
291 | | OSSL_TIME rtt) |
292 | 0 | { |
293 | | /* Are we sending updates too often? */ |
294 | 0 | uint64_t new_window_size; |
295 | |
|
296 | 0 | new_window_size = rxfc->cur_window_size; |
297 | |
|
298 | 0 | if (rxfc_should_bump_window_size(rxfc, rtt)) |
299 | 0 | new_window_size *= 2; |
300 | |
|
301 | 0 | if (new_window_size < min_window_size) |
302 | 0 | new_window_size = min_window_size; |
303 | 0 | if (new_window_size > rxfc->max_window_size) /* takes precedence over min size */ |
304 | 0 | new_window_size = rxfc->max_window_size; |
305 | |
|
306 | 0 | rxfc->cur_window_size = new_window_size; |
307 | 0 | rxfc_start_epoch(rxfc); |
308 | 0 | } |
309 | | |
310 | | static void rxfc_update_cwm(QUIC_RXFC *rxfc, uint64_t min_window_size, |
311 | | OSSL_TIME rtt) |
312 | 37.6k | { |
313 | 37.6k | uint64_t new_cwm; |
314 | | |
315 | 37.6k | if (!rxfc_cwm_bump_desired(rxfc)) |
316 | 37.6k | return; |
317 | | |
318 | 0 | rxfc_adjust_window_size(rxfc, min_window_size, rtt); |
319 | |
|
320 | 0 | new_cwm = rxfc->rwm + rxfc->cur_window_size; |
321 | 0 | if (new_cwm > rxfc->cwm) { |
322 | 0 | rxfc->cwm = new_cwm; |
323 | 0 | rxfc->has_cwm_changed = 1; |
324 | 0 | } |
325 | 0 | } |
326 | | |
327 | | static int rxfc_on_retire(QUIC_RXFC *rxfc, uint64_t num_bytes, |
328 | | uint64_t min_window_size, |
329 | | OSSL_TIME rtt) |
330 | 37.6k | { |
331 | 37.6k | if (ossl_time_is_zero(rxfc->epoch_start)) |
332 | | /* This happens when we retire our first ever bytes. */ |
333 | 29.4k | rxfc_start_epoch(rxfc); |
334 | | |
335 | 37.6k | rxfc->rwm += num_bytes; |
336 | 37.6k | rxfc_update_cwm(rxfc, min_window_size, rtt); |
337 | 37.6k | return 1; |
338 | 37.6k | } |
339 | | |
340 | | int ossl_quic_rxfc_on_retire(QUIC_RXFC *rxfc, |
341 | | uint64_t num_bytes, |
342 | | OSSL_TIME rtt) |
343 | 35.6k | { |
344 | 35.6k | if (rxfc->parent == NULL && !rxfc->standalone) |
345 | 0 | return 0; |
346 | | |
347 | 35.6k | if (num_bytes == 0) |
348 | 0 | return 1; |
349 | | |
350 | 35.6k | if (rxfc->rwm + num_bytes > rxfc->swm) |
351 | | /* Impossible for us to retire more bytes than we have received. */ |
352 | 0 | return 0; |
353 | | |
354 | 35.6k | rxfc_on_retire(rxfc, num_bytes, 0, rtt); |
355 | | |
356 | 35.6k | if (!rxfc->standalone) |
357 | 2.00k | rxfc_on_retire(rxfc->parent, num_bytes, rxfc->cur_window_size, rtt); |
358 | | |
359 | 35.6k | return 1; |
360 | 35.6k | } |
361 | | |
362 | | uint64_t ossl_quic_rxfc_get_cwm(QUIC_RXFC *rxfc) |
363 | 66.9k | { |
364 | 66.9k | return rxfc->cwm; |
365 | 66.9k | } |
366 | | |
367 | | uint64_t ossl_quic_rxfc_get_swm(QUIC_RXFC *rxfc) |
368 | 0 | { |
369 | 0 | return rxfc->swm; |
370 | 0 | } |
371 | | |
372 | | uint64_t ossl_quic_rxfc_get_rwm(QUIC_RXFC *rxfc) |
373 | 0 | { |
374 | 0 | return rxfc->rwm; |
375 | 0 | } |
376 | | |
377 | | int ossl_quic_rxfc_has_cwm_changed(QUIC_RXFC *rxfc, int clear) |
378 | 8.24M | { |
379 | 8.24M | int r = rxfc->has_cwm_changed; |
380 | | |
381 | 8.24M | if (clear) |
382 | 563 | rxfc->has_cwm_changed = 0; |
383 | | |
384 | 8.24M | return r; |
385 | 8.24M | } |
386 | | |
387 | | int ossl_quic_rxfc_get_error(QUIC_RXFC *rxfc, int clear) |
388 | 130k | { |
389 | 130k | int r = rxfc->error_code; |
390 | | |
391 | 130k | if (clear) |
392 | 0 | rxfc->error_code = 0; |
393 | | |
394 | 130k | return r; |
395 | 130k | } |
396 | | |
397 | | int ossl_quic_rxfc_get_final_size(const QUIC_RXFC *rxfc, uint64_t *final_size) |
398 | 16.6k | { |
399 | 16.6k | if (!rxfc->is_fin) |
400 | 0 | return 0; |
401 | | |
402 | 16.6k | if (final_size != NULL) |
403 | 6 | *final_size = rxfc->hwm; |
404 | | |
405 | 16.6k | return 1; |
406 | 16.6k | } |