1 /* 2 * Copyright 2022-2026 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_channel.h" 11 #include "internal/quic_cfq.h" 12 #include "internal/numbers.h" 13 14 typedef struct quic_cfq_item_ex_st QUIC_CFQ_ITEM_EX; 15 16 struct quic_cfq_item_ex_st { 17 QUIC_CFQ_ITEM public; 18 QUIC_CFQ_ITEM_EX *prev, *next; 19 unsigned char *encoded; 20 cfq_free_cb *free_cb; 21 void *free_cb_arg; 22 uint64_t frame_type; 23 size_t encoded_len; 24 uint32_t priority, pn_space, flags; 25 int state; 26 }; 27 28 uint64_t ossl_quic_cfq_item_get_frame_type(const QUIC_CFQ_ITEM *item) 29 { 30 QUIC_CFQ_ITEM_EX *ex = (QUIC_CFQ_ITEM_EX *)item; 31 32 return ex->frame_type; 33 } 34 35 const unsigned char *ossl_quic_cfq_item_get_encoded(const QUIC_CFQ_ITEM *item) 36 { 37 QUIC_CFQ_ITEM_EX *ex = (QUIC_CFQ_ITEM_EX *)item; 38 39 return ex->encoded; 40 } 41 42 size_t ossl_quic_cfq_item_get_encoded_len(const QUIC_CFQ_ITEM *item) 43 { 44 QUIC_CFQ_ITEM_EX *ex = (QUIC_CFQ_ITEM_EX *)item; 45 46 return ex->encoded_len; 47 } 48 49 int ossl_quic_cfq_item_get_state(const QUIC_CFQ_ITEM *item) 50 { 51 QUIC_CFQ_ITEM_EX *ex = (QUIC_CFQ_ITEM_EX *)item; 52 53 return ex->state; 54 } 55 56 uint32_t ossl_quic_cfq_item_get_pn_space(const QUIC_CFQ_ITEM *item) 57 { 58 QUIC_CFQ_ITEM_EX *ex = (QUIC_CFQ_ITEM_EX *)item; 59 60 return ex->pn_space; 61 } 62 63 int ossl_quic_cfq_item_is_unreliable(const QUIC_CFQ_ITEM *item) 64 { 65 QUIC_CFQ_ITEM_EX *ex = (QUIC_CFQ_ITEM_EX *)item; 66 67 return (ex->flags & QUIC_CFQ_ITEM_FLAG_UNRELIABLE) != 0; 68 } 69 70 typedef struct quic_cfq_item_list_st { 71 QUIC_CFQ_ITEM_EX *head, *tail; 72 } QUIC_CFQ_ITEM_LIST; 73 74 struct quic_cfq_st { 75 /* 76 * Invariant: A CFQ item is always in exactly one of these lists, never more 77 * or less than one. 78 * 79 * Invariant: The list the CFQ item is determined exactly by the state field 80 * of the item. 81 */ 82 QUIC_CFQ_ITEM_LIST new_list, tx_list, free_list; 83 }; 84 85 static int compare(const QUIC_CFQ_ITEM_EX *a, const QUIC_CFQ_ITEM_EX *b) 86 { 87 if (a->pn_space < b->pn_space) 88 return -1; 89 else if (a->pn_space > b->pn_space) 90 return 1; 91 92 if (a->priority > b->priority) 93 return -1; 94 else if (a->priority < b->priority) 95 return 1; 96 97 return 0; 98 } 99 100 static void list_remove(QUIC_CFQ_ITEM_LIST *l, QUIC_CFQ_ITEM_EX *n) 101 { 102 if (l->head == n) 103 l->head = n->next; 104 if (l->tail == n) 105 l->tail = n->prev; 106 if (n->prev != NULL) 107 n->prev->next = n->next; 108 if (n->next != NULL) 109 n->next->prev = n->prev; 110 n->prev = n->next = NULL; 111 } 112 113 static void list_insert_head(QUIC_CFQ_ITEM_LIST *l, QUIC_CFQ_ITEM_EX *n) 114 { 115 n->next = l->head; 116 n->prev = NULL; 117 l->head = n; 118 if (n->next != NULL) 119 n->next->prev = n; 120 if (l->tail == NULL) 121 l->tail = n; 122 } 123 124 static void list_insert_tail(QUIC_CFQ_ITEM_LIST *l, QUIC_CFQ_ITEM_EX *n) 125 { 126 n->prev = l->tail; 127 n->next = NULL; 128 l->tail = n; 129 if (n->prev != NULL) 130 n->prev->next = n; 131 if (l->head == NULL) 132 l->head = n; 133 } 134 135 static void list_insert_after(QUIC_CFQ_ITEM_LIST *l, 136 QUIC_CFQ_ITEM_EX *ref, 137 QUIC_CFQ_ITEM_EX *n) 138 { 139 n->prev = ref; 140 n->next = ref->next; 141 if (ref->next != NULL) 142 ref->next->prev = n; 143 ref->next = n; 144 if (l->tail == ref) 145 l->tail = n; 146 } 147 148 static void list_insert_sorted(QUIC_CFQ_ITEM_LIST *l, QUIC_CFQ_ITEM_EX *n, 149 int (*cmp)(const QUIC_CFQ_ITEM_EX *a, 150 const QUIC_CFQ_ITEM_EX *b)) 151 { 152 QUIC_CFQ_ITEM_EX *p = l->head, *pprev = NULL; 153 154 if (p == NULL) { 155 l->head = l->tail = n; 156 n->prev = n->next = NULL; 157 return; 158 } 159 160 for (; p != NULL && cmp(p, n) < 0; pprev = p, p = p->next) 161 ; 162 163 if (p == NULL) 164 list_insert_tail(l, n); 165 else if (pprev == NULL) 166 list_insert_head(l, n); 167 else 168 list_insert_after(l, pprev, n); 169 } 170 171 QUIC_CFQ *ossl_quic_cfq_new(void) 172 { 173 QUIC_CFQ *cfq = OPENSSL_zalloc(sizeof(*cfq)); 174 175 if (cfq == NULL) 176 return NULL; 177 178 return cfq; 179 } 180 181 static void clear_item(QUIC_CFQ_ITEM_EX *item) 182 { 183 if (item->free_cb != NULL) { 184 item->free_cb(item->encoded, item->encoded_len, item->free_cb_arg); 185 186 item->free_cb = NULL; 187 item->encoded = NULL; 188 item->encoded_len = 0; 189 } 190 191 item->state = -1; 192 } 193 194 static void free_list_items(QUIC_CFQ_ITEM_LIST *l) 195 { 196 QUIC_CFQ_ITEM_EX *p, *pnext; 197 198 for (p = l->head; p != NULL; p = pnext) { 199 pnext = p->next; 200 clear_item(p); 201 OPENSSL_free(p); 202 } 203 } 204 205 void ossl_quic_cfq_free(QUIC_CFQ *cfq) 206 { 207 if (cfq == NULL) 208 return; 209 210 free_list_items(&cfq->new_list); 211 free_list_items(&cfq->tx_list); 212 free_list_items(&cfq->free_list); 213 OPENSSL_free(cfq); 214 } 215 216 static QUIC_CFQ_ITEM_EX *cfq_get_free(QUIC_CFQ *cfq) 217 { 218 QUIC_CFQ_ITEM_EX *item = cfq->free_list.head; 219 220 if (item != NULL) 221 return item; 222 223 item = OPENSSL_zalloc(sizeof(*item)); 224 if (item == NULL) 225 return NULL; 226 227 item->state = -1; 228 list_insert_tail(&cfq->free_list, item); 229 return item; 230 } 231 232 QUIC_CFQ_ITEM *ossl_quic_cfq_add_frame(QUIC_CFQ *cfq, 233 uint32_t priority, 234 uint32_t pn_space, 235 uint64_t frame_type, 236 uint32_t flags, 237 const unsigned char *encoded, 238 size_t encoded_len, 239 cfq_free_cb *free_cb, 240 void *free_cb_arg) 241 { 242 QUIC_CFQ_ITEM_EX *item = cfq_get_free(cfq); 243 244 if (item == NULL) 245 return NULL; 246 247 item->priority = priority; 248 item->frame_type = frame_type; 249 item->pn_space = pn_space; 250 item->encoded = (unsigned char *)encoded; 251 item->encoded_len = encoded_len; 252 item->free_cb = free_cb; 253 item->free_cb_arg = free_cb_arg; 254 255 item->state = QUIC_CFQ_STATE_NEW; 256 item->flags = flags; 257 list_remove(&cfq->free_list, item); 258 list_insert_sorted(&cfq->new_list, item, compare); 259 return &item->public; 260 } 261 262 void ossl_quic_cfq_mark_tx(QUIC_CFQ *cfq, QUIC_CFQ_ITEM *item) 263 { 264 QUIC_CFQ_ITEM_EX *ex = (QUIC_CFQ_ITEM_EX *)item; 265 266 switch (ex->state) { 267 case QUIC_CFQ_STATE_NEW: 268 list_remove(&cfq->new_list, ex); 269 list_insert_tail(&cfq->tx_list, ex); 270 ex->state = QUIC_CFQ_STATE_TX; 271 break; 272 case QUIC_CFQ_STATE_TX: 273 break; /* nothing to do */ 274 default: 275 assert(0); /* invalid state (e.g. in free state) */ 276 break; 277 } 278 } 279 280 void ossl_quic_cfq_mark_lost(QUIC_CFQ *cfq, QUIC_CFQ_ITEM *item, 281 uint32_t priority) 282 { 283 QUIC_CFQ_ITEM_EX *ex = (QUIC_CFQ_ITEM_EX *)item; 284 285 if (ossl_quic_cfq_item_is_unreliable(item)) { 286 ossl_quic_cfq_release(cfq, item); 287 return; 288 } 289 290 switch (ex->state) { 291 case QUIC_CFQ_STATE_NEW: 292 if (priority != UINT32_MAX && priority != ex->priority) { 293 list_remove(&cfq->new_list, ex); 294 ex->priority = priority; 295 list_insert_sorted(&cfq->new_list, ex, compare); 296 } 297 break; /* nothing to do */ 298 case QUIC_CFQ_STATE_TX: 299 if (priority != UINT32_MAX) 300 ex->priority = priority; 301 list_remove(&cfq->tx_list, ex); 302 list_insert_sorted(&cfq->new_list, ex, compare); 303 ex->state = QUIC_CFQ_STATE_NEW; 304 break; 305 default: 306 assert(0); /* invalid state (e.g. in free state) */ 307 break; 308 } 309 } 310 311 int ossl_quic_cfq_discard_unreliable(QUIC_CFQ *cfq, QUIC_CFQ_ITEM *item) 312 { 313 int discarded; 314 315 if (ossl_quic_cfq_item_is_unreliable(item)) { 316 ossl_quic_cfq_release(cfq, item); 317 discarded = 1; 318 } else { 319 discarded = 0; 320 } 321 322 return discarded; 323 } 324 325 /* 326 * Releases a CFQ item. The item may be in either state (NEW or TX) prior to the 327 * call. The QUIC_CFQ_ITEM pointer must not be used following this call. 328 */ 329 void ossl_quic_cfq_release(QUIC_CFQ *cfq, QUIC_CFQ_ITEM *item) 330 { 331 QUIC_CFQ_ITEM_EX *ex = (QUIC_CFQ_ITEM_EX *)item; 332 333 switch (ex->state) { 334 case QUIC_CFQ_STATE_NEW: 335 list_remove(&cfq->new_list, ex); 336 list_insert_tail(&cfq->free_list, ex); 337 clear_item(ex); 338 break; 339 case QUIC_CFQ_STATE_TX: 340 list_remove(&cfq->tx_list, ex); 341 list_insert_tail(&cfq->free_list, ex); 342 clear_item(ex); 343 break; 344 default: 345 assert(0); /* invalid state (e.g. in free state) */ 346 break; 347 } 348 } 349 350 QUIC_CFQ_ITEM *ossl_quic_cfq_get_priority_head(const QUIC_CFQ *cfq, 351 uint32_t pn_space) 352 { 353 QUIC_CFQ_ITEM_EX *item = cfq->new_list.head; 354 355 for (; item != NULL && item->pn_space != pn_space; item = item->next) 356 ; 357 358 if (item == NULL) 359 return NULL; 360 361 return &item->public; 362 } 363 364 QUIC_CFQ_ITEM *ossl_quic_cfq_item_get_priority_next(const QUIC_CFQ_ITEM *item, 365 uint32_t pn_space) 366 { 367 QUIC_CFQ_ITEM_EX *ex = (QUIC_CFQ_ITEM_EX *)item; 368 369 if (ex == NULL) 370 return NULL; 371 372 ex = ex->next; 373 374 for (; ex != NULL && ex->pn_space != pn_space; ex = ex->next) 375 ; 376 377 if (ex == NULL) 378 return NULL; /* ubsan */ 379 380 return &ex->public; 381 } 382