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
ossl_quic_txpim_new(void)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
free_list(QUIC_TXPIM_PKT_EX_LIST * l)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
ossl_quic_txpim_free(QUIC_TXPIM * txpim)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
list_remove(QUIC_TXPIM_PKT_EX_LIST * l,QUIC_TXPIM_PKT_EX * n)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
list_insert_tail(QUIC_TXPIM_PKT_EX_LIST * l,QUIC_TXPIM_PKT_EX * n)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
txpim_get_free(QUIC_TXPIM * txpim)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
txpim_clear(QUIC_TXPIM_PKT_EX * ex)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
ossl_quic_txpim_pkt_alloc(QUIC_TXPIM * txpim)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
ossl_quic_txpim_pkt_release(QUIC_TXPIM * txpim,QUIC_TXPIM_PKT * fpkt)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
ossl_quic_txpim_pkt_add_cfq_item(QUIC_TXPIM_PKT * fpkt,QUIC_CFQ_ITEM * item)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
ossl_quic_txpim_pkt_clear_chunks(QUIC_TXPIM_PKT * fpkt)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
ossl_quic_txpim_pkt_append_chunk(QUIC_TXPIM_PKT * fpkt,const QUIC_TXPIM_CHUNK * chunk)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
compare(const void * a,const void * b)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
ossl_quic_txpim_pkt_get_chunks(const QUIC_TXPIM_PKT * fpkt)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
ossl_quic_txpim_pkt_get_num_chunks(const QUIC_TXPIM_PKT * fpkt)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
ossl_quic_txpim_get_in_use(const QUIC_TXPIM * txpim)226 size_t ossl_quic_txpim_get_in_use(const QUIC_TXPIM *txpim)
227 {
228 return txpim->in_use;
229 }
230