xref: /openssl/ssl/quic/quic_rcidm.c (revision c4ec708b)
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