Coverage Report

Created: 2025-11-16 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl35/ssl/quic/quic_rcidm.c
Line
Count
Source
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
9.28M
#define PACKETS_PER_RCID        10000
41
42
73.7k
#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
417M
DEFINE_LIST_OF(retiring, RCID);
quic_rcidm.c:ossl_list_retiring_head
Line
Count
Source
137
DEFINE_LIST_OF(retiring, RCID);
quic_rcidm.c:ossl_list_retiring_next
Line
Count
Source
137
DEFINE_LIST_OF(retiring, RCID);
quic_rcidm.c:ossl_list_retiring_insert_tail
Line
Count
Source
137
DEFINE_LIST_OF(retiring, RCID);
quic_rcidm.c:ossl_list_retiring_prev
Line
Count
Source
137
DEFINE_LIST_OF(retiring, RCID);
quic_rcidm.c:ossl_list_retiring_remove
Line
Count
Source
137
DEFINE_LIST_OF(retiring, RCID);
138
417M
139
417M
/*
140
417M
 * RCID Manager
141
417M
 * ============
142
417M
 *
143
417M
 * The following "business logic" invariants also apply to the RCIDM
144
417M
 * as a whole:
145
417M
 *
146
417M
 *   Invariant: An RCID of INITIAL   type has a sequence number of 0.
147
417M
 *   Invariant: An RCID of PREF_ADDR type has a sequence number of 1.
148
417M
 *
149
417M
 *   Invariant: There is never more than one Initial ODCID
150
417M
 *              added throughout the lifetime of an RCIDM.
151
417M
 *   Invariant: There is never more than one Retry ODCID
152
417M
 *              added throughout the lifetime of an RCIDM.
153
417M
 *   Invariant: There is never more than one INITIAL RCID created
154
417M
 *              throughout the lifetime of an RCIDM.
155
417M
 *   Invariant: There is never more than one PREF_ADDR RCID created
156
417M
 *              throughout the lifetime of an RCIDM.
157
417M
 *   Invariant: No INITIAL or PREF_ADDR RCID may be added after
158
417M
 *              the handshake is completed.
159
417M
 *
160
417M
 */
161
417M
struct quic_rcidm_st {
162
417M
    /*
163
417M
     * The current RCID we prefer to use (value undefined if
164
417M
     * !have_preferred_rcid).
165
417M
     *
166
417M
     * This is preferentially set to a numbered RCID (represented by an RCID
167
417M
     * object) if we have one (in which case preferred_rcid == cur_rcid->cid);
168
417M
     * otherwise it is set to one of the unnumbered RCIDs (the Initial ODCID or
169
417M
     * Retry ODCID) if available (and cur_rcid == NULL).
170
417M
     */
171
417M
    QUIC_CONN_ID                preferred_rcid;
172
417M
173
417M
    /*
174
417M
     * These are initialized if the corresponding added_ flags are set.
175
417M
     */
176
417M
    QUIC_CONN_ID                initial_odcid, retry_odcid;
177
417M
178
417M
    /*
179
417M
     * Total number of packets sent since we last made a packet count-based RCID
180
417M
     * update decision.
181
417M
     */
182
417M
    uint64_t                    packets_sent;
183
417M
184
417M
    /* Number of post-handshake RCID changes we have performed. */
185
417M
    uint64_t                    num_changes;
186
417M
187
417M
    /*
188
417M
     * The Retire Prior To watermark value; max(retire_prior_to) of all received
189
417M
     * NCID frames.
190
417M
     */
191
417M
    uint64_t                    retire_prior_to;
192
417M
193
417M
    /* (SORT BY seq_num ASC) -> (RCID *) */
194
417M
    PRIORITY_QUEUE_OF(RCID)     *rcids;
195
417M
196
417M
    /*
197
417M
     * Current RCID object we are using. This may differ from the first item in
198
417M
     * the priority queue if we received NCID frames out of order. For example
199
417M
     * if we get seq 5, switch to it immediately, then get seq 4, we want to
200
417M
     * keep using seq 5 until we decide to roll again rather than immediately
201
417M
     * switch to seq 4. Never points to an object on the retiring_list.
202
417M
     */
203
417M
    RCID                        *cur_rcid;
204
417M
205
417M
    /*
206
417M
     * When a RCID becomes pending-retirement, it is moved to the retiring_list,
207
417M
     * then freed when it is popped from the retired queue. We use a list for
208
417M
     * this rather than a priority queue as the order in which items are freed
209
417M
     * does not matter. We always append to the tail of the list in order to
210
417M
     * maintain the guarantee that the head (if present) only changes when a
211
417M
     * caller calls pop().
212
417M
     */
213
417M
    OSSL_LIST(retiring)         retiring_list;
214
417M
215
417M
    /* Number of entries on the retiring_list. */
216
417M
    size_t                      num_retiring;
217
417M
218
417M
    /* preferred_rcid has been changed? */
219
417M
    unsigned int    preferred_rcid_changed          : 1;
220
417M
221
417M
    /* Do we have any RCID we can use currently? */
222
417M
    unsigned int    have_preferred_rcid             : 1;
223
417M
224
417M
    /* QUIC handshake has been completed? */
225
417M
    unsigned int    handshake_complete              : 1;
226
417M
227
417M
    /* odcid was set (not necessarily still valid as a RCID)? */
228
417M
    unsigned int    added_initial_odcid             : 1;
229
417M
    /* retry_odcid was set (not necessarily still valid as a RCID?) */
230
417M
    unsigned int    added_retry_odcid               : 1;
231
417M
    /* An initial RCID was added as an RCID structure? */
232
417M
    unsigned int    added_initial_rcid              : 1;
233
417M
    /* Has a RCID roll been manually requested? */
234
417M
    unsigned int    roll_requested                  : 1;
235
417M
};
236
417M
237
417M
/*
238
417M
 * Caller must periodically pop retired RCIDs and handle them. If the caller
239
417M
 * fails to do so, fail safely rather than start exhibiting integer rollover.
240
417M
 * Limit the total number of numbered RCIDs to an implausibly large but safe
241
417M
 * value.
242
417M
 */
