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