quic_rcidm.c revision 1.1.1.2 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