243
417M
#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 */
249
static void rcidm_check_rcid(QUIC_RCIDM *rcidm, RCID *rcid)
250
16.9M
{
251
16.9M
    assert(rcid->state == RCID_STATE_PENDING
252
16.9M
           || rcid->state == RCID_STATE_CUR
253
16.9M
           || rcid->state == RCID_STATE_RETIRING);
254
16.9M
    assert((rcid->state == RCID_STATE_PENDING)
255
16.9M
           == (rcid->pq_idx != SIZE_MAX));
256
16.9M
    assert((rcid->state == RCID_STATE_CUR)
257
16.9M
           == (rcidm->cur_rcid == rcid));
258
16.9M
    assert((ossl_list_retiring_next(rcid) != NULL
259
16.9M
            || ossl_list_retiring_prev(rcid) != NULL
260
16.9M
            || ossl_list_retiring_head(&rcidm->retiring_list) == rcid)
261
16.9M
           == (rcid->state == RCID_STATE_RETIRING));
262
16.9M
    assert(rcid->type != RCID_TYPE_INITIAL || rcid->seq_num == 0);
263
16.9M
    assert(rcid->type != RCID_TYPE_PREF_ADDR || rcid->seq_num == 1);
264
16.9M
    assert(rcid->seq_num <= OSSL_QUIC_VLINT_MAX);
265
16.9M
    assert(rcid->cid.id_len > 0 && rcid->cid.id_len <= QUIC_MAX_CONN_ID_LEN);
266
16.9M
    assert(rcid->seq_num >= rcidm->retire_prior_to
267
16.9M
            || rcid->state == RCID_STATE_RETIRING);
268
16.9M
    assert(rcidm->num_changes == 0 || rcidm->handshake_complete);
269
16.9M
    assert(rcid->state != RCID_STATE_RETIRING || rcidm->num_retiring > 0);
270
16.9M
}
271
272
static int rcid_cmp(const RCID *a, const RCID *b)
273
46.5M
{
274
46.5M
    if (a->seq_num < b->seq_num)
275
21.6M
        return -1;
276
24.8M
    if (a->seq_num > b->seq_num)
277
10.1M
        return 1;
278
14.7M
    return 0;
279
24.8M
}
280
281
QUIC_RCIDM *ossl_quic_rcidm_new(const QUIC_CONN_ID *initial_odcid)
282
2.23M
{
283
2.23M
    QUIC_RCIDM *rcidm;
284
285
2.23M
    if ((rcidm = OPENSSL_zalloc(sizeof(*rcidm))) == NULL)
286
0
        return NULL;
287
288
2.23M
    if ((rcidm->rcids = ossl_pqueue_RCID_new(rcid_cmp)) == NULL) {
289
0
        OPENSSL_free(rcidm);
290
0
        return NULL;
291
0
    }
292
293
2.23M
    if (initial_odcid != NULL) {
294
2.11M
        rcidm->initial_odcid        = *initial_odcid;
295
2.11M
        rcidm->added_initial_odcid  = 1;
296
2.11M
    }
297
298
2.23M
    rcidm_update(rcidm);
299
2.23M
    return rcidm;
300
2.23M
}
301
302
void ossl_quic_rcidm_free(QUIC_RCIDM *rcidm)
303
2.23M
{
304
2.23M
    RCID *rcid, *rnext;
305
306
2.23M
    if (rcidm == NULL)
307
0
        return;
308
309
2.23M
    OPENSSL_free(rcidm->cur_rcid);
310
3.73M
    while ((rcid = ossl_pqueue_RCID_pop(rcidm->rcids)) != NULL)
311
1.49M
        OPENSSL_free(rcid);
312
313
2.23M
    OSSL_LIST_FOREACH_DELSAFE(rcid, rnext, retiring, &rcidm->retiring_list)
314
3.24M
        OPENSSL_free(rcid);
315
316
2.23M
    ossl_pqueue_RCID_free(rcidm->rcids);
317
2.23M
    OPENSSL_free(rcidm);
318
2.23M
}
319
320
static void rcidm_set_preferred_rcid(QUIC_RCIDM *rcidm,
321
                                     const QUIC_CONN_ID *rcid)
