Coverage Report

Created: 2026-05-24 07:14

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