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