322
10.2M
{
323
10.2M
    if (rcid == NULL) {
324
1.45M
        rcidm->preferred_rcid_changed   = 1;
325
1.45M
        rcidm->have_preferred_rcid      = 0;
326
1.45M
        return;
327
1.45M
    }
328
329
8.78M
    if (ossl_quic_conn_id_eq(&rcidm->preferred_rcid, rcid))
330
7.94M
        return;
331
332
835k
    rcidm->preferred_rcid           = *rcid;
333
835k
    rcidm->preferred_rcid_changed   = 1;
334
835k
    rcidm->have_preferred_rcid      = 1;
335
835k
}
336
337
/*
338
 * RCID Lifecycle Management
339
 * =========================
340
 */
341
static RCID *rcidm_create_rcid(QUIC_RCIDM *rcidm, uint64_t seq_num,
342
                               const QUIC_CONN_ID *cid,
343
                               unsigned int type)
344
4.99M
{
345
4.99M
    RCID *rcid;
346
347
4.99M
    if (cid->id_len < 1 || cid->id_len > QUIC_MAX_CONN_ID_LEN
348
4.91M
        || seq_num > OSSL_QUIC_VLINT_MAX
349
4.89M
        || ossl_pqueue_RCID_num(rcidm->rcids) + rcidm->num_retiring
350
4.89M
            > MAX_NUMBERED_RCIDS)
351
101k
        return NULL;
352
353
4.89M
    if ((rcid = OPENSSL_zalloc(sizeof(*rcid))) == NULL)
354
0
        return NULL;
355
356
4.89M
    rcid->seq_num           = seq_num;
357
4.89M
    rcid->cid               = *cid;
358
4.89M
    rcid->type              = type;
359
360
4.89M
    if (rcid->seq_num >= rcidm->retire_prior_to) {
361
3.76M
        rcid->state = RCID_STATE_PENDING;
362
363
3.76M
        if (!ossl_pqueue_RCID_push(rcidm->rcids, rcid, &rcid->pq_idx)) {
364
0
            OPENSSL_free(rcid);
365
0
            return NULL;
366
0
        }
367
3.76M
    } else {
368
        /* RCID is immediately retired upon creation. */
369
1.12M
        rcid->state     = RCID_STATE_RETIRING;
370
1.12M
        rcid->pq_idx    = SIZE_MAX;
371
1.12M
        ossl_list_retiring_insert_tail(&rcidm->retiring_list, rcid);
372
1.12M
        ++rcidm->num_retiring;
373
1.12M
    }
374
375
4.89M
    rcidm_check_rcid(rcidm, rcid);
376
4.89M
    return rcid;
377
4.89M
}
378
379
static void rcidm_transition_rcid(QUIC_RCIDM *rcidm, RCID *rcid,
380
                                  unsigned int state)
