1 /* 2 * Copyright 2023-2024 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_rcidm.h" 11 #include "internal/priority_queue.h" 12 #include "internal/list.h" 13 #include "internal/common.h" 14 15 /* 16 * QUIC Remote Connection ID Manager 17 * ================================= 18 * 19 * We can receive an arbitrary number of RCIDs via NCID frames. Periodically, we 20 * may desire (for example for anti-connection fingerprinting reasons, etc.) 21 * to switch to a new RCID according to some arbitrary policy such as the number 22 * of packets we have sent. 23 * 24 * When we do this we should move to the next RCID in the sequence of received 25 * RCIDs ordered by sequence number. For example, if a peer sends us three NCID 26 * frames with sequence numbers 10, 11, 12, we should seek to consume these 27 * RCIDs in order. 28 * 29 * However, due to the possibility of packet reordering in the network, NCID 30 * frames might be received out of order. Thus if a peer sends us NCID frames 31 * with sequence numbers 12, 10, 11, we should still consume the RCID with 32 * sequence number 10 before consuming the RCIDs with sequence numbers 11 or 12. 33 * 34 * We use a priority queue for this purpose. 35 */ 36 static void rcidm_update(QUIC_RCIDM *rcidm); 37 static void rcidm_set_preferred_rcid(QUIC_RCIDM *rcidm, 38 const QUIC_CONN_ID *rcid); 39 40 #define PACKETS_PER_RCID 10000 41 42 #define INITIAL_SEQ_NUM 0 43 #define PREF_ADDR_SEQ_NUM 1 44 45 /* 46 * RCID 47 * ==== 48 * 49 * The RCID structure is used to track RCIDs which have sequence numbers (i.e., 50 * INITIAL, PREF_ADDR and NCID type RCIDs). The RCIDs without sequence numbers 51 * (Initial ODCIDs and Retry ODCIDs), hereafter referred to as unnumbered RCIDs, 52 * can logically be viewed as their own type of RCID but are tracked separately 53 * as singletons without needing a discrete structure. 54 * 55 * At any given time an RCID object is in one of these states: 56 * 57 * 58 * (start) 59 * | 60 * [add] 61 * | 62 * _____v_____ ___________ ____________ 63 * | | | | | | 64 * | PENDING | --[select]--> | CURRENT | --[retire]--> | RETIRING | 65 * |___________| |___________| |____________| 66 * | 67 * [pop] 68 * | 69 * v 70 * (fin) 71 * 72 * The transition through the states is monotonic and irreversible. 73 * The RCID object is freed when it is popped. 74 * 75 * PENDING 76 * Invariants: 77 * rcid->state == RCID_STATE_PENDING; 78 * rcid->pq_idx != SIZE_MAX (debug assert only); 79 * the RCID is not the current RCID, rcidm->cur_rcid != rcid; 80 * the RCID is in the priority queue; 81 * the RCID is not in the retiring_list. 82 * 83 * CURRENT 84 * Invariants: 85 * rcid->state == RCID_STATE_CUR; 86 * rcid->pq_idx == SIZE_MAX (debug assert only); 87 * the RCID is the current RCID, rcidm->cur_rcid == rcid; 88 * the RCID is not in the priority queue; 89 * the RCID is not in the retiring_list. 90 * 91 * RETIRING 92 * Invariants: 93 * rcid->state == RCID_STATE_RETIRING; 94 * rcid->pq_idx == SIZE_MAX (debug assert only); 95 * the RCID is not the current RCID, rcidm->cur_rcid != rcid; 96 * the RCID is not in the priority queue; 97 * the RCID is in the retiring_list. 98 * 99 * Invariant: At most one RCID object is in the CURRENT state at any one time. 100 * 101 * (If no RCID object is in the CURRENT state, this means either 102 * an unnumbered RCID is being used as the preferred RCID 103 * or we currently have no preferred RCID.) 104 * 105 * All of the above states can be considered substates of the 'ACTIVE' state 106 * for an RCID as specified in RFC 9000. A CID only ceases to be active 107 * when we send a RETIRE_CONN_ID frame, which is the responsibility of the 108 * user of the RCIDM and happens after the above state machine is terminated. 109 */ 110 enum { 111 RCID_STATE_PENDING, 112 RCID_STATE_CUR, 113 RCID_STATE_RETIRING 114 }; 115 116 enum { 117 RCID_TYPE_INITIAL, /* CID is from an peer INITIAL packet (seq 0) */ 118 RCID_TYPE_PREF_ADDR, /* CID is from a preferred_address TPARAM (seq 1) */ 119 RCID_TYPE_NCID /* CID is from a NCID frame */ 120 /* 121 * INITIAL_ODCID and RETRY_ODCID also conceptually exist but are tracked 122 * separately. 123 */ 124 }; 125 126 typedef struct rcid_st { 127 OSSL_LIST_MEMBER(retiring, struct rcid_st); /* valid iff RETIRING */ 128 129 QUIC_CONN_ID cid; /* The actual CID string for this RCID */ 130 uint64_t seq_num; 131 size_t pq_idx; /* Index of entry into priority queue */ 132 unsigned int state : 2; /* RCID_STATE_* */ 133 unsigned int type : 2; /* RCID_TYPE_* */ 134 } RCID; 135 136 DEFINE_PRIORITY_QUEUE_OF(RCID); 137 DEFINE_LIST_OF(retiring, RCID); 138 139 /* 140 * RCID Manager 141 * ============ 142 * 143 * The following "business logic" invariants also apply to the RCIDM 144 * as a whole: 145 * 146 * Invariant: An RCID of INITIAL type has a sequence number of 0. 147 * Invariant: An RCID of PREF_ADDR type has a sequence number of 1. 148 * 149 * Invariant: There is never more than one Initial ODCID 150 * added throughout the lifetime of an RCIDM. 151 * Invariant: There is never more than one Retry ODCID 152 * added throughout the lifetime of an RCIDM. 153 * Invariant: There is never more than one INITIAL RCID created 154 * throughout the lifetime of an RCIDM. 155 * Invariant: There is never more than one PREF_ADDR RCID created 156 * throughout the lifetime of an RCIDM. 157 * Invariant: No INITIAL or PREF_ADDR RCID may be added after 158 * the handshake is completed. 159 * 160 */ 161 struct quic_rcidm_st { 162 /* 163 * The current RCID we prefer to use (value undefined if 164 * !have_preferred_rcid). 165 * 166 * This is preferentially set to a numbered RCID (represented by an RCID 167 * object) if we have one (in which case preferred_rcid == cur_rcid->cid); 168 * otherwise it is set to one of the unnumbered RCIDs (the Initial ODCID or 169 * Retry ODCID) if available (and cur_rcid == NULL). 170 */ 171 QUIC_CONN_ID preferred_rcid; 172 173 /* 174 * These are initialized if the corresponding added_ flags are set. 175 */ 176 QUIC_CONN_ID initial_odcid, retry_odcid; 177 178 /* 179 * Total number of packets sent since we last made a packet count-based RCID 180 * update decision. 181 */ 182 uint64_t packets_sent; 183 184 /* Number of post-handshake RCID changes we have performed. */ 185 uint64_t num_changes; 186 187 /* 188 * The Retire Prior To watermark value; max(retire_prior_to) of all received 189 * NCID frames. 190 */ 191 uint64_t retire_prior_to; 192 193 /* (SORT BY seq_num ASC) -> (RCID *) */ 194 PRIORITY_QUEUE_OF(RCID) *rcids; 195 196 /* 197 * Current RCID object we are using. This may differ from the first item in 198 * the priority queue if we received NCID frames out of order. For example 199 * if we get seq 5, switch to it immediately, then get seq 4, we want to 200 * keep using seq 5 until we decide to roll again rather than immediately 201 * switch to seq 4. Never points to an object on the retiring_list. 202 */ 203 RCID *cur_rcid; 204 205 /* 206 * When a RCID becomes pending-retirement, it is moved to the retiring_list, 207 * then freed when it is popped from the retired queue. We use a list for 208 * this rather than a priority queue as the order in which items are freed 209 * does not matter. We always append to the tail of the list in order to 210 * maintain the guarantee that the head (if present) only changes when a 211 * caller calls pop(). 212 */ 213 OSSL_LIST(retiring) retiring_list; 214 215 /* Number of entries on the retiring_list. */ 216 size_t num_retiring; 217 218 /* preferred_rcid has been changed? */ 219 unsigned int preferred_rcid_changed : 1; 220 221 /* Do we have any RCID we can use currently? */ 222 unsigned int have_preferred_rcid : 1; 223 224 /* QUIC handshake has been completed? */ 225 unsigned int handshake_complete : 1; 226 227 /* odcid was set (not necessarily still valid as a RCID)? */ 228 unsigned int added_initial_odcid : 1; 229 /* retry_odcid was set (not necessarily still valid as a RCID?) */ 230 unsigned int added_retry_odcid : 1; 231 /* An initial RCID was added as an RCID structure? */ 232 unsigned int added_initial_rcid : 1; 233 /* Has a RCID roll been manually requested? */ 234 unsigned int roll_requested : 1; 235 }; 236 237 /* 238 * Caller must periodically pop retired RCIDs and handle them. If the caller 239 * fails to do so, fail safely rather than start exhibiting integer rollover. 240 * Limit the total number of numbered RCIDs to an implausibly large but safe 241 * value. 242 */ 243 #define MAX_NUMBERED_RCIDS (SIZE_MAX / 2) 244 245 static void rcidm_transition_rcid(QUIC_RCIDM *rcidm, RCID *rcid, 246 unsigned int state); 247 248 /* Check invariants of an RCID */ 249 static void rcidm_check_rcid(QUIC_RCIDM *rcidm, RCID *rcid) 250 { 251 assert(rcid->state == RCID_STATE_PENDING 252 || rcid->state == RCID_STATE_CUR 253 || rcid->state == RCID_STATE_RETIRING); 254 assert((rcid->state == RCID_STATE_PENDING) 255 == (rcid->pq_idx != SIZE_MAX)); 256 assert((rcid->state == RCID_STATE_CUR) 257 == (rcidm->cur_rcid == rcid)); 258 assert((ossl_list_retiring_next(rcid) != NULL 259 || ossl_list_retiring_prev(rcid) != NULL 260 || ossl_list_retiring_head(&rcidm->retiring_list) == rcid) 261 == (rcid->state == RCID_STATE_RETIRING)); 262 assert(rcid->type != RCID_TYPE_INITIAL || rcid->seq_num == 0); 263 assert(rcid->type != RCID_TYPE_PREF_ADDR || rcid->seq_num == 1); 264 assert(rcid->seq_num <= OSSL_QUIC_VLINT_MAX); 265 assert(rcid->cid.id_len > 0 && rcid->cid.id_len <= QUIC_MAX_CONN_ID_LEN); 266 assert(rcid->seq_num >= rcidm->retire_prior_to 267 || rcid->state == RCID_STATE_RETIRING); 268 assert(rcidm->num_changes == 0 || rcidm->handshake_complete); 269 assert(rcid->state != RCID_STATE_RETIRING || rcidm->num_retiring > 0); 270 } 271 272 static int rcid_cmp(const RCID *a, const RCID *b) 273 { 274 if (a->seq_num < b->seq_num) 275 return -1; 276 if (a->seq_num > b->seq_num) 277 return 1; 278 return 0; 279 } 280 281 QUIC_RCIDM *ossl_quic_rcidm_new(const QUIC_CONN_ID *initial_odcid) 282 { 283 QUIC_RCIDM *rcidm; 284 285 if ((rcidm = OPENSSL_zalloc(sizeof(*rcidm))) == NULL) 286 return NULL; 287 288 if ((rcidm->rcids = ossl_pqueue_RCID_new(rcid_cmp)) == NULL) { 289 OPENSSL_free(rcidm); 290 return NULL; 291 } 292 293 if (initial_odcid != NULL) { 294 rcidm->initial_odcid = *initial_odcid; 295 rcidm->added_initial_odcid = 1; 296 } 297 298 rcidm_update(rcidm); 299 return rcidm; 300 } 301 302 void ossl_quic_rcidm_free(QUIC_RCIDM *rcidm) 303 { 304 RCID *rcid, *rnext; 305 306 if (rcidm == NULL) 307 return; 308 309 OPENSSL_free(rcidm->cur_rcid); 310 while ((rcid = ossl_pqueue_RCID_pop(rcidm->rcids)) != NULL) 311 OPENSSL_free(rcid); 312 313 OSSL_LIST_FOREACH_DELSAFE(rcid, rnext, retiring, &rcidm->retiring_list) 314 OPENSSL_free(rcid); 315 316 ossl_pqueue_RCID_free(rcidm->rcids); 317 OPENSSL_free(rcidm); 318 } 319 320 static void rcidm_set_preferred_rcid(QUIC_RCIDM *rcidm, 321 const QUIC_CONN_ID *rcid) 322 { 323 if (rcid == NULL) { 324 rcidm->preferred_rcid_changed = 1; 325 rcidm->have_preferred_rcid = 0; 326 return; 327 } 328 329 if (ossl_quic_conn_id_eq(&rcidm->preferred_rcid, rcid)) 330 return; 331 332 rcidm->preferred_rcid = *rcid; 333 rcidm->preferred_rcid_changed = 1; 334 rcidm->have_preferred_rcid = 1; 335 } 336 337 /* 338 * RCID Lifecycle Management 339 * ========================= 340 */ 341 static RCID *rcidm_create_rcid(QUIC_RCIDM *rcidm, uint64_t seq_num, 342 const QUIC_CONN_ID *cid, 343 unsigned int type) 344 { 345 RCID *rcid; 346 347 if (cid->id_len < 1 || cid->id_len > QUIC_MAX_CONN_ID_LEN 348 || seq_num > OSSL_QUIC_VLINT_MAX 349 || ossl_pqueue_RCID_num(rcidm->rcids) + rcidm->num_retiring 350 > MAX_NUMBERED_RCIDS) 351 return NULL; 352 353 if ((rcid = OPENSSL_zalloc(sizeof(*rcid))) == NULL) 354 return NULL; 355 356 rcid->seq_num = seq_num; 357 rcid->cid = *cid; 358 rcid->type = type; 359 360 if (rcid->seq_num >= rcidm->retire_prior_to) { 361 rcid->state = RCID_STATE_PENDING; 362 363 if (!ossl_pqueue_RCID_push(rcidm->rcids, rcid, &rcid->pq_idx)) { 364 OPENSSL_free(rcid); 365 return NULL; 366 } 367 } else { 368 /* RCID is immediately retired upon creation. */ 369 rcid->state = RCID_STATE_RETIRING; 370 rcid->pq_idx = SIZE_MAX; 371 ossl_list_retiring_insert_tail(&rcidm->retiring_list, rcid); 372 ++rcidm->num_retiring; 373 } 374 375 rcidm_check_rcid(rcidm, rcid); 376 return rcid; 377 } 378 379 static void rcidm_transition_rcid(QUIC_RCIDM *rcidm, RCID *rcid, 380 unsigned int state) 381 { 382 unsigned int old_state = rcid->state; 383 384 assert(state >= old_state && state <= RCID_STATE_RETIRING); 385 rcidm_check_rcid(rcidm, rcid); 386 if (state == old_state) 387 return; 388 389 if (rcidm->cur_rcid != NULL && state == RCID_STATE_CUR) { 390 rcidm_transition_rcid(rcidm, rcidm->cur_rcid, RCID_STATE_RETIRING); 391 assert(rcidm->cur_rcid == NULL); 392 } 393 394 if (old_state == RCID_STATE_PENDING) { 395 ossl_pqueue_RCID_remove(rcidm->rcids, rcid->pq_idx); 396 rcid->pq_idx = SIZE_MAX; 397 } 398 399 rcid->state = state; 400 401 if (state == RCID_STATE_CUR) { 402 rcidm->cur_rcid = rcid; 403 } else if (state == RCID_STATE_RETIRING) { 404 if (old_state == RCID_STATE_CUR) 405 rcidm->cur_rcid = NULL; 406 407 ossl_list_retiring_insert_tail(&rcidm->retiring_list, rcid); 408 ++rcidm->num_retiring; 409 } 410 411 rcidm_check_rcid(rcidm, rcid); 412 } 413 414 static void rcidm_free_rcid(QUIC_RCIDM *rcidm, RCID *rcid) 415 { 416 if (rcid == NULL) 417 return; 418 419 rcidm_check_rcid(rcidm, rcid); 420 421 switch (rcid->state) { 422 case RCID_STATE_PENDING: 423 ossl_pqueue_RCID_remove(rcidm->rcids, rcid->pq_idx); 424 break; 425 case RCID_STATE_CUR: 426 rcidm->cur_rcid = NULL; 427 break; 428 case RCID_STATE_RETIRING: 429 ossl_list_retiring_remove(&rcidm->retiring_list, rcid); 430 --rcidm->num_retiring; 431 break; 432 default: 433 assert(0); 434 break; 435 } 436 437 OPENSSL_free(rcid); 438 } 439 440 static void rcidm_handle_retire_prior_to(QUIC_RCIDM *rcidm, 441 uint64_t retire_prior_to) 442 { 443 RCID *rcid; 444 445 if (retire_prior_to <= rcidm->retire_prior_to) 446 return; 447 448 /* 449 * Retire the current RCID (if any) if it is affected. 450 */ 451 if (rcidm->cur_rcid != NULL && rcidm->cur_rcid->seq_num < retire_prior_to) 452 rcidm_transition_rcid(rcidm, rcidm->cur_rcid, RCID_STATE_RETIRING); 453 454 /* 455 * Any other RCIDs needing retirement will be at the start of the priority 456 * queue, so just stop once we see a higher sequence number exceeding the 457 * threshold. 458 */ 459 while ((rcid = ossl_pqueue_RCID_peek(rcidm->rcids)) != NULL 460 && rcid->seq_num < retire_prior_to) 461 rcidm_transition_rcid(rcidm, rcid, RCID_STATE_RETIRING); 462 463 rcidm->retire_prior_to = retire_prior_to; 464 } 465 466 /* 467 * Decision Logic 468 * ============== 469 */ 470 471 static void rcidm_roll(QUIC_RCIDM *rcidm) 472 { 473 RCID *rcid; 474 475 if ((rcid = ossl_pqueue_RCID_peek(rcidm->rcids)) == NULL) 476 return; 477 478 rcidm_transition_rcid(rcidm, rcid, RCID_STATE_CUR); 479 480 ++rcidm->num_changes; 481 rcidm->roll_requested = 0; 482 483 if (rcidm->packets_sent >= PACKETS_PER_RCID) 484 rcidm->packets_sent %= PACKETS_PER_RCID; 485 else 486 rcidm->packets_sent = 0; 487 } 488 489 static void rcidm_update(QUIC_RCIDM *rcidm) 490 { 491 RCID *rcid; 492 493 /* 494 * If we have no current numbered RCID but have one or more pending, use it. 495 */ 496 if (rcidm->cur_rcid == NULL 497 && (rcid = ossl_pqueue_RCID_peek(rcidm->rcids)) != NULL) { 498 rcidm_transition_rcid(rcidm, rcid, RCID_STATE_CUR); 499 assert(rcidm->cur_rcid != NULL); 500 } 501 502 /* Prefer use of any current numbered RCID we have, if possible. */ 503 if (rcidm->cur_rcid != NULL) { 504 rcidm_check_rcid(rcidm, rcidm->cur_rcid); 505 rcidm_set_preferred_rcid(rcidm, &rcidm->cur_rcid->cid); 506 return; 507 } 508 509 /* 510 * If there are no RCIDs from NCID frames we can use, go through the various 511 * kinds of bootstrapping RCIDs we can use in order of priority. 512 */ 513 if (rcidm->added_retry_odcid && !rcidm->handshake_complete) { 514 rcidm_set_preferred_rcid(rcidm, &rcidm->retry_odcid); 515 return; 516 } 517 518 if (rcidm->added_initial_odcid && !rcidm->handshake_complete) { 519 rcidm_set_preferred_rcid(rcidm, &rcidm->initial_odcid); 520 return; 521 } 522 523 /* We don't know of any usable RCIDs */ 524 rcidm_set_preferred_rcid(rcidm, NULL); 525 } 526 527 static int rcidm_should_roll(QUIC_RCIDM *rcidm) 528 { 529 /* 530 * Always switch as soon as possible if handshake completes; 531 * and every n packets after handshake completes or the last roll; and 532 * whenever manually requested. 533 */ 534 return rcidm->handshake_complete 535 && (rcidm->num_changes == 0 536 || rcidm->packets_sent >= PACKETS_PER_RCID 537 || rcidm->roll_requested); 538 } 539 540 static void rcidm_tick(QUIC_RCIDM *rcidm) 541 { 542 if (rcidm_should_roll(rcidm)) 543 rcidm_roll(rcidm); 544 545 rcidm_update(rcidm); 546 } 547 548 /* 549 * Events 550 * ====== 551 */ 552 void ossl_quic_rcidm_on_handshake_complete(QUIC_RCIDM *rcidm) 553 { 554 if (rcidm->handshake_complete) 555 return; 556 557 rcidm->handshake_complete = 1; 558 rcidm_tick(rcidm); 559 } 560 561 void ossl_quic_rcidm_on_packet_sent(QUIC_RCIDM *rcidm, uint64_t num_packets) 562 { 563 if (num_packets == 0) 564 return; 565 566 rcidm->packets_sent += num_packets; 567 rcidm_tick(rcidm); 568 } 569 570 void ossl_quic_rcidm_request_roll(QUIC_RCIDM *rcidm) 571 { 572 rcidm->roll_requested = 1; 573 rcidm_tick(rcidm); 574 } 575 576 /* 577 * Mutation Operations 578 * =================== 579 */ 580 int ossl_quic_rcidm_add_from_initial(QUIC_RCIDM *rcidm, 581 const QUIC_CONN_ID *rcid) 582 { 583 RCID *rcid_obj; 584 585 if (rcidm->added_initial_rcid || rcidm->handshake_complete) 586 return 0; 587 588 rcid_obj = rcidm_create_rcid(rcidm, INITIAL_SEQ_NUM, 589 rcid, RCID_TYPE_INITIAL); 590 if (rcid_obj == NULL) 591 return 0; 592 593 rcidm->added_initial_rcid = 1; 594 rcidm_tick(rcidm); 595 return 1; 596 } 597 598 int ossl_quic_rcidm_add_from_server_retry(QUIC_RCIDM *rcidm, 599 const QUIC_CONN_ID *retry_odcid) 600 { 601 if (rcidm->added_retry_odcid || rcidm->handshake_complete) 602 return 0; 603 604 rcidm->retry_odcid = *retry_odcid; 605 rcidm->added_retry_odcid = 1; 606 rcidm_tick(rcidm); 607 return 1; 608 } 609 610 int ossl_quic_rcidm_add_from_ncid(QUIC_RCIDM *rcidm, 611 const OSSL_QUIC_FRAME_NEW_CONN_ID *ncid) 612 { 613 RCID *rcid; 614 615 rcid = rcidm_create_rcid(rcidm, ncid->seq_num, &ncid->conn_id, RCID_TYPE_NCID); 616 if (rcid == NULL) 617 return 0; 618 619 rcidm_handle_retire_prior_to(rcidm, ncid->retire_prior_to); 620 rcidm_tick(rcidm); 621 return 1; 622 } 623 624 /* 625 * Queries 626 * ======= 627 */ 628 629 static int rcidm_get_retire(QUIC_RCIDM *rcidm, uint64_t *seq_num, int peek) 630 { 631 RCID *rcid = ossl_list_retiring_head(&rcidm->retiring_list); 632 633 if (rcid == NULL) 634 return 0; 635 636 if (seq_num != NULL) 637 *seq_num = rcid->seq_num; 638 639 if (!peek) 640 rcidm_free_rcid(rcidm, rcid); 641 642 return 1; 643 } 644 645 int ossl_quic_rcidm_pop_retire_seq_num(QUIC_RCIDM *rcidm, 646 uint64_t *seq_num) 647 { 648 return rcidm_get_retire(rcidm, seq_num, /*peek=*/0); 649 } 650 651 int ossl_quic_rcidm_peek_retire_seq_num(QUIC_RCIDM *rcidm, 652 uint64_t *seq_num) 653 { 654 return rcidm_get_retire(rcidm, seq_num, /*peek=*/1); 655 } 656 657 int ossl_quic_rcidm_get_preferred_tx_dcid(QUIC_RCIDM *rcidm, 658 QUIC_CONN_ID *tx_dcid) 659 { 660 if (!rcidm->have_preferred_rcid) 661 return 0; 662 663 *tx_dcid = rcidm->preferred_rcid; 664 return 1; 665 } 666 667 int ossl_quic_rcidm_get_preferred_tx_dcid_changed(QUIC_RCIDM *rcidm, 668 int clear) 669 { 670 int r = rcidm->preferred_rcid_changed; 671 672 if (clear) 673 rcidm->preferred_rcid_changed = 0; 674 675 return r; 676 } 677 678 size_t ossl_quic_rcidm_get_num_active(const QUIC_RCIDM *rcidm) 679 { 680 return ossl_pqueue_RCID_num(rcidm->rcids) 681 + (rcidm->cur_rcid != NULL ? 1 : 0) 682 + ossl_quic_rcidm_get_num_retiring(rcidm); 683 } 684 685 size_t ossl_quic_rcidm_get_num_retiring(const QUIC_RCIDM *rcidm) 686 { 687 return rcidm->num_retiring; 688 } 689