/src/openssl34/ssl/quic/quic_rcidm.c
Line | Count | Source (jump to first uncovered line) |
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 | 10.4M | #define PACKETS_PER_RCID 10000 |
41 | | |
42 | 102k | #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 | 4.79M | #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 | 17.6M | { |
251 | 17.6M | assert(rcid->state == RCID_STATE_PENDING |
252 | 17.6M | || rcid->state == RCID_STATE_CUR |
253 | 17.6M | || rcid->state == RCID_STATE_RETIRING); |
254 | 17.6M | assert((rcid->state == RCID_STATE_PENDING) |
255 | 17.6M | == (rcid->pq_idx != SIZE_MAX)); |
256 | 17.6M | assert((rcid->state == RCID_STATE_CUR) |
257 | 17.6M | == (rcidm->cur_rcid == rcid)); |
258 | 17.6M | assert((ossl_list_retiring_next(rcid) != NULL |
259 | 17.6M | || ossl_list_retiring_prev(rcid) != NULL |
260 | 17.6M | || ossl_list_retiring_head(&rcidm->retiring_list) == rcid) |
261 | 17.6M | == (rcid->state == RCID_STATE_RETIRING)); |
262 | 17.6M | assert(rcid->type != RCID_TYPE_INITIAL || rcid->seq_num == 0); |
263 | 17.6M | assert(rcid->type != RCID_TYPE_PREF_ADDR || rcid->seq_num == 1); |
264 | 17.6M | assert(rcid->seq_num <= OSSL_QUIC_VLINT_MAX); |
265 | 17.6M | assert(rcid->cid.id_len > 0 && rcid->cid.id_len <= QUIC_MAX_CONN_ID_LEN); |
266 | 17.6M | assert(rcid->seq_num >= rcidm->retire_prior_to |
267 | 17.6M | || rcid->state == RCID_STATE_RETIRING); |
268 | 17.6M | assert(rcidm->num_changes == 0 || rcidm->handshake_complete); |
269 | 17.6M | assert(rcid->state != RCID_STATE_RETIRING || rcidm->num_retiring > 0); |
270 | 17.6M | } |
271 | | |
272 | | static int rcid_cmp(const RCID *a, const RCID *b) |
273 | 37.7M | { |
274 | 37.7M | if (a->seq_num < b->seq_num) |
275 | 16.8M | return -1; |
276 | 20.8M | if (a->seq_num > b->seq_num) |
277 | 7.90M | return 1; |
278 | 12.9M | return 0; |
279 | 20.8M | } |
280 | | |
281 | | QUIC_RCIDM *ossl_quic_rcidm_new(const QUIC_CONN_ID *initial_odcid) |
282 | 3.20M | { |
283 | 3.20M | QUIC_RCIDM *rcidm; |
284 | | |
285 | 3.20M | if ((rcidm = OPENSSL_zalloc(sizeof(*rcidm))) == NULL) |
286 | 0 | return NULL; |
287 | | |
288 | 3.20M | if ((rcidm->rcids = ossl_pqueue_RCID_new(rcid_cmp)) == NULL) { |
289 | 0 | OPENSSL_free(rcidm); |
290 | 0 | return NULL; |
291 | 0 | } |
292 | | |
293 | 3.20M | if (initial_odcid != NULL) { |
294 | 3.09M | rcidm->initial_odcid = *initial_odcid; |
295 | 3.09M | rcidm->added_initial_odcid = 1; |
296 | 3.09M | } |
297 | | |
298 | 3.20M | rcidm_update(rcidm); |
299 | 3.20M | return rcidm; |
300 | 3.20M | } |
301 | | |
302 | | void ossl_quic_rcidm_free(QUIC_RCIDM *rcidm) |
303 | 3.20M | { |
304 | 3.20M | RCID *rcid, *rnext; |
305 | | |
306 | 3.20M | if (rcidm == NULL) |
307 | 0 | return; |
308 | | |
309 | 3.20M | OPENSSL_free(rcidm->cur_rcid); |
310 | 4.43M | while ((rcid = ossl_pqueue_RCID_pop(rcidm->rcids)) != NULL) |
311 | 1.23M | OPENSSL_free(rcid); |
312 | | |
313 | 3.20M | OSSL_LIST_FOREACH_DELSAFE(rcid, rnext, retiring, &rcidm->retiring_list) |
314 | 3.36M | OPENSSL_free(rcid); |
315 | | |
316 | 3.20M | ossl_pqueue_RCID_free(rcidm->rcids); |
317 | 3.20M | OPENSSL_free(rcidm); |
318 | 3.20M | } |
319 | | |
320 | | static void rcidm_set_preferred_rcid(QUIC_RCIDM *rcidm, |
321 | | const QUIC_CONN_ID *rcid) |
322 | 11.6M | { |
323 | 11.6M | if (rcid == NULL) { |
324 | 1.52M | rcidm->preferred_rcid_changed = 1; |
325 | 1.52M | rcidm->have_preferred_rcid = 0; |
326 | 1.52M | return; |
327 | 1.52M | } |
328 | | |
329 | 10.1M | if (ossl_quic_conn_id_eq(&rcidm->preferred_rcid, rcid)) |
330 | 9.15M | return; |
331 | | |
332 | 967k | rcidm->preferred_rcid = *rcid; |
333 | 967k | rcidm->preferred_rcid_changed = 1; |
334 | 967k | rcidm->have_preferred_rcid = 1; |
335 | 967k | } |
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.90M | { |
345 | 4.90M | RCID *rcid; |
346 | | |
347 | 4.90M | if (cid->id_len < 1 || cid->id_len > QUIC_MAX_CONN_ID_LEN |
348 | 4.90M | || seq_num > OSSL_QUIC_VLINT_MAX |
349 | 4.90M | || ossl_pqueue_RCID_num(rcidm->rcids) + rcidm->num_retiring |
350 | 4.79M | > MAX_NUMBERED_RCIDS) |
351 | 112k | return NULL; |
352 | | |
353 | 4.79M | if ((rcid = OPENSSL_zalloc(sizeof(*rcid))) == NULL) |
354 | 0 | return NULL; |
355 | | |
356 | 4.79M | rcid->seq_num = seq_num; |
357 | 4.79M | rcid->cid = *cid; |
358 | 4.79M | rcid->type = type; |
359 | | |
360 | 4.79M | if (rcid->seq_num >= rcidm->retire_prior_to) { |
361 | 3.64M | rcid->state = RCID_STATE_PENDING; |
362 | | |
363 | 3.64M | if (!ossl_pqueue_RCID_push(rcidm->rcids, rcid, &rcid->pq_idx)) { |
364 | 0 | OPENSSL_free(rcid); |
365 | 0 | return NULL; |
366 | 0 | } |
367 | 3.64M | } else { |
368 | | /* RCID is immediately retired upon creation. */ |
369 | 1.14M | rcid->state = RCID_STATE_RETIRING; |
370 | 1.14M | rcid->pq_idx = SIZE_MAX; |
371 | 1.14M | ossl_list_retiring_insert_tail(&rcidm->retiring_list, rcid); |
372 | 1.14M | ++rcidm->num_retiring; |
373 | 1.14M | } |
374 | | |
375 | 4.79M | rcidm_check_rcid(rcidm, rcid); |
376 | 4.79M | return rcid; |
377 | 4.79M | } |
378 | | |
379 | | static void rcidm_transition_rcid(QUIC_RCIDM *rcidm, RCID *rcid, |
380 | | unsigned int state) |
381 | 3.18M | { |
382 | 3.18M | unsigned int old_state = rcid->state; |
383 | | |
384 | 3.18M | assert(state >= old_state && state <= RCID_STATE_RETIRING); |
385 | 3.18M | rcidm_check_rcid(rcidm, rcid); |
386 | 3.18M | if (state == old_state) |
387 | 0 | return; |
388 | | |
389 | 3.18M | if (rcidm->cur_rcid != NULL && state == RCID_STATE_CUR) { |
390 | 675k | rcidm_transition_rcid(rcidm, rcidm->cur_rcid, RCID_STATE_RETIRING); |
391 | 675k | assert(rcidm->cur_rcid == NULL); |
392 | 675k | } |
393 | | |
394 | 3.18M | if (old_state == RCID_STATE_PENDING) { |
395 | 2.41M | ossl_pqueue_RCID_remove(rcidm->rcids, rcid->pq_idx); |
396 | 2.41M | rcid->pq_idx = SIZE_MAX; |
397 | 2.41M | } |
398 | | |
399 | 3.18M | rcid->state = state; |
400 | | |
401 | 3.18M | if (state == RCID_STATE_CUR) { |
402 | 934k | rcidm->cur_rcid = rcid; |
403 | 2.25M | } else if (state == RCID_STATE_RETIRING) { |
404 | 2.25M | if (old_state == RCID_STATE_CUR) |
405 | 775k | rcidm->cur_rcid = NULL; |
406 | | |
407 | 2.25M | ossl_list_retiring_insert_tail(&rcidm->retiring_list, rcid); |
408 | 2.25M | ++rcidm->num_retiring; |
409 | 2.25M | } |
410 | | |
411 | 3.18M | rcidm_check_rcid(rcidm, rcid); |
412 | 3.18M | } |
413 | | |
414 | | static void rcidm_free_rcid(QUIC_RCIDM *rcidm, RCID *rcid) |
415 | 35.0k | { |
416 | 35.0k | if (rcid == NULL) |
417 | 0 | return; |
418 | | |
419 | 35.0k | rcidm_check_rcid(rcidm, rcid); |
420 | | |
421 | 35.0k | 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 | 35.0k | case RCID_STATE_RETIRING: |
429 | 35.0k | ossl_list_retiring_remove(&rcidm->retiring_list, rcid); |
430 | 35.0k | --rcidm->num_retiring; |
431 | 35.0k | break; |
432 | 0 | default: |
433 | 0 | assert(0); |
434 | 0 | break; |
435 | 35.0k | } |
436 | | |
437 | 35.0k | OPENSSL_free(rcid); |
438 | 35.0k | } |
439 | | |
440 | | static void rcidm_handle_retire_prior_to(QUIC_RCIDM *rcidm, |
441 | | uint64_t retire_prior_to) |
442 | 4.70M | { |
443 | 4.70M | RCID *rcid; |
444 | | |
445 | 4.70M | if (retire_prior_to <= rcidm->retire_prior_to) |
446 | 4.41M | return; |
447 | | |
448 | | /* |
449 | | * Retire the current RCID (if any) if it is affected. |
450 | | */ |
451 | 288k | if (rcidm->cur_rcid != NULL && rcidm->cur_rcid->seq_num < retire_prior_to) |
452 | 100k | 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.76M | while ((rcid = ossl_pqueue_RCID_peek(rcidm->rcids)) != NULL |
460 | 1.76M | && rcid->seq_num < retire_prior_to) |
461 | 1.47M | rcidm_transition_rcid(rcidm, rcid, RCID_STATE_RETIRING); |
462 | | |
463 | 288k | rcidm->retire_prior_to = retire_prior_to; |
464 | 288k | } |
465 | | |
466 | | /* |
467 | | * Decision Logic |
468 | | * ============== |
469 | | */ |
470 | | |
471 | | static void rcidm_roll(QUIC_RCIDM *rcidm) |
472 | 3.51M | { |
473 | 3.51M | RCID *rcid; |
474 | | |
475 | 3.51M | if ((rcid = ossl_pqueue_RCID_peek(rcidm->rcids)) == NULL) |
476 | 2.79M | return; |
477 | | |
478 | 720k | rcidm_transition_rcid(rcidm, rcid, RCID_STATE_CUR); |
479 | | |
480 | 720k | ++rcidm->num_changes; |
481 | 720k | rcidm->roll_requested = 0; |
482 | | |
483 | 720k | if (rcidm->packets_sent >= PACKETS_PER_RCID) |
484 | 114k | rcidm->packets_sent %= PACKETS_PER_RCID; |
485 | 606k | else |
486 | 606k | rcidm->packets_sent = 0; |
487 | 720k | } |
488 | | |
489 | | static void rcidm_update(QUIC_RCIDM *rcidm) |
490 | 11.6M | { |
491 | 11.6M | RCID *rcid; |
492 | | |
493 | | /* |
494 | | * If we have no current numbered RCID but have one or more pending, use it. |
495 | | */ |
496 | 11.6M | if (rcidm->cur_rcid == NULL |
497 | 11.6M | && (rcid = ossl_pqueue_RCID_peek(rcidm->rcids)) != NULL) { |
498 | 213k | rcidm_transition_rcid(rcidm, rcid, RCID_STATE_CUR); |
499 | 213k | assert(rcidm->cur_rcid != NULL); |
500 | 213k | } |
501 | | |
502 | | /* Prefer use of any current numbered RCID we have, if possible. */ |
503 | 11.6M | if (rcidm->cur_rcid != NULL) { |
504 | 6.47M | rcidm_check_rcid(rcidm, rcidm->cur_rcid); |
505 | 6.47M | rcidm_set_preferred_rcid(rcidm, &rcidm->cur_rcid->cid); |
506 | 6.47M | return; |
507 | 6.47M | } |
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 | 5.17M | if (rcidm->added_retry_odcid && !rcidm->handshake_complete) { |
514 | 202k | rcidm_set_preferred_rcid(rcidm, &rcidm->retry_odcid); |
515 | 202k | return; |
516 | 202k | } |
517 | | |
518 | 4.97M | if (rcidm->added_initial_odcid && !rcidm->handshake_complete) { |
519 | 3.44M | rcidm_set_preferred_rcid(rcidm, &rcidm->initial_odcid); |
520 | 3.44M | return; |
521 | 3.44M | } |
522 | | |
523 | | /* We don't know of any usable RCIDs */ |
524 | 1.52M | rcidm_set_preferred_rcid(rcidm, NULL); |
525 | 1.52M | } |
526 | | |
527 | | static int rcidm_should_roll(QUIC_RCIDM *rcidm) |
528 | 8.45M | { |
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.45M | return rcidm->handshake_complete |
535 | 8.45M | && (rcidm->num_changes == 0 |
536 | 5.33M | || rcidm->packets_sent >= PACKETS_PER_RCID |
537 | 5.33M | || rcidm->roll_requested); |
538 | 8.45M | } |
539 | | |
540 | | static void rcidm_tick(QUIC_RCIDM *rcidm) |
541 | 8.45M | { |
542 | 8.45M | if (rcidm_should_roll(rcidm)) |
543 | 3.51M | rcidm_roll(rcidm); |
544 | | |
545 | 8.45M | rcidm_update(rcidm); |
546 | 8.45M | } |
547 | | |
548 | | /* |
549 | | * Events |
550 | | * ====== |
551 | | */ |
552 | | void ossl_quic_rcidm_on_handshake_complete(QUIC_RCIDM *rcidm) |
553 | 419k | { |
554 | 419k | if (rcidm->handshake_complete) |
555 | 313k | return; |
556 | | |
557 | 105k | rcidm->handshake_complete = 1; |
558 | 105k | rcidm_tick(rcidm); |
559 | 105k | } |
560 | | |
561 | | void ossl_quic_rcidm_on_packet_sent(QUIC_RCIDM *rcidm, uint64_t num_packets) |
562 | 565k | { |
563 | 565k | if (num_packets == 0) |
564 | 19.4k | return; |
565 | | |
566 | 545k | rcidm->packets_sent += num_packets; |
567 | 545k | rcidm_tick(rcidm); |
568 | 545k | } |
569 | | |
570 | | void ossl_quic_rcidm_request_roll(QUIC_RCIDM *rcidm) |
571 | 2.82M | { |
572 | 2.82M | rcidm->roll_requested = 1; |
573 | 2.82M | rcidm_tick(rcidm); |
574 | 2.82M | } |
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 | 346k | { |
583 | 346k | RCID *rcid_obj; |
584 | | |
585 | 346k | if (rcidm->added_initial_rcid || rcidm->handshake_complete) |
586 | 244k | return 0; |
587 | | |
588 | 102k | rcid_obj = rcidm_create_rcid(rcidm, INITIAL_SEQ_NUM, |
589 | 102k | rcid, RCID_TYPE_INITIAL); |
590 | 102k | if (rcid_obj == NULL) |
591 | 14.4k | return 0; |
592 | | |
593 | 87.9k | rcidm->added_initial_rcid = 1; |
594 | 87.9k | rcidm_tick(rcidm); |
595 | 87.9k | return 1; |
596 | 102k | } |
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 | 125k | return 0; |
603 | | |
604 | 177k | rcidm->retry_odcid = *retry_odcid; |
605 | 177k | rcidm->added_retry_odcid = 1; |
606 | 177k | rcidm_tick(rcidm); |
607 | 177k | 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.80M | { |
613 | 4.80M | RCID *rcid; |
614 | | |
615 | 4.80M | rcid = rcidm_create_rcid(rcidm, ncid->seq_num, &ncid->conn_id, RCID_TYPE_NCID); |
616 | 4.80M | if (rcid == NULL) |
617 | 98.3k | return 0; |
618 | | |
619 | 4.70M | rcidm_handle_retire_prior_to(rcidm, ncid->retire_prior_to); |
620 | 4.70M | rcidm_tick(rcidm); |
621 | 4.70M | return 1; |
622 | 4.80M | } |
623 | | |
624 | | /* |
625 | | * Queries |
626 | | * ======= |
627 | | */ |
628 | | |
629 | | static int rcidm_get_retire(QUIC_RCIDM *rcidm, uint64_t *seq_num, int peek) |
630 | 166k | { |
631 | 166k | RCID *rcid = ossl_list_retiring_head(&rcidm->retiring_list); |
632 | | |
633 | 166k | if (rcid == NULL) |
634 | 104k | return 0; |
635 | | |
636 | 61.2k | if (seq_num != NULL) |
637 | 61.2k | *seq_num = rcid->seq_num; |
638 | | |
639 | 61.2k | if (!peek) |
640 | 35.0k | rcidm_free_rcid(rcidm, rcid); |
641 | | |
642 | 61.2k | return 1; |
643 | 166k | } |
644 | | |
645 | | int ossl_quic_rcidm_pop_retire_seq_num(QUIC_RCIDM *rcidm, |
646 | | uint64_t *seq_num) |
647 | 111k | { |
648 | 111k | return rcidm_get_retire(rcidm, seq_num, /*peek=*/0); |
649 | 111k | } |
650 | | |
651 | | int ossl_quic_rcidm_peek_retire_seq_num(QUIC_RCIDM *rcidm, |
652 | | uint64_t *seq_num) |
653 | 54.5k | { |
654 | 54.5k | return rcidm_get_retire(rcidm, seq_num, /*peek=*/1); |
655 | 54.5k | } |
656 | | |
657 | | int ossl_quic_rcidm_get_preferred_tx_dcid(QUIC_RCIDM *rcidm, |
658 | | QUIC_CONN_ID *tx_dcid) |
659 | 28.4k | { |
660 | 28.4k | if (!rcidm->have_preferred_rcid) |
661 | 19.2k | return 0; |
662 | | |
663 | 9.18k | *tx_dcid = rcidm->preferred_rcid; |
664 | 9.18k | return 1; |
665 | 28.4k | } |
666 | | |
667 | | int ossl_quic_rcidm_get_preferred_tx_dcid_changed(QUIC_RCIDM *rcidm, |
668 | | int clear) |
669 | 318k | { |
670 | 318k | int r = rcidm->preferred_rcid_changed; |
671 | | |
672 | 318k | if (clear) |
673 | 305k | rcidm->preferred_rcid_changed = 0; |
674 | | |
675 | 318k | return r; |
676 | 318k | } |
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 | } |