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