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 */
rcidm_check_rcid(QUIC_RCIDM * rcidm,RCID * 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
rcid_cmp(const RCID * a,const RCID * b)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
ossl_quic_rcidm_new(const QUIC_CONN_ID * initial_odcid)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
ossl_quic_rcidm_free(QUIC_RCIDM * rcidm)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
rcidm_set_preferred_rcid(QUIC_RCIDM * rcidm,const QUIC_CONN_ID * rcid)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 */
rcidm_create_rcid(QUIC_RCIDM * rcidm,uint64_t seq_num,const QUIC_CONN_ID * cid,unsigned int type)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
rcidm_transition_rcid(QUIC_RCIDM * rcidm,RCID * rcid,unsigned int state)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
rcidm_free_rcid(QUIC_RCIDM * rcidm,RCID * rcid)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
rcidm_handle_retire_prior_to(QUIC_RCIDM * rcidm,uint64_t retire_prior_to)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
rcidm_roll(QUIC_RCIDM * rcidm)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
rcidm_update(QUIC_RCIDM * rcidm)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
rcidm_should_roll(QUIC_RCIDM * rcidm)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
rcidm_tick(QUIC_RCIDM * rcidm)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 */
ossl_quic_rcidm_on_handshake_complete(QUIC_RCIDM * rcidm)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
ossl_quic_rcidm_on_packet_sent(QUIC_RCIDM * rcidm,uint64_t num_packets)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
ossl_quic_rcidm_request_roll(QUIC_RCIDM * rcidm)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 */
ossl_quic_rcidm_add_from_initial(QUIC_RCIDM * rcidm,const QUIC_CONN_ID * rcid)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
ossl_quic_rcidm_add_from_server_retry(QUIC_RCIDM * rcidm,const QUIC_CONN_ID * retry_odcid)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
ossl_quic_rcidm_add_from_ncid(QUIC_RCIDM * rcidm,const OSSL_QUIC_FRAME_NEW_CONN_ID * ncid)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
rcidm_get_retire(QUIC_RCIDM * rcidm,uint64_t * seq_num,int peek)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
ossl_quic_rcidm_pop_retire_seq_num(QUIC_RCIDM * rcidm,uint64_t * seq_num)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
ossl_quic_rcidm_peek_retire_seq_num(QUIC_RCIDM * rcidm,uint64_t * seq_num)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
ossl_quic_rcidm_get_preferred_tx_dcid(QUIC_RCIDM * rcidm,QUIC_CONN_ID * tx_dcid)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
ossl_quic_rcidm_get_preferred_tx_dcid_changed(QUIC_RCIDM * rcidm,int clear)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
ossl_quic_rcidm_get_num_active(const QUIC_RCIDM * rcidm)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
ossl_quic_rcidm_get_num_retiring(const QUIC_RCIDM * rcidm)685 size_t ossl_quic_rcidm_get_num_retiring(const QUIC_RCIDM *rcidm)
686 {
687 return rcidm->num_retiring;
688 }
689