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