xref: /openssl/ssl/quic/quic_txpim.c (revision da1c088f)
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