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