/src/openssl33/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 | } |