Home | History | Annotate | Line # | Download | only in quic
      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_txpim.h"
     11 #include <stdlib.h>
     12 
     13 typedef struct quic_txpim_pkt_ex_st QUIC_TXPIM_PKT_EX;
     14 
     15 struct quic_txpim_pkt_ex_st {
     16     QUIC_TXPIM_PKT public;
     17     QUIC_TXPIM_PKT_EX *prev, *next;
     18     QUIC_TXPIM_CHUNK *chunks;
     19     size_t num_chunks, alloc_chunks;
     20     unsigned int chunks_need_sort : 1;
     21 };
     22 
     23 typedef struct quic_txpim_pkt_ex_list {
     24     QUIC_TXPIM_PKT_EX *head, *tail;
     25 } QUIC_TXPIM_PKT_EX_LIST;
     26 
     27 struct quic_txpim_st {
     28     QUIC_TXPIM_PKT_EX_LIST free_list;
     29     size_t in_use;
     30 };
     31 
     32 #define MAX_ALLOC_CHUNKS 512
     33 
     34 QUIC_TXPIM *ossl_quic_txpim_new(void)
     35 {
     36     QUIC_TXPIM *txpim = OPENSSL_zalloc(sizeof(*txpim));
     37 
     38     if (txpim == NULL)
     39         return NULL;
     40 
     41     return txpim;
     42 }
     43 
     44 static void free_list(QUIC_TXPIM_PKT_EX_LIST *l)
     45 {
     46     QUIC_TXPIM_PKT_EX *n, *nnext;
     47 
     48     for (n = l->head; n != NULL; n = nnext) {
     49         nnext = n->next;
     50 
     51         OPENSSL_free(n->chunks);
     52         OPENSSL_free(n);
     53     }
     54 
     55     l->head = l->tail = NULL;
     56 }
     57 
     58 void ossl_quic_txpim_free(QUIC_TXPIM *txpim)
     59 {
     60     if (txpim == NULL)
     61         return;
     62 
     63     assert(txpim->in_use == 0);
     64     free_list(&txpim->free_list);
     65     OPENSSL_free(txpim);
     66 }
     67 
     68 static void list_remove(QUIC_TXPIM_PKT_EX_LIST *l, QUIC_TXPIM_PKT_EX *n)
     69 {
     70     if (l->head == n)
     71         l->head = n->next;
     72     if (l->tail == n)
     73         l->tail = n->prev;
     74     if (n->prev != NULL)
     75         n->prev->next = n->next;
     76     if (n->next != NULL)
     77         n->next->prev = n->prev;
     78     n->prev = n->next = NULL;
     79 }
     80 
     81 static void list_insert_tail(QUIC_TXPIM_PKT_EX_LIST *l, QUIC_TXPIM_PKT_EX *n)
     82 {
     83     n->prev = l->tail;
     84     n->next = NULL;
     85     l->tail = n;
     86     if (n->prev != NULL)
     87         n->prev->next = n;
     88     if (l->head == NULL)
     89         l->head = n;
     90 }
     91 
     92 static QUIC_TXPIM_PKT_EX *txpim_get_free(QUIC_TXPIM *txpim)
     93 {
     94     QUIC_TXPIM_PKT_EX *ex = txpim->free_list.head;
     95 
     96     if (ex != NULL)
     97         return ex;
     98 
     99     ex = OPENSSL_zalloc(sizeof(*ex));
    100     if (ex == NULL)
    101         return NULL;
    102 
    103     list_insert_tail(&txpim->free_list, ex);
    104     return ex;
    105 }
    106 
    107 static void txpim_clear(QUIC_TXPIM_PKT_EX *ex)
    108 {
    109     memset(&ex->public.ackm_pkt, 0, sizeof(ex->public.ackm_pkt));
    110     ossl_quic_txpim_pkt_clear_chunks(&ex->public);
    111     ex->public.retx_head = NULL;
    112     ex->public.fifd = NULL;
    113     ex->public.had_handshake_done_frame = 0;
    114     ex->public.had_max_data_frame = 0;
    115     ex->public.had_max_streams_bidi_frame = 0;
    116     ex->public.had_max_streams_uni_frame = 0;
    117     ex->public.had_ack_frame = 0;
    118     ex->public.had_conn_close = 0;
    119 }
    120 
    121 QUIC_TXPIM_PKT *ossl_quic_txpim_pkt_alloc(QUIC_TXPIM *txpim)
    122 {
    123     QUIC_TXPIM_PKT_EX *ex = txpim_get_free(txpim);
    124 
    125     if (ex == NULL)
    126         return NULL;
    127 
    128     txpim_clear(ex);
    129     list_remove(&txpim->free_list, ex);
    130     ++txpim->in_use;
    131     return &ex->public;
    132 }
    133 
    134 void ossl_quic_txpim_pkt_release(QUIC_TXPIM *txpim, QUIC_TXPIM_PKT *fpkt)
    135 {
    136     QUIC_TXPIM_PKT_EX *ex = (QUIC_TXPIM_PKT_EX *)fpkt;
    137 
    138     assert(txpim->in_use > 0);
    139     --txpim->in_use;
    140     list_insert_tail(&txpim->free_list, ex);
    141 }
    142 
    143 void ossl_quic_txpim_pkt_add_cfq_item(QUIC_TXPIM_PKT *fpkt,
    144     QUIC_CFQ_ITEM *item)
    145 {
    146     item->pkt_next = fpkt->retx_head;
    147     item->pkt_prev = NULL;
    148     fpkt->retx_head = item;
    149 }
    150 
    151 void ossl_quic_txpim_pkt_clear_chunks(QUIC_TXPIM_PKT *fpkt)
    152 {
    153     QUIC_TXPIM_PKT_EX *ex = (QUIC_TXPIM_PKT_EX *)fpkt;
    154 
    155     ex->num_chunks = 0;
    156 }
    157 
    158 int ossl_quic_txpim_pkt_append_chunk(QUIC_TXPIM_PKT *fpkt,
    159     const QUIC_TXPIM_CHUNK *chunk)
    160 {
    161     QUIC_TXPIM_PKT_EX *ex = (QUIC_TXPIM_PKT_EX *)fpkt;
    162     QUIC_TXPIM_CHUNK *new_chunk;
    163     size_t new_alloc_chunks = ex->alloc_chunks;
    164 
    165     if (ex->num_chunks == ex->alloc_chunks) {
    166         new_alloc_chunks = (ex->alloc_chunks == 0) ? 4 : ex->alloc_chunks * 8 / 5;
    167         if (new_alloc_chunks > MAX_ALLOC_CHUNKS)
    168             new_alloc_chunks = MAX_ALLOC_CHUNKS;
    169         if (ex->num_chunks == new_alloc_chunks)
    170             return 0;
    171 
    172         new_chunk = OPENSSL_realloc(ex->chunks,
    173             new_alloc_chunks * sizeof(QUIC_TXPIM_CHUNK));
    174         if (new_chunk == NULL)
    175             return 0;
    176 
    177         ex->chunks = new_chunk;
    178         ex->alloc_chunks = new_alloc_chunks;
    179     }
    180 
    181     ex->chunks[ex->num_chunks++] = *chunk;
    182     ex->chunks_need_sort = 1;
    183     return 1;
    184 }
    185 
    186 static int compare(const void *a, const void *b)
    187 {
    188     const QUIC_TXPIM_CHUNK *ac = a, *bc = b;
    189 
    190     if (ac->stream_id < bc->stream_id)
    191         return -1;
    192     else if (ac->stream_id > bc->stream_id)
    193         return 1;
    194 
    195     if (ac->start < bc->start)
    196         return -1;
    197     else if (ac->start > bc->start)
    198         return 1;
    199 
    200     return 0;
    201 }
    202 
    203 const QUIC_TXPIM_CHUNK *ossl_quic_txpim_pkt_get_chunks(const QUIC_TXPIM_PKT *fpkt)
    204 {
    205     QUIC_TXPIM_PKT_EX *ex = (QUIC_TXPIM_PKT_EX *)fpkt;
    206 
    207     if (ex->chunks_need_sort) {
    208         /*
    209          * List of chunks will generally be very small so there is no issue
    210          * simply sorting here.
    211          */
    212         qsort(ex->chunks, ex->num_chunks, sizeof(QUIC_TXPIM_CHUNK), compare);
    213         ex->chunks_need_sort = 0;
    214     }
    215 
    216     return ex->chunks;
    217 }
    218 
    219 size_t ossl_quic_txpim_pkt_get_num_chunks(const QUIC_TXPIM_PKT *fpkt)
    220 {
    221     QUIC_TXPIM_PKT_EX *ex = (QUIC_TXPIM_PKT_EX *)fpkt;
    222 
    223     return ex->num_chunks;
    224 }
    225 
    226 size_t ossl_quic_txpim_get_in_use(const QUIC_TXPIM *txpim)
    227 {
    228     return txpim->in_use;
    229 }
    230