381
2.95M
{
382
2.95M
    unsigned int old_state = rcid->state;
383
384
2.95M
    assert(state >= old_state && state <= RCID_STATE_RETIRING);
385
2.95M
    rcidm_check_rcid(rcidm, rcid);
386
2.95M
    if (state == old_state)
387
0
        return;
388
389
2.95M
    if (rcidm->cur_rcid != NULL && state == RCID_STATE_CUR) {
390
598k
        rcidm_transition_rcid(rcidm, rcidm->cur_rcid, RCID_STATE_RETIRING);
391
598k
        assert(rcidm->cur_rcid == NULL);
392
598k
    }
393
394
2.95M
    if (old_state == RCID_STATE_PENDING) {
395
2.27M
        ossl_pqueue_RCID_remove(rcidm->rcids, rcid->pq_idx);
396
2.27M
        rcid->pq_idx = SIZE_MAX;
397
2.27M
    }
398
399
2.95M
    rcid->state = state;
400
401
2.95M
    if (state == RCID_STATE_CUR) {
402
802k
        rcidm->cur_rcid = rcid;
403
2.15M
    } else if (state == RCID_STATE_RETIRING) {
404
2.15M
        if (old_state == RCID_STATE_CUR)
405
680k
            rcidm->cur_rcid = NULL;
406
407
2.15M
        ossl_list_retiring_insert_tail(&rcidm->retiring_list, rcid);
408
2.15M
        ++rcidm->num_retiring;
409
2.15M
    }
410
411
2.95M
    rcidm_check_rcid(rcidm, rcid);
412
2.95M
}
413
414
static void rcidm_free_rcid(QUIC_RCIDM *rcidm, RCID *rcid)
415
30.3k
{
416
30.3k
    if (rcid == NULL)
417
0
        return;
418
419
30.3k
    rcidm_check_rcid(rcidm, rcid);
420
421
30.3k
    switch (rcid->state) {
422
0
    case RCID_STATE_PENDING:
423
0
        ossl_pqueue_RCID_remove(rcidm->rcids, rcid->pq_idx);
424
0
        break;
425
0
    case RCID_STATE_CUR:
426
0
        rcidm->cur_rcid = NULL;
427
0
        break;
428
30.3k
    case RCID_STATE_RETIRING:
429
30.3k
        ossl_list_retiring_remove(&rcidm->retiring_list, rcid);
430
30.3k
        --rcidm->num_retiring;
431
30.3k
        break;
432
0
    default:
433
0
        assert(0);
434
0
        break;
435
30.3k
    }
436
437
30.3k
    OPENSSL_free(rcid);
438
30.3k
}
439
440
static void rcidm_handle_retire_prior_to(QUIC_RCIDM *rcidm,
441
                                         uint64_t retire_prior_to)
442
4.83M
{
443
4.83M
    RCID *rcid;
444
445
4.83M
    if (retire_prior_to <= rcidm->retire_prior_to)
446
4.58M
        return;
447
448
    /*
449
     * Retire the current RCID (if any) if it is affected.
450
     */
451
250k
    if (rcidm->cur_rcid != NULL && rcidm->cur_rcid->seq_num < retire_prior_to)
452
81.1k
        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
1.72M
    while ((rcid = ossl_pqueue_RCID_peek(rcidm->rcids)) != NULL
460
1.65M
           && rcid->seq_num < retire_prior_to)
461
1.47M
        rcidm_transition_rcid(rcidm, rcid, RCID_STATE_RETIRING);
462
463
250k
    rcidm->retire_prior_to = retire_prior_to;
464
250k
}
465
466
/*
467
 * Decision Logic
468
 * ==============
469
 */
