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) 214 retiring_list; 215 216 /* Number of entries on the retiring_list. */ 217 size_t num_retiring; 218 219 /* preferred_rcid has been changed? */ 220 unsigned int preferred_rcid_changed : 1; 221 222 /* Do we have any RCID we can use currently? */ 223 unsigned int have_preferred_rcid : 1; 224 225 /* QUIC handshake has been completed? */ 226 unsigned int handshake_complete : 1; 227 228 /* odcid was set (not necessarily still valid as a RCID)? */ 229 unsigned int added_initial_odcid : 1; 230 /* retry_odcid was set (not necessarily still valid as a RCID?) */ 231 unsigned int added_retry_odcid : 1; 232 /* An initial RCID was added as an RCID structure? */ 233 unsigned int added_initial_rcid : 1; 234 /* Has a RCID roll been manually requested? */ 235 unsigned int roll_requested : 1; 236 }; 237 238 /* 239 * Caller must periodically pop retired RCIDs and handle them. If the caller 240 * fails to do so, fail safely rather than start exhibiting integer rollover. 241 * Limit the total number of numbered RCIDs to an implausibly large but safe 242 * value. 243 */ 244 #define MAX_NUMBERED_RCIDS (SIZE_MAX / 2) 245 246 static void rcidm_transition_rcid(QUIC_RCIDM *rcidm, RCID *rcid, 247 unsigned int state); 248 249 /* Check invariants of an RCID */ 250 static void rcidm_check_rcid(QUIC_RCIDM *rcidm, RCID *rcid) 251 { 252 assert(rcid->state == RCID_STATE_PENDING 253 || rcid->state == RCID_STATE_CUR 254 || rcid->state == RCID_STATE_RETIRING); 255 assert((rcid->state == RCID_STATE_PENDING) 256 == (rcid->pq_idx != SIZE_MAX)); 257 assert((rcid->state == RCID_STATE_CUR) 258 == (rcidm->cur_rcid == rcid)); 259 assert((ossl_list_retiring_next(rcid) != NULL 260 || ossl_list_retiring_prev(rcid) != NULL 261 || ossl_list_retiring_head(&rcidm->retiring_list) == rcid) 262 == (rcid->state == RCID_STATE_RETIRING)); 263 assert(rcid->type != RCID_TYPE_INITIAL || rcid->seq_num == 0); 264 assert(rcid->type != RCID_TYPE_PREF_ADDR || rcid->seq_num == 1); 265 assert(rcid->seq_num <= OSSL_QUIC_VLINT_MAX); 266 assert(rcid->cid.id_len > 0 && rcid->cid.id_len <= QUIC_MAX_CONN_ID_LEN); 267 assert(rcid->seq_num >= rcidm->retire_prior_to 268 || rcid->state == RCID_STATE_RETIRING); 269 assert(rcidm->num_changes == 0 || rcidm->handshake_complete); 270 assert(rcid->state != RCID_STATE_RETIRING || rcidm->num_retiring > 0); 271 } 272 273 static int rcid_cmp(const RCID *a, const RCID *b) 274 { 275 if (a->seq_num < b->seq_num) 276 return -1; 277 if (a->seq_num > b->seq_num) 278 return 1; 279 return 0; 280 } 281 282 QUIC_RCIDM *ossl_quic_rcidm_new(const QUIC_CONN_ID *initial_odcid) 283 { 284 QUIC_RCIDM *rcidm; 285 286 if ((rcidm = OPENSSL_zalloc(sizeof(*rcidm))) == NULL) 287 return NULL; 288 289 if ((rcidm->rcids = ossl_pqueue_RCID_new(rcid_cmp)) == NULL) { 290 OPENSSL_free(rcidm); 291 return NULL; 292 } 293 294 if (initial_odcid != NULL) { 295 rcidm->initial_odcid = *initial_odcid; 296 rcidm->added_initial_odcid = 1; 297 } 298 299 rcidm_update(rcidm); 300 return rcidm; 301 } 302 303 void ossl_quic_rcidm_free(QUIC_RCIDM *rcidm) 304 { 305 RCID *rcid, *rnext; 306 307 if (rcidm == NULL) 308 return; 309 310 OPENSSL_free(rcidm->cur_rcid); 311 while ((rcid = ossl_pqueue_RCID_pop(rcidm->rcids)) != NULL) 312 OPENSSL_free(rcid); 313 314 OSSL_LIST_FOREACH_DELSAFE(rcid, rnext, retiring, &rcidm->retiring_list) 315 OPENSSL_free(rcid); 316 317 ossl_pqueue_RCID_free(rcidm->rcids); 318 OPENSSL_free(rcidm); 319 } 320 321 static void rcidm_set_preferred_rcid(QUIC_RCIDM *rcidm, 322 const QUIC_CONN_ID *rcid) 323 { 324 if (rcid == NULL) { 325 rcidm->preferred_rcid_changed = 1; 326 rcidm->have_preferred_rcid = 0; 327 return; 328 } 329 330 if (ossl_quic_conn_id_eq(&rcidm->preferred_rcid, rcid)) 331 return; 332 333 rcidm->preferred_rcid = *rcid; 334 rcidm->preferred_rcid_changed = 1; 335 rcidm->have_preferred_rcid = 1; 336 } 337 338 /* 339 * RCID Lifecycle Management 340 * ========================= 341 */ 342 static RCID *rcidm_create_rcid(QUIC_RCIDM *rcidm, uint64_t seq_num, 343 const QUIC_CONN_ID *cid, 344 unsigned int type) 345 { 346 RCID *rcid; 347 348 if (cid->id_len < 1 || cid->id_len > QUIC_MAX_CONN_ID_LEN 349 || seq_num > OSSL_QUIC_VLINT_MAX 350 || ossl_pqueue_RCID_num(rcidm->rcids) + rcidm->num_retiring 351 > MAX_NUMBERED_RCIDS) 352 return NULL; 353 354 if ((rcid = OPENSSL_zalloc(sizeof(*rcid))) == NULL) 355 return NULL; 356 357 rcid->seq_num = seq_num; 358 rcid->cid = *cid; 359 rcid->type = type; 360 361 if (rcid->seq_num >= rcidm->retire_prior_to) { 362 rcid->state = RCID_STATE_PENDING; 363 364 if (!ossl_pqueue_RCID_push(rcidm->rcids, rcid, &rcid->pq_idx)) { 365 OPENSSL_free(rcid); 366 return NULL; 367 } 368 } else { 369 /* RCID is immediately retired upon creation. */ 370 rcid->state = RCID_STATE_RETIRING; 371 rcid->pq_idx = SIZE_MAX; 372 ossl_list_retiring_insert_tail(&rcidm->retiring_list, rcid); 373 ++rcidm->num_retiring; 374 } 375 376 rcidm_check_rcid(rcidm, rcid); 377 return rcid; 378 } 379 380 static void rcidm_transition_rcid(QUIC_RCIDM *rcidm, RCID *rcid, 381 unsigned int state) 382 { 383 unsigned int old_state = rcid->state; 384 385 assert(state >= old_state && state <= RCID_STATE_RETIRING); 386 rcidm_check_rcid(rcidm, rcid); 387 if (state == old_state) 388 return; 389 390 if (rcidm->cur_rcid != NULL && state == RCID_STATE_CUR) { 391 rcidm_transition_rcid(rcidm, rcidm->cur_rcid, RCID_STATE_RETIRING); 392 assert(rcidm->cur_rcid == NULL); 393 } 394 395 if (old_state == RCID_STATE_PENDING) { 396 ossl_pqueue_RCID_remove(rcidm->rcids, rcid->pq_idx); 397 rcid->pq_idx = SIZE_MAX; 398 } 399 400 rcid->state = state; 401 402 if (state == RCID_STATE_CUR) { 403 rcidm->cur_rcid = rcid; 404 } else if (state == RCID_STATE_RETIRING) { 405 if (old_state == RCID_STATE_CUR) 406 rcidm->cur_rcid = NULL; 407 408 ossl_list_retiring_insert_tail(&rcidm->retiring_list, rcid); 409 ++rcidm->num_retiring; 410 } 411 412 rcidm_check_rcid(rcidm, rcid); 413 } 414 415 static void rcidm_free_rcid(QUIC_RCIDM *rcidm, RCID *rcid) 416 { 417 if (rcid == NULL) 418 return; 419 420 rcidm_check_rcid(rcidm, rcid); 421 422 switch (rcid->state) { 423 case RCID_STATE_PENDING: 424 ossl_pqueue_RCID_remove(rcidm->rcids, rcid->pq_idx); 425 break; 426 case RCID_STATE_CUR: 427 rcidm->cur_rcid = NULL; 428 break; 429 case RCID_STATE_RETIRING: 430 ossl_list_retiring_remove(&rcidm->retiring_list, rcid); 431 --rcidm->num_retiring; 432 break; 433 default: 434 assert(0); 435 break; 436 } 437 438 OPENSSL_free(rcid); 439 } 440 441 static void rcidm_handle_retire_prior_to(QUIC_RCIDM *rcidm, 442 uint64_t retire_prior_to) 443 { 444 RCID *rcid; 445 446 if (retire_prior_to <= rcidm->retire_prior_to) 447 return; 448 449 /* 450 * Retire the current RCID (if any) if it is affected. 451 */ 452 if (rcidm->cur_rcid != NULL && rcidm->cur_rcid->seq_num < retire_prior_to) 453 rcidm_transition_rcid(rcidm, rcidm->cur_rcid, RCID_STATE_RETIRING); 454 455 /* 456 * Any other RCIDs needing retirement will be at the start of the priority 457 * queue, so just stop once we see a higher sequence number exceeding the 458 * threshold. 459 */ 460 while ((rcid = ossl_pqueue_RCID_peek(rcidm->rcids)) != NULL 461 && rcid->seq_num < retire_prior_to) 462 rcidm_transition_rcid(rcidm, rcid, RCID_STATE_RETIRING); 463 464 rcidm->retire_prior_to = retire_prior_to; 465 } 466 467 /* 468 * Decision Logic 469 * ============== 470 */ 471 472 static void rcidm_roll(QUIC_RCIDM *rcidm) 473 { 474 RCID *rcid; 475 476 if ((rcid = ossl_pqueue_RCID_peek(rcidm->rcids)) == NULL) 477 return; 478 479 rcidm_transition_rcid(rcidm, rcid, RCID_STATE_CUR); 480 481 ++rcidm->num_changes; 482 rcidm->roll_requested = 0; 483 484 if (rcidm->packets_sent >= PACKETS_PER_RCID) 485 rcidm->packets_sent %= PACKETS_PER_RCID; 486 else 487 rcidm->packets_sent = 0; 488 } 489 490 static void rcidm_update(QUIC_RCIDM *rcidm) 491 { 492 RCID *rcid; 493 494 /* 495 * If we have no current numbered RCID but have one or more pending, use it. 496 */ 497 if (rcidm->cur_rcid == NULL 498 && (rcid = ossl_pqueue_RCID_peek(rcidm->rcids)) != NULL) { 499 rcidm_transition_rcid(rcidm, rcid, RCID_STATE_CUR); 500 assert(rcidm->cur_rcid != NULL); 501 } 502 503 /* Prefer use of any current numbered RCID we have, if possible. */ 504 if (rcidm->cur_rcid != NULL) { 505 rcidm_check_rcid(rcidm, rcidm->cur_rcid); 506 rcidm_set_preferred_rcid(rcidm, &rcidm->cur_rcid->cid); 507 return; 508 } 509 510 /* 511 * If there are no RCIDs from NCID frames we can use, go through the various 512 * kinds of bootstrapping RCIDs we can use in order of priority. 513 */ 514 if (rcidm->added_retry_odcid && !rcidm->handshake_complete) { 515 rcidm_set_preferred_rcid(rcidm, &rcidm->retry_odcid); 516 return; 517 } 518 519 if (rcidm->added_initial_odcid && !rcidm->handshake_complete) { 520 rcidm_set_preferred_rcid(rcidm, &rcidm->initial_odcid); 521 return; 522 } 523 524 /* We don't know of any usable RCIDs */ 525 rcidm_set_preferred_rcid(rcidm, NULL); 526 } 527 528 static int rcidm_should_roll(QUIC_RCIDM *rcidm) 529 { 530 /* 531 * Always switch as soon as possible if handshake completes; 532 * and every n packets after handshake completes or the last roll; and 533 * whenever manually requested. 534 */ 535 return rcidm->handshake_complete 536 && (rcidm->num_changes == 0 537 || rcidm->packets_sent >= PACKETS_PER_RCID 538 || rcidm->roll_requested); 539 } 540 541 static void rcidm_tick(QUIC_RCIDM *rcidm) 542 { 543 if (rcidm_should_roll(rcidm)) 544 rcidm_roll(rcidm); 545 546 rcidm_update(rcidm); 547 } 548 549 /* 550 * Events 551 * ====== 552 */ 553 void ossl_quic_rcidm_on_handshake_complete(QUIC_RCIDM *rcidm) 554 { 555 if (rcidm->handshake_complete) 556 return; 557 558 rcidm->handshake_complete = 1; 559 rcidm_tick(rcidm); 560 } 561 562 void ossl_quic_rcidm_on_packet_sent(QUIC_RCIDM *rcidm, uint64_t num_packets) 563 { 564 if (num_packets == 0) 565 return; 566 567 rcidm->packets_sent += num_packets; 568 rcidm_tick(rcidm); 569 } 570 571 void ossl_quic_rcidm_request_roll(QUIC_RCIDM *rcidm) 572 { 573 rcidm->roll_requested = 1; 574 rcidm_tick(rcidm); 575 } 576 577 /* 578 * Mutation Operations 579 * =================== 580 */ 581 int ossl_quic_rcidm_add_from_initial(QUIC_RCIDM *rcidm, 582 const QUIC_CONN_ID *rcid) 583 { 584 RCID *rcid_obj; 585 586 if (rcidm->added_initial_rcid || rcidm->handshake_complete) 587 return 0; 588 589 rcid_obj = rcidm_create_rcid(rcidm, INITIAL_SEQ_NUM, 590 rcid, RCID_TYPE_INITIAL); 591 if (rcid_obj == NULL) 592 return 0; 593 594 rcidm->added_initial_rcid = 1; 595 rcidm_tick(rcidm); 596 return 1; 597 } 598 599 int ossl_quic_rcidm_add_from_server_retry(QUIC_RCIDM *rcidm, 600 const QUIC_CONN_ID *retry_odcid) 601 { 602 if (rcidm->added_retry_odcid || rcidm->handshake_complete) 603 return 0; 604 605 rcidm->retry_odcid = *retry_odcid; 606 rcidm->added_retry_odcid = 1; 607 rcidm_tick(rcidm); 608 return 1; 609 } 610 611 int ossl_quic_rcidm_add_from_ncid(QUIC_RCIDM *rcidm, 612 const OSSL_QUIC_FRAME_NEW_CONN_ID *ncid) 613 { 614 RCID *rcid; 615 616 rcid = rcidm_create_rcid(rcidm, ncid->seq_num, &ncid->conn_id, RCID_TYPE_NCID); 617 if (rcid == NULL) 618 return 0; 619 620 rcidm_handle_retire_prior_to(rcidm, ncid->retire_prior_to); 621 rcidm_tick(rcidm); 622 return 1; 623 } 624 625 /* 626 * Queries 627 * ======= 628 */ 629 630 static int rcidm_get_retire(QUIC_RCIDM *rcidm, uint64_t *seq_num, int peek) 631 { 632 RCID *rcid = ossl_list_retiring_head(&rcidm->retiring_list); 633 634 if (rcid == NULL) 635 return 0; 636 637 if (seq_num != NULL) 638 *seq_num = rcid->seq_num; 639 640 if (!peek) 641 rcidm_free_rcid(rcidm, rcid); 642 643 return 1; 644 } 645 646 int ossl_quic_rcidm_pop_retire_seq_num(QUIC_RCIDM *rcidm, 647 uint64_t *seq_num) 648 { 649 return rcidm_get_retire(rcidm, seq_num, /*peek=*/0); 650 } 651 652 int ossl_quic_rcidm_peek_retire_seq_num(QUIC_RCIDM *rcidm, 653 uint64_t *seq_num) 654 { 655 return rcidm_get_retire(rcidm, seq_num, /*peek=*/1); 656 } 657 658 int ossl_quic_rcidm_get_preferred_tx_dcid(QUIC_RCIDM *rcidm, 659 QUIC_CONN_ID *tx_dcid) 660 { 661 if (!rcidm->have_preferred_rcid) 662 return 0; 663 664 *tx_dcid = rcidm->preferred_rcid; 665 return 1; 666 } 667 668 int ossl_quic_rcidm_get_preferred_tx_dcid_changed(QUIC_RCIDM *rcidm, 669 int clear) 670 { 671 int r = rcidm->preferred_rcid_changed; 672 673 if (clear) 674 rcidm->preferred_rcid_changed = 0; 675 676 return r; 677 } 678 679 size_t ossl_quic_rcidm_get_num_active(const QUIC_RCIDM *rcidm) 680 { 681 return ossl_pqueue_RCID_num(rcidm->rcids) 682 + (rcidm->cur_rcid != NULL ? 1 : 0) 683 + ossl_quic_rcidm_get_num_retiring(rcidm); 684 } 685 686 size_t ossl_quic_rcidm_get_num_retiring(const QUIC_RCIDM *rcidm) 687 { 688 return rcidm->num_retiring; 689 } 690