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