470
471
static void rcidm_roll(QUIC_RCIDM *rcidm)
472
3.04M
{
473
3.04M
    RCID *rcid;
474
475
3.04M
    if ((rcid = ossl_pqueue_RCID_peek(rcidm->rcids)) == NULL)
476
2.40M
        return;
477
478
642k
    rcidm_transition_rcid(rcidm, rcid, RCID_STATE_CUR);
479
480
642k
    ++rcidm->num_changes;
481
642k
    rcidm->roll_requested = 0;
482
483
642k
    if (rcidm->packets_sent >= PACKETS_PER_RCID)
484
129k
        rcidm->packets_sent %= PACKETS_PER_RCID;
485
513k
    else
486
513k
        rcidm->packets_sent = 0;
487
642k
}
488
489
static void rcidm_update(QUIC_RCIDM *rcidm)
490
10.2M
{
491
10.2M
    RCID *rcid;
492
493
    /*
494
     * If we have no current numbered RCID but have one or more pending, use it.
495
     */
496
10.2M
    if (rcidm->cur_rcid == NULL
497
4.25M
        && (rcid = ossl_pqueue_RCID_peek(rcidm->rcids)) != NULL) {
498
160k
        rcidm_transition_rcid(rcidm, rcid, RCID_STATE_CUR);
499
160k
        assert(rcidm->cur_rcid != NULL);
500
160k
    }
501
502
    /* Prefer use of any current numbered RCID we have, if possible. */
503
10.2M
    if (rcidm->cur_rcid != NULL) {
504
6.15M
        rcidm_check_rcid(rcidm, rcidm->cur_rcid);
505
6.15M
        rcidm_set_preferred_rcid(rcidm, &rcidm->cur_rcid->cid);
506
6.15M
        return;
507
6.15M
    }
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
4.09M
    if (rcidm->added_retry_odcid && !rcidm->handshake_complete) {
514
215k
        rcidm_set_preferred_rcid(rcidm, &rcidm->retry_odcid);
515
215k
        return;
516
215k
    }
517
518
3.87M
    if (rcidm->added_initial_odcid && !rcidm->handshake_complete) {
519
2.41M
        rcidm_set_preferred_rcid(rcidm, &rcidm->initial_odcid);
520
2.41M
        return;
521
2.41M
    }
522
523
    /* We don't know of any usable RCIDs */
524
1.45M
    rcidm_set_preferred_rcid(rcidm, NULL);
525
1.45M
}
526
527
static int rcidm_should_roll(QUIC_RCIDM *rcidm)
528
8.00M
{
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
8.00M
    return rcidm->handshake_complete
535
4.66M
        && (rcidm->num_changes == 0
536
3.84M
            || rcidm->packets_sent >= PACKETS_PER_RCID
537
3.15M
            || rcidm->roll_requested);
538
8.00M
}
539
540
static void rcidm_tick(QUIC_RCIDM *rcidm)
541
8.00M
{
542
8.00M
    if (rcidm_should_roll(rcidm))
543
3.04M
        rcidm_roll(rcidm);
544
545
8.00M
    rcidm_update(rcidm);
546
8.00M
}
547
548
/*
549
 * Events
550
 * ======
551
 */
552
void ossl_quic_rcidm_on_handshake_complete(QUIC_RCIDM *rcidm)
553
404k
{
554
404k
    if (rcidm->handshake_complete)
555
310k
        return;
556
557
93.7k
    rcidm->handshake_complete = 1;
558
93.7k
    rcidm_tick(rcidm);
559
93.7k
}
560
561
void ossl_quic_rcidm_on_packet_sent(QUIC_RCIDM *rcidm, uint64_t num_packets)
562
539k
{
563
539k
    if (num_packets == 0)
564
17.8k
        return;
565
566
521k
    rcidm->packets_sent += num_packets;
567
521k
    rcidm_tick(rcidm);
568
521k
}
569
570
void ossl_quic_rcidm_request_roll(QUIC_RCIDM *rcidm)
571
2.30M
{
572
2.30M
    rcidm->roll_requested = 1;
573
2.30M
    rcidm_tick(rcidm);
574
2.30M
}
575
576
/*
577
 * Mutation Operations
578
 * ===================
579
 */
580
int ossl_quic_rcidm_add_from_initial(QUIC_RCIDM *rcidm,
581
                                     const QUIC_CONN_ID *rcid)
