diff options
Diffstat (limited to 'ssl/quic/quic_txpim.c')
-rw-r--r-- | ssl/quic/quic_txpim.c | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/ssl/quic/quic_txpim.c b/ssl/quic/quic_txpim.c new file mode 100644 index 000000000000..04b25ee47a04 --- /dev/null +++ b/ssl/quic/quic_txpim.c @@ -0,0 +1,229 @@ +/* + * 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_txpim.h" +#include <stdlib.h> + +typedef struct quic_txpim_pkt_ex_st QUIC_TXPIM_PKT_EX; + +struct quic_txpim_pkt_ex_st { + QUIC_TXPIM_PKT public; + QUIC_TXPIM_PKT_EX *prev, *next; + QUIC_TXPIM_CHUNK *chunks; + size_t num_chunks, alloc_chunks; + unsigned int chunks_need_sort : 1; +}; + +typedef struct quic_txpim_pkt_ex_list { + QUIC_TXPIM_PKT_EX *head, *tail; +} QUIC_TXPIM_PKT_EX_LIST; + +struct quic_txpim_st { + QUIC_TXPIM_PKT_EX_LIST free_list; + size_t in_use; +}; + +#define MAX_ALLOC_CHUNKS 512 + +QUIC_TXPIM *ossl_quic_txpim_new(void) +{ + QUIC_TXPIM *txpim = OPENSSL_zalloc(sizeof(*txpim)); + + if (txpim == NULL) + return NULL; + + return txpim; +} + +static void free_list(QUIC_TXPIM_PKT_EX_LIST *l) +{ + QUIC_TXPIM_PKT_EX *n, *nnext; + + for (n = l->head; n != NULL; n = nnext) { + nnext = n->next; + + OPENSSL_free(n->chunks); + OPENSSL_free(n); + } + + l->head = l->tail = NULL; +} + +void ossl_quic_txpim_free(QUIC_TXPIM *txpim) +{ + if (txpim == NULL) + return; + + assert(txpim->in_use == 0); + free_list(&txpim->free_list); + OPENSSL_free(txpim); +} + +static void list_remove(QUIC_TXPIM_PKT_EX_LIST *l, QUIC_TXPIM_PKT_EX *n) +{ + if (l->head == n) + l->head = n->next; + if (l->tail == n) + l->tail = n->prev; + if (n->prev != NULL) + n->prev->next = n->next; + if (n->next != NULL) + n->next->prev = n->prev; + n->prev = n->next = NULL; +} + +static void list_insert_tail(QUIC_TXPIM_PKT_EX_LIST *l, QUIC_TXPIM_PKT_EX *n) +{ + n->prev = l->tail; + n->next = NULL; + l->tail = n; + if (n->prev != NULL) + n->prev->next = n; + if (l->head == NULL) + l->head = n; +} + +static QUIC_TXPIM_PKT_EX *txpim_get_free(QUIC_TXPIM *txpim) +{ + QUIC_TXPIM_PKT_EX *ex = txpim->free_list.head; + + if (ex != NULL) + return ex; + + ex = OPENSSL_zalloc(sizeof(*ex)); + if (ex == NULL) + return NULL; + + list_insert_tail(&txpim->free_list, ex); + return ex; +} + +static void txpim_clear(QUIC_TXPIM_PKT_EX *ex) +{ + memset(&ex->public.ackm_pkt, 0, sizeof(ex->public.ackm_pkt)); + ossl_quic_txpim_pkt_clear_chunks(&ex->public); + ex->public.retx_head = NULL; + ex->public.fifd = NULL; + ex->public.had_handshake_done_frame = 0; + ex->public.had_max_data_frame = 0; + ex->public.had_max_streams_bidi_frame = 0; + ex->public.had_max_streams_uni_frame = 0; + ex->public.had_ack_frame = 0; + ex->public.had_conn_close = 0; +} + +QUIC_TXPIM_PKT *ossl_quic_txpim_pkt_alloc(QUIC_TXPIM *txpim) +{ + QUIC_TXPIM_PKT_EX *ex = txpim_get_free(txpim); + + if (ex == NULL) + return NULL; + + txpim_clear(ex); + list_remove(&txpim->free_list, ex); + ++txpim->in_use; + return &ex->public; +} + +void ossl_quic_txpim_pkt_release(QUIC_TXPIM *txpim, QUIC_TXPIM_PKT *fpkt) +{ + QUIC_TXPIM_PKT_EX *ex = (QUIC_TXPIM_PKT_EX *)fpkt; + + assert(txpim->in_use > 0); + --txpim->in_use; + list_insert_tail(&txpim->free_list, ex); +} + +void ossl_quic_txpim_pkt_add_cfq_item(QUIC_TXPIM_PKT *fpkt, + QUIC_CFQ_ITEM *item) +{ + item->pkt_next = fpkt->retx_head; + item->pkt_prev = NULL; + fpkt->retx_head = item; +} + +void ossl_quic_txpim_pkt_clear_chunks(QUIC_TXPIM_PKT *fpkt) +{ + QUIC_TXPIM_PKT_EX *ex = (QUIC_TXPIM_PKT_EX *)fpkt; + + ex->num_chunks = 0; +} + +int ossl_quic_txpim_pkt_append_chunk(QUIC_TXPIM_PKT *fpkt, + const QUIC_TXPIM_CHUNK *chunk) +{ + QUIC_TXPIM_PKT_EX *ex = (QUIC_TXPIM_PKT_EX *)fpkt; + QUIC_TXPIM_CHUNK *new_chunk; + size_t new_alloc_chunks = ex->alloc_chunks; + + if (ex->num_chunks == ex->alloc_chunks) { + new_alloc_chunks = (ex->alloc_chunks == 0) ? 4 : ex->alloc_chunks * 8 / 5; + if (new_alloc_chunks > MAX_ALLOC_CHUNKS) + new_alloc_chunks = MAX_ALLOC_CHUNKS; + if (ex->num_chunks == new_alloc_chunks) + return 0; + + new_chunk = OPENSSL_realloc(ex->chunks, + new_alloc_chunks * sizeof(QUIC_TXPIM_CHUNK)); + if (new_chunk == NULL) + return 0; + + ex->chunks = new_chunk; + ex->alloc_chunks = new_alloc_chunks; + } + + ex->chunks[ex->num_chunks++] = *chunk; + ex->chunks_need_sort = 1; + return 1; +} + +static int compare(const void *a, const void *b) +{ + const QUIC_TXPIM_CHUNK *ac = a, *bc = b; + + if (ac->stream_id < bc->stream_id) + return -1; + else if (ac->stream_id > bc->stream_id) + return 1; + + if (ac->start < bc->start) + return -1; + else if (ac->start > bc->start) + return 1; + + return 0; +} + +const QUIC_TXPIM_CHUNK *ossl_quic_txpim_pkt_get_chunks(const QUIC_TXPIM_PKT *fpkt) +{ + QUIC_TXPIM_PKT_EX *ex = (QUIC_TXPIM_PKT_EX *)fpkt; + + if (ex->chunks_need_sort) { + /* + * List of chunks will generally be very small so there is no issue + * simply sorting here. + */ + qsort(ex->chunks, ex->num_chunks, sizeof(QUIC_TXPIM_CHUNK), compare); + ex->chunks_need_sort = 0; + } + + return ex->chunks; +} + +size_t ossl_quic_txpim_pkt_get_num_chunks(const QUIC_TXPIM_PKT *fpkt) +{ + QUIC_TXPIM_PKT_EX *ex = (QUIC_TXPIM_PKT_EX *)fpkt; + + return ex->num_chunks; +} + +size_t ossl_quic_txpim_get_in_use(const QUIC_TXPIM *txpim) +{ + return txpim->in_use; +} |