123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406 |
- /*
- * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution or at
- * https://www.openssl.org/source/license.html
- */
- #include "internal/quic_fc.h"
- #include "internal/quic_error.h"
- #include "internal/common.h"
- #include "internal/safe_math.h"
- #include <assert.h>
- OSSL_SAFE_MATH_UNSIGNED(uint64_t, uint64_t)
- /*
- * TX Flow Controller (TXFC)
- * =========================
- */
- int ossl_quic_txfc_init(QUIC_TXFC *txfc, QUIC_TXFC *conn_txfc)
- {
- if (conn_txfc != NULL && conn_txfc->parent != NULL)
- return 0;
- txfc->swm = 0;
- txfc->cwm = 0;
- txfc->parent = conn_txfc;
- txfc->has_become_blocked = 0;
- return 1;
- }
- QUIC_TXFC *ossl_quic_txfc_get_parent(QUIC_TXFC *txfc)
- {
- return txfc->parent;
- }
- int ossl_quic_txfc_bump_cwm(QUIC_TXFC *txfc, uint64_t cwm)
- {
- if (cwm <= txfc->cwm)
- return 0;
- txfc->cwm = cwm;
- return 1;
- }
- uint64_t ossl_quic_txfc_get_credit_local(QUIC_TXFC *txfc, uint64_t consumed)
- {
- assert((txfc->swm + consumed) <= txfc->cwm);
- return txfc->cwm - (consumed + txfc->swm);
- }
- uint64_t ossl_quic_txfc_get_credit(QUIC_TXFC *txfc, uint64_t consumed)
- {
- uint64_t r, conn_r;
- r = ossl_quic_txfc_get_credit_local(txfc, 0);
- if (txfc->parent != NULL) {
- assert(txfc->parent->parent == NULL);
- conn_r = ossl_quic_txfc_get_credit_local(txfc->parent, consumed);
- if (conn_r < r)
- r = conn_r;
- }
- return r;
- }
- int ossl_quic_txfc_consume_credit_local(QUIC_TXFC *txfc, uint64_t num_bytes)
- {
- int ok = 1;
- uint64_t credit = ossl_quic_txfc_get_credit_local(txfc, 0);
- if (num_bytes > credit) {
- ok = 0;
- num_bytes = credit;
- }
- if (num_bytes > 0 && num_bytes == credit)
- txfc->has_become_blocked = 1;
- txfc->swm += num_bytes;
- return ok;
- }
- int ossl_quic_txfc_consume_credit(QUIC_TXFC *txfc, uint64_t num_bytes)
- {
- int ok = ossl_quic_txfc_consume_credit_local(txfc, num_bytes);
- if (txfc->parent != NULL) {
- assert(txfc->parent->parent == NULL);
- if (!ossl_quic_txfc_consume_credit_local(txfc->parent, num_bytes))
- return 0;
- }
- return ok;
- }
- int ossl_quic_txfc_has_become_blocked(QUIC_TXFC *txfc, int clear)
- {
- int r = txfc->has_become_blocked;
- if (clear)
- txfc->has_become_blocked = 0;
- return r;
- }
- uint64_t ossl_quic_txfc_get_cwm(QUIC_TXFC *txfc)
- {
- return txfc->cwm;
- }
- uint64_t ossl_quic_txfc_get_swm(QUIC_TXFC *txfc)
- {
- return txfc->swm;
- }
- /*
- * RX Flow Controller (RXFC)
- * =========================
- */
- int 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)
- {
- if (conn_rxfc != NULL && conn_rxfc->parent != NULL)
- return 0;
- rxfc->swm = 0;
- rxfc->cwm = initial_window_size;
- rxfc->rwm = 0;
- rxfc->esrwm = 0;
- rxfc->hwm = 0;
- rxfc->cur_window_size = initial_window_size;
- rxfc->max_window_size = max_window_size;
- rxfc->parent = conn_rxfc;
- rxfc->error_code = 0;
- rxfc->has_cwm_changed = 0;
- rxfc->epoch_start = ossl_time_zero();
- rxfc->now = now;
- rxfc->now_arg = now_arg;
- rxfc->is_fin = 0;
- rxfc->standalone = 0;
- return 1;
- }
- int ossl_quic_rxfc_init_standalone(QUIC_RXFC *rxfc,
- uint64_t initial_window_size,
- OSSL_TIME (*now)(void *arg),
- void *now_arg)
- {
- if (!ossl_quic_rxfc_init(rxfc, NULL,
- initial_window_size, initial_window_size,
- now, now_arg))
- return 0;
- rxfc->standalone = 1;
- return 1;
- }
- QUIC_RXFC *ossl_quic_rxfc_get_parent(QUIC_RXFC *rxfc)
- {
- return rxfc->parent;
- }
- void ossl_quic_rxfc_set_max_window_size(QUIC_RXFC *rxfc,
- size_t max_window_size)
- {
- rxfc->max_window_size = max_window_size;
- }
- static void rxfc_start_epoch(QUIC_RXFC *rxfc)
- {
- rxfc->epoch_start = rxfc->now(rxfc->now_arg);
- rxfc->esrwm = rxfc->rwm;
- }
- static int on_rx_controlled_bytes(QUIC_RXFC *rxfc, uint64_t num_bytes)
- {
- int ok = 1;
- uint64_t credit = rxfc->cwm - rxfc->swm;
- if (num_bytes > credit) {
- ok = 0;
- num_bytes = credit;
- rxfc->error_code = QUIC_ERR_FLOW_CONTROL_ERROR;
- }
- rxfc->swm += num_bytes;
- return ok;
- }
- int ossl_quic_rxfc_on_rx_stream_frame(QUIC_RXFC *rxfc, uint64_t end, int is_fin)
- {
- uint64_t delta;
- if (!rxfc->standalone && rxfc->parent == NULL)
- return 0;
- if (rxfc->is_fin && ((is_fin && rxfc->hwm != end) || end > rxfc->hwm)) {
- /* Stream size cannot change after the stream is finished */
- rxfc->error_code = QUIC_ERR_FINAL_SIZE_ERROR;
- return 1; /* not a caller error */
- }
- if (is_fin)
- rxfc->is_fin = 1;
- if (end > rxfc->hwm) {
- delta = end - rxfc->hwm;
- rxfc->hwm = end;
- on_rx_controlled_bytes(rxfc, delta); /* result ignored */
- if (rxfc->parent != NULL)
- on_rx_controlled_bytes(rxfc->parent, delta); /* result ignored */
- } else if (end < rxfc->hwm && is_fin) {
- rxfc->error_code = QUIC_ERR_FINAL_SIZE_ERROR;
- return 1; /* not a caller error */
- }
- return 1;
- }
- /* threshold = 3/4 */
- #define WINDOW_THRESHOLD_NUM 3
- #define WINDOW_THRESHOLD_DEN 4
- static int rxfc_cwm_bump_desired(QUIC_RXFC *rxfc)
- {
- int err = 0;
- uint64_t window_rem = rxfc->cwm - rxfc->rwm;
- uint64_t threshold
- = safe_muldiv_uint64_t(rxfc->cur_window_size,
- WINDOW_THRESHOLD_NUM, WINDOW_THRESHOLD_DEN, &err);
- if (err)
- /*
- * Extremely large window should never occur, but if it does, just use
- * 1/2 as the threshold.
- */
- threshold = rxfc->cur_window_size / 2;
- /*
- * No point emitting a new MAX_STREAM_DATA frame if the stream has a final
- * size.
- */
- return !rxfc->is_fin && window_rem <= threshold;
- }
- static int rxfc_should_bump_window_size(QUIC_RXFC *rxfc, OSSL_TIME rtt)
- {
- /*
- * dt: time since start of epoch
- * b: bytes of window consumed since start of epoch
- * dw: proportion of window consumed since start of epoch
- * T_window: time it will take to use up the entire window, based on dt, dw
- * RTT: The current estimated RTT.
- *
- * b = rwm - esrwm
- * dw = b / window_size
- * T_window = dt / dw
- * T_window = dt / (b / window_size)
- * T_window = (dt * window_size) / b
- *
- * We bump the window size if T_window < 4 * RTT.
- *
- * We leave the division by b on the LHS to reduce the risk of overflowing
- * our 64-bit nanosecond representation, which will afford plenty of
- * precision left over after the division anyway.
- */
- uint64_t b = rxfc->rwm - rxfc->esrwm;
- OSSL_TIME now, dt, t_window;
- if (b == 0)
- return 0;
- now = rxfc->now(rxfc->now_arg);
- dt = ossl_time_subtract(now, rxfc->epoch_start);
- t_window = ossl_time_muldiv(dt, rxfc->cur_window_size, b);
- return ossl_time_compare(t_window, ossl_time_multiply(rtt, 4)) < 0;
- }
- static void rxfc_adjust_window_size(QUIC_RXFC *rxfc, uint64_t min_window_size,
- OSSL_TIME rtt)
- {
- /* Are we sending updates too often? */
- uint64_t new_window_size;
- new_window_size = rxfc->cur_window_size;
- if (rxfc_should_bump_window_size(rxfc, rtt))
- new_window_size *= 2;
- if (new_window_size < min_window_size)
- new_window_size = min_window_size;
- if (new_window_size > rxfc->max_window_size) /* takes precedence over min size */
- new_window_size = rxfc->max_window_size;
- rxfc->cur_window_size = new_window_size;
- rxfc_start_epoch(rxfc);
- }
- static void rxfc_update_cwm(QUIC_RXFC *rxfc, uint64_t min_window_size,
- OSSL_TIME rtt)
- {
- uint64_t new_cwm;
- if (!rxfc_cwm_bump_desired(rxfc))
- return;
- rxfc_adjust_window_size(rxfc, min_window_size, rtt);
- new_cwm = rxfc->rwm + rxfc->cur_window_size;
- if (new_cwm > rxfc->cwm) {
- rxfc->cwm = new_cwm;
- rxfc->has_cwm_changed = 1;
- }
- }
- static int rxfc_on_retire(QUIC_RXFC *rxfc, uint64_t num_bytes,
- uint64_t min_window_size,
- OSSL_TIME rtt)
- {
- if (ossl_time_is_zero(rxfc->epoch_start))
- /* This happens when we retire our first ever bytes. */
- rxfc_start_epoch(rxfc);
- rxfc->rwm += num_bytes;
- rxfc_update_cwm(rxfc, min_window_size, rtt);
- return 1;
- }
- int ossl_quic_rxfc_on_retire(QUIC_RXFC *rxfc,
- uint64_t num_bytes,
- OSSL_TIME rtt)
- {
- if (rxfc->parent == NULL && !rxfc->standalone)
- return 0;
- if (num_bytes == 0)
- return 1;
- if (rxfc->rwm + num_bytes > rxfc->swm)
- /* Impossible for us to retire more bytes than we have received. */
- return 0;
- rxfc_on_retire(rxfc, num_bytes, 0, rtt);
- if (!rxfc->standalone)
- rxfc_on_retire(rxfc->parent, num_bytes, rxfc->cur_window_size, rtt);
- return 1;
- }
- uint64_t ossl_quic_rxfc_get_cwm(QUIC_RXFC *rxfc)
- {
- return rxfc->cwm;
- }
- uint64_t ossl_quic_rxfc_get_swm(QUIC_RXFC *rxfc)
- {
- return rxfc->swm;
- }
- uint64_t ossl_quic_rxfc_get_rwm(QUIC_RXFC *rxfc)
- {
- return rxfc->rwm;
- }
- int ossl_quic_rxfc_has_cwm_changed(QUIC_RXFC *rxfc, int clear)
- {
- int r = rxfc->has_cwm_changed;
- if (clear)
- rxfc->has_cwm_changed = 0;
- return r;
- }
- int ossl_quic_rxfc_get_error(QUIC_RXFC *rxfc, int clear)
- {
- int r = rxfc->error_code;
- if (clear)
- rxfc->error_code = 0;
- return r;
- }
- int ossl_quic_rxfc_get_final_size(const QUIC_RXFC *rxfc, uint64_t *final_size)
- {
- if (!rxfc->is_fin)
- return 0;
- if (final_size != NULL)
- *final_size = rxfc->hwm;
- return 1;
- }
|