582
265k
{
583
265k
    RCID *rcid_obj;
584
585
265k
    if (rcidm->added_initial_rcid || rcidm->handshake_complete)
586
192k
        return 0;
587
588
73.7k
    rcid_obj = rcidm_create_rcid(rcidm, INITIAL_SEQ_NUM,
589
73.7k
                                 rcid, RCID_TYPE_INITIAL);
590
73.7k
    if (rcid_obj == NULL)
591
11.8k
        return 0;
592
593
61.8k
    rcidm->added_initial_rcid = 1;
594
61.8k
    rcidm_tick(rcidm);
595
61.8k
    return 1;
596
73.7k
}
597
598
int ossl_quic_rcidm_add_from_server_retry(QUIC_RCIDM *rcidm,
599
                                          const QUIC_CONN_ID *retry_odcid)
600
303k
{
601
303k
    if (rcidm->added_retry_odcid || rcidm->handshake_complete)
602
108k
        return 0;
603
604
195k
    rcidm->retry_odcid          = *retry_odcid;
605
195k
    rcidm->added_retry_odcid    = 1;
606
195k
    rcidm_tick(rcidm);
607
195k
    return 1;
608
303k
}
609
610
int ossl_quic_rcidm_add_from_ncid(QUIC_RCIDM *rcidm,
611
                                  const OSSL_QUIC_FRAME_NEW_CONN_ID *ncid)
612
4.92M
{
613
4.92M
    RCID *rcid;
614
615
4.92M
    rcid = rcidm_create_rcid(rcidm, ncid->seq_num, &ncid->conn_id, RCID_TYPE_NCID);
616
4.92M
    if (rcid == NULL)
617
89.1k
        return 0;
618
619
4.83M
    rcidm_handle_retire_prior_to(rcidm, ncid->retire_prior_to);
620
4.83M
    rcidm_tick(rcidm);
621
4.83M
    return 1;
622
4.92M
}
623
624
/*
625
 * Queries
626
 * =======
627
 */
628
629
static int rcidm_get_retire(QUIC_RCIDM *rcidm, uint64_t *seq_num, int peek)
630
151k
{
631
151k
    RCID *rcid = ossl_list_retiring_head(&rcidm->retiring_list);
632
633
151k
    if (rcid == NULL)
634
93.5k
        return 0;
635
636
57.9k
    if (seq_num != NULL)
637
57.9k
        *seq_num = rcid->seq_num;
638
639
57.9k
    if (!peek)
640
30.3k
        rcidm_free_rcid(rcidm, rcid);
641
642
57.9k
    return 1;
643
151k
}
644
645
int ossl_quic_rcidm_pop_retire_seq_num(QUIC_RCIDM *rcidm,
646
                                       uint64_t *seq_num)
647
95.0k
{
648
95.0k
    return rcidm_get_retire(rcidm, seq_num, /*peek=*/0);
649
95.0k
}
650
651
int ossl_quic_rcidm_peek_retire_seq_num(QUIC_RCIDM *rcidm,
652
                                        uint64_t *seq_num)
653
56.4k
{
654
56.4k
    return rcidm_get_retire(rcidm, seq_num, /*peek=*/1);
655
56.4k
}
656
657
int ossl_quic_rcidm_get_preferred_tx_dcid(QUIC_RCIDM *rcidm,
658
                                          QUIC_CONN_ID *tx_dcid)
659
73.4k
{
660
73.4k
    if (!rcidm->have_preferred_rcid)
661
31.9k
        return 0;
662
663
41.4k
    *tx_dcid = rcidm->preferred_rcid;
664
41.4k
    return 1;
665
73.4k
}
666
667
int ossl_quic_rcidm_get_preferred_tx_dcid_changed(QUIC_RCIDM *rcidm,
668
                                                  int clear)
669
288k
{
670
288k
    int r = rcidm->preferred_rcid_changed;
671
672
288k
    if (clear)
673
274k
        rcidm->preferred_rcid_changed = 0;
674
675
288k
    return r;
676
288k
}
677
678
size_t ossl_quic_rcidm_get_num_active(const QUIC_RCIDM *rcidm)
679
0
{
680
0
    return ossl_pqueue_RCID_num(rcidm->rcids)
681
0
        + (rcidm->cur_rcid != NULL ? 1 : 0)
682
0
        + ossl_quic_rcidm_get_num_retiring(rcidm);
683
0
}
684
685
size_t ossl_quic_rcidm_get_num_retiring(const QUIC_RCIDM *rcidm)
686
0
{
687
0
    return rcidm->num_retiring;
688
0
}