]>
Commit | Line | Data |
---|---|---|
63f77f04 HL |
1 | /* |
2 | * Copyright 2023 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 | ||
9eabb30a | 40 | #define PACKETS_PER_RCID 10000 |
63f77f04 HL |
41 | |
42 | #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 | * | |
3fe0899a | 91 | * RETIRING |
63f77f04 | 92 | * Invariants: |
3fe0899a | 93 | * rcid->state == RCID_STATE_RETIRING; |
63f77f04 HL |
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; | |
3fe0899a | 97 | * the RCID is in the retiring_list. |
63f77f04 HL |
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.) | |
3fe0899a HL |
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. | |
63f77f04 HL |
109 | */ |
110 | enum { | |
111 | RCID_STATE_PENDING, | |
112 | RCID_STATE_CUR, | |
3ba9345e | 113 | RCID_STATE_RETIRING |
63f77f04 HL |
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) */ | |
3ba9345e | 119 | RCID_TYPE_NCID /* CID is from a NCID frame */ |
63f77f04 HL |
120 | /* |
121 | * INITIAL_ODCID and RETRY_ODCID also conceptually exist but are tracked | |
122 | * separately. | |
123 | */ | |
124 | }; | |
125 | ||
126 | typedef struct rcid_st { | |
9eabb30a | 127 | OSSL_LIST_MEMBER(retiring, struct rcid_st); /* valid iff RETIRING */ |
63f77f04 HL |
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). | |
9eabb30a HL |
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). | |
63f77f04 HL |
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 | /* | |
9eabb30a HL |
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. | |
63f77f04 HL |
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 | ||
9575b218 HL |
215 | /* Number of entries on the retiring_list. */ |
216 | size_t num_retiring; | |
217 | ||
63f77f04 HL |
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 | ||
9eabb30a HL |
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 | #define MAX_NUMBERED_RCIDS (SIZE_MAX / 2) | |
244 | ||
3fe0899a HL |
245 | static void rcidm_transition_rcid(QUIC_RCIDM *rcidm, RCID *rcid, |
246 | unsigned int state); | |
247 | ||
63f77f04 HL |
248 | /* Check invariants of an RCID */ |
249 | static void rcidm_check_rcid(QUIC_RCIDM *rcidm, RCID *rcid) | |
250 | { | |
251 | assert(rcid->state == RCID_STATE_PENDING | |
252 | || rcid->state == RCID_STATE_CUR | |
253 | || rcid->state == RCID_STATE_RETIRING); | |
254 | assert((rcid->state == RCID_STATE_PENDING) | |
255 | == (rcid->pq_idx != SIZE_MAX)); | |
256 | assert((rcid->state == RCID_STATE_CUR) | |
257 | == (rcidm->cur_rcid == rcid)); | |
258 | assert((ossl_list_retiring_next(rcid) != NULL | |
259 | || ossl_list_retiring_prev(rcid) != NULL | |
260 | || ossl_list_retiring_head(&rcidm->retiring_list) == rcid) | |
261 | == (rcid->state == RCID_STATE_RETIRING)); | |
262 | assert(rcid->type != RCID_TYPE_INITIAL || rcid->seq_num == 0); | |
263 | assert(rcid->type != RCID_TYPE_PREF_ADDR || rcid->seq_num == 1); | |
264 | assert(rcid->seq_num <= OSSL_QUIC_VLINT_MAX); | |
265 | assert(rcid->cid.id_len > 0 && rcid->cid.id_len <= QUIC_MAX_CONN_ID_LEN); | |
266 | assert(rcid->seq_num >= rcidm->retire_prior_to | |
267 | || rcid->state == RCID_STATE_RETIRING); | |
268 | assert(rcidm->num_changes == 0 || rcidm->handshake_complete); | |
9575b218 | 269 | assert(rcid->state != RCID_STATE_RETIRING || rcidm->num_retiring > 0); |
63f77f04 HL |
270 | } |
271 | ||
272 | static int rcid_cmp(const RCID *a, const RCID *b) | |
273 | { | |
274 | if (a->seq_num < b->seq_num) | |
275 | return -1; | |
276 | if (a->seq_num > b->seq_num) | |
277 | return 1; | |
278 | return 0; | |
279 | } | |
280 | ||
281 | QUIC_RCIDM *ossl_quic_rcidm_new(const QUIC_CONN_ID *initial_odcid) | |
282 | { | |
283 | QUIC_RCIDM *rcidm; | |
284 | ||
285 | if ((rcidm = OPENSSL_zalloc(sizeof(*rcidm))) == NULL) | |
286 | return NULL; | |
287 | ||
288 | if ((rcidm->rcids = ossl_pqueue_RCID_new(rcid_cmp)) == NULL) { | |
289 | OPENSSL_free(rcidm); | |
290 | return NULL; | |
291 | } | |
292 | ||
293 | if (initial_odcid != NULL) { | |
294 | rcidm->initial_odcid = *initial_odcid; | |
295 | rcidm->added_initial_odcid = 1; | |
296 | } | |
297 | ||
298 | rcidm_update(rcidm); | |
299 | return rcidm; | |
300 | } | |
301 | ||
302 | void ossl_quic_rcidm_free(QUIC_RCIDM *rcidm) | |
303 | { | |
304 | RCID *rcid, *rnext; | |
305 | ||
306 | if (rcidm == NULL) | |
307 | return; | |
308 | ||
309 | OPENSSL_free(rcidm->cur_rcid); | |
310 | while ((rcid = ossl_pqueue_RCID_pop(rcidm->rcids)) != NULL) | |
311 | OPENSSL_free(rcid); | |
312 | ||
313 | LIST_FOREACH_DELSAFE(rcid, rnext, retiring, &rcidm->retiring_list) | |
314 | OPENSSL_free(rcid); | |
315 | ||
316 | ossl_pqueue_RCID_free(rcidm->rcids); | |
317 | OPENSSL_free(rcidm); | |
318 | } | |
319 | ||
320 | static void rcidm_set_preferred_rcid(QUIC_RCIDM *rcidm, | |
321 | const QUIC_CONN_ID *rcid) | |
322 | { | |
323 | if (rcid == NULL) { | |
324 | rcidm->preferred_rcid_changed = 1; | |
325 | rcidm->have_preferred_rcid = 0; | |
326 | return; | |
327 | } | |
328 | ||
329 | if (ossl_quic_conn_id_eq(&rcidm->preferred_rcid, rcid)) | |
330 | return; | |
331 | ||
332 | rcidm->preferred_rcid = *rcid; | |
333 | rcidm->preferred_rcid_changed = 1; | |
334 | rcidm->have_preferred_rcid = 1; | |
335 | } | |
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 | { | |
345 | RCID *rcid; | |
346 | ||
3fe0899a | 347 | if (cid->id_len < 1 || cid->id_len > QUIC_MAX_CONN_ID_LEN |
9eabb30a HL |
348 | || seq_num > OSSL_QUIC_VLINT_MAX |
349 | || ossl_pqueue_RCID_num(rcidm->rcids) + rcidm->num_retiring | |
350 | > MAX_NUMBERED_RCIDS) | |
3fe0899a HL |
351 | return NULL; |
352 | ||
63f77f04 HL |
353 | if ((rcid = OPENSSL_zalloc(sizeof(*rcid))) == NULL) |
354 | return NULL; | |
355 | ||
356 | rcid->seq_num = seq_num; | |
357 | rcid->cid = *cid; | |
358 | rcid->type = type; | |
63f77f04 | 359 | |
3fe0899a HL |
360 | if (rcid->seq_num >= rcidm->retire_prior_to) { |
361 | rcid->state = RCID_STATE_PENDING; | |
362 | ||
363 | if (!ossl_pqueue_RCID_push(rcidm->rcids, rcid, &rcid->pq_idx)) { | |
3fe0899a HL |
364 | OPENSSL_free(rcid); |
365 | return NULL; | |
366 | } | |
367 | } else { | |
368 | /* RCID is immediately retired upon creation. */ | |
369 | rcid->state = RCID_STATE_RETIRING; | |
370 | rcid->pq_idx = SIZE_MAX; | |
371 | ossl_list_retiring_insert_tail(&rcidm->retiring_list, rcid); | |
9575b218 | 372 | ++rcidm->num_retiring; |
63f77f04 HL |
373 | } |
374 | ||
375 | rcidm_check_rcid(rcidm, rcid); | |
376 | return rcid; | |
377 | } | |
378 | ||
379 | static void rcidm_transition_rcid(QUIC_RCIDM *rcidm, RCID *rcid, | |
380 | unsigned int state) | |
381 | { | |
382 | unsigned int old_state = rcid->state; | |
383 | ||
384 | assert(state >= old_state && state <= RCID_STATE_RETIRING); | |
385 | rcidm_check_rcid(rcidm, rcid); | |
386 | if (state == old_state) | |
387 | return; | |
388 | ||
389 | if (rcidm->cur_rcid != NULL && state == RCID_STATE_CUR) { | |
390 | rcidm_transition_rcid(rcidm, rcidm->cur_rcid, RCID_STATE_RETIRING); | |
391 | assert(rcidm->cur_rcid == NULL); | |
392 | } | |
393 | ||
394 | if (old_state == RCID_STATE_PENDING) { | |
395 | ossl_pqueue_RCID_remove(rcidm->rcids, rcid->pq_idx); | |
396 | rcid->pq_idx = SIZE_MAX; | |
397 | } | |
398 | ||
399 | rcid->state = state; | |
400 | ||
401 | if (state == RCID_STATE_CUR) { | |
402 | rcidm->cur_rcid = rcid; | |
403 | } else if (state == RCID_STATE_RETIRING) { | |
404 | if (old_state == RCID_STATE_CUR) | |
405 | rcidm->cur_rcid = NULL; | |
406 | ||
407 | ossl_list_retiring_insert_tail(&rcidm->retiring_list, rcid); | |
9575b218 | 408 | ++rcidm->num_retiring; |
63f77f04 HL |
409 | } |
410 | ||
411 | rcidm_check_rcid(rcidm, rcid); | |
412 | } | |
413 | ||
414 | static void rcidm_free_rcid(QUIC_RCIDM *rcidm, RCID *rcid) | |
415 | { | |
416 | if (rcid == NULL) | |
417 | return; | |
418 | ||
419 | rcidm_check_rcid(rcidm, rcid); | |
420 | ||
421 | switch (rcid->state) { | |
422 | case RCID_STATE_PENDING: | |
423 | ossl_pqueue_RCID_remove(rcidm->rcids, rcid->pq_idx); | |
424 | break; | |
425 | case RCID_STATE_CUR: | |
426 | rcidm->cur_rcid = NULL; | |
427 | break; | |
428 | case RCID_STATE_RETIRING: | |
429 | ossl_list_retiring_remove(&rcidm->retiring_list, rcid); | |
9575b218 | 430 | --rcidm->num_retiring; |
63f77f04 HL |
431 | break; |
432 | default: | |
433 | assert(0); | |
434 | break; | |
435 | } | |
436 | ||
437 | OPENSSL_free(rcid); | |
438 | } | |
439 | ||
440 | static void rcidm_handle_retire_prior_to(QUIC_RCIDM *rcidm, | |
441 | uint64_t retire_prior_to) | |
442 | { | |
443 | RCID *rcid; | |
444 | ||
445 | if (retire_prior_to <= rcidm->retire_prior_to) | |
446 | return; | |
447 | ||
3fe0899a HL |
448 | /* |
449 | * Retire the current RCID (if any) if it is affected. | |
450 | */ | |
451 | if (rcidm->cur_rcid != NULL && rcidm->cur_rcid->seq_num < retire_prior_to) | |
452 | rcidm_transition_rcid(rcidm, rcidm->cur_rcid, RCID_STATE_RETIRING); | |
63f77f04 HL |
453 | |
454 | /* | |
3fe0899a HL |
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 | |
63f77f04 HL |
457 | * threshold. |
458 | */ | |
459 | while ((rcid = ossl_pqueue_RCID_peek(rcidm->rcids)) != NULL | |
460 | && rcid->seq_num < retire_prior_to) | |
461 | rcidm_transition_rcid(rcidm, rcid, RCID_STATE_RETIRING); | |
3fe0899a HL |
462 | |
463 | rcidm->retire_prior_to = retire_prior_to; | |
63f77f04 HL |
464 | } |
465 | ||
466 | /* | |
467 | * Decision Logic | |
468 | * ============== | |
469 | */ | |
470 | ||
471 | static void rcidm_roll(QUIC_RCIDM *rcidm) | |
472 | { | |
473 | RCID *rcid; | |
474 | ||
3fe0899a | 475 | if ((rcid = ossl_pqueue_RCID_peek(rcidm->rcids)) == NULL) |
63f77f04 HL |
476 | return; |
477 | ||
63f77f04 HL |
478 | rcidm_transition_rcid(rcidm, rcid, RCID_STATE_CUR); |
479 | ||
480 | ++rcidm->num_changes; | |
481 | rcidm->roll_requested = 0; | |
482 | ||
483 | if (rcidm->packets_sent >= PACKETS_PER_RCID) | |
484 | rcidm->packets_sent %= PACKETS_PER_RCID; | |
485 | else | |
486 | rcidm->packets_sent = 0; | |
487 | } | |
488 | ||
489 | static void rcidm_update(QUIC_RCIDM *rcidm) | |
490 | { | |
3fe0899a HL |
491 | RCID *rcid; |
492 | ||
493 | /* | |
494 | * If we have no current numbered RCID but have one or more pending, use it. | |
495 | */ | |
496 | if (rcidm->cur_rcid == NULL | |
497 | && (rcid = ossl_pqueue_RCID_peek(rcidm->rcids)) != NULL) { | |
498 | rcidm_transition_rcid(rcidm, rcid, RCID_STATE_CUR); | |
499 | assert(rcidm->cur_rcid != NULL); | |
500 | } | |
501 | ||
63f77f04 HL |
502 | /* Prefer use of any current numbered RCID we have, if possible. */ |
503 | if (rcidm->cur_rcid != NULL) { | |
504 | rcidm_check_rcid(rcidm, rcidm->cur_rcid); | |
505 | rcidm_set_preferred_rcid(rcidm, &rcidm->cur_rcid->cid); | |
506 | return; | |
507 | } | |
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 | */ | |
9eabb30a | 513 | if (rcidm->added_retry_odcid && !rcidm->handshake_complete) { |
63f77f04 HL |
514 | rcidm_set_preferred_rcid(rcidm, &rcidm->retry_odcid); |
515 | return; | |
516 | } | |
517 | ||
518 | if (rcidm->added_initial_odcid && !rcidm->handshake_complete) { | |
519 | rcidm_set_preferred_rcid(rcidm, &rcidm->initial_odcid); | |
520 | return; | |
521 | } | |
522 | ||
523 | /* We don't know of any usable RCIDs */ | |
524 | rcidm_set_preferred_rcid(rcidm, NULL); | |
525 | } | |
526 | ||
527 | static int rcidm_should_roll(QUIC_RCIDM *rcidm) | |
528 | { | |
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 | return rcidm->handshake_complete | |
535 | && (rcidm->num_changes == 0 | |
536 | || rcidm->packets_sent >= PACKETS_PER_RCID | |
537 | || rcidm->roll_requested); | |
538 | } | |
539 | ||
540 | static void rcidm_tick(QUIC_RCIDM *rcidm) | |
541 | { | |
542 | if (rcidm_should_roll(rcidm)) | |
543 | rcidm_roll(rcidm); | |
544 | ||
545 | rcidm_update(rcidm); | |
546 | } | |
547 | ||
548 | /* | |
549 | * Events | |
550 | * ====== | |
551 | */ | |
552 | void ossl_quic_rcidm_on_handshake_complete(QUIC_RCIDM *rcidm) | |
553 | { | |
554 | if (rcidm->handshake_complete) | |
555 | return; | |
556 | ||
557 | rcidm->handshake_complete = 1; | |
558 | rcidm_tick(rcidm); | |
559 | } | |
560 | ||
561 | void ossl_quic_rcidm_on_packet_sent(QUIC_RCIDM *rcidm, uint64_t num_packets) | |
562 | { | |
563 | if (num_packets == 0) | |
564 | return; | |
565 | ||
566 | rcidm->packets_sent += num_packets; | |
567 | rcidm_tick(rcidm); | |
568 | } | |
569 | ||
570 | void ossl_quic_rcidm_request_roll(QUIC_RCIDM *rcidm) | |
571 | { | |
572 | rcidm->roll_requested = 1; | |
573 | rcidm_tick(rcidm); | |
574 | } | |
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 | { | |
583 | RCID *rcid_obj; | |
584 | ||
585 | if (rcidm->added_initial_rcid || rcidm->handshake_complete) | |
586 | return 0; | |
587 | ||
588 | rcid_obj = rcidm_create_rcid(rcidm, INITIAL_SEQ_NUM, | |
589 | rcid, RCID_TYPE_INITIAL); | |
590 | if (rcid_obj == NULL) | |
591 | return 0; | |
592 | ||
593 | rcidm->added_initial_rcid = 1; | |
594 | rcidm_tick(rcidm); | |
595 | return 1; | |
596 | } | |
597 | ||
598 | int ossl_quic_rcidm_add_from_server_retry(QUIC_RCIDM *rcidm, | |
599 | const QUIC_CONN_ID *retry_odcid) | |
600 | { | |
601 | if (rcidm->added_retry_odcid || rcidm->handshake_complete) | |
602 | return 0; | |
603 | ||
604 | rcidm->retry_odcid = *retry_odcid; | |
605 | rcidm->added_retry_odcid = 1; | |
606 | rcidm_tick(rcidm); | |
607 | return 1; | |
608 | } | |
609 | ||
610 | int ossl_quic_rcidm_add_from_ncid(QUIC_RCIDM *rcidm, | |
611 | const OSSL_QUIC_FRAME_NEW_CONN_ID *ncid) | |
612 | { | |
613 | RCID *rcid; | |
614 | ||
63f77f04 HL |
615 | rcid = rcidm_create_rcid(rcidm, ncid->seq_num, &ncid->conn_id, RCID_TYPE_NCID); |
616 | if (rcid == NULL) | |
617 | return 0; | |
618 | ||
3fe0899a | 619 | rcidm_handle_retire_prior_to(rcidm, ncid->retire_prior_to); |
63f77f04 HL |
620 | rcidm_tick(rcidm); |
621 | return 1; | |
622 | } | |
623 | ||
624 | /* | |
625 | * Queries | |
626 | * ======= | |
627 | */ | |
628 | ||
629 | static int rcidm_get_retire(QUIC_RCIDM *rcidm, uint64_t *seq_num, int peek) | |
630 | { | |
631 | RCID *rcid = ossl_list_retiring_head(&rcidm->retiring_list); | |
632 | ||
633 | if (rcid == NULL) | |
634 | return 0; | |
635 | ||
636 | if (seq_num != NULL) | |
637 | *seq_num = rcid->seq_num; | |
638 | ||
639 | if (!peek) | |
640 | rcidm_free_rcid(rcidm, rcid); | |
641 | ||
642 | return 1; | |
643 | } | |
644 | ||
645 | int ossl_quic_rcidm_pop_retire_seq_num(QUIC_RCIDM *rcidm, | |
646 | uint64_t *seq_num) | |
647 | { | |
648 | return rcidm_get_retire(rcidm, seq_num, /*peek=*/0); | |
649 | } | |
650 | ||
651 | int ossl_quic_rcidm_peek_retire_seq_num(QUIC_RCIDM *rcidm, | |
652 | uint64_t *seq_num) | |
653 | { | |
654 | return rcidm_get_retire(rcidm, seq_num, /*peek=*/1); | |
655 | } | |
656 | ||
657 | int ossl_quic_rcidm_get_preferred_tx_dcid(QUIC_RCIDM *rcidm, | |
658 | QUIC_CONN_ID *tx_dcid) | |
659 | { | |
660 | if (!rcidm->have_preferred_rcid) | |
661 | return 0; | |
662 | ||
663 | *tx_dcid = rcidm->preferred_rcid; | |
664 | return 1; | |
665 | } | |
666 | ||
667 | int ossl_quic_rcidm_get_preferred_tx_dcid_changed(QUIC_RCIDM *rcidm, | |
668 | int clear) | |
669 | { | |
670 | int r = rcidm->preferred_rcid_changed; | |
671 | ||
672 | if (clear) | |
673 | rcidm->preferred_rcid_changed = 0; | |
674 | ||
675 | return r; | |
676 | } | |
3fe0899a | 677 | |
9575b218 HL |
678 | size_t ossl_quic_rcidm_get_num_active(const QUIC_RCIDM *rcidm) |
679 | { | |
680 | return ossl_pqueue_RCID_num(rcidm->rcids) | |
681 | + (rcidm->cur_rcid != NULL ? 1 : 0) | |
682 | + ossl_quic_rcidm_get_num_retiring(rcidm); | |
683 | } | |
684 | ||
685 | size_t ossl_quic_rcidm_get_num_retiring(const QUIC_RCIDM *rcidm) | |
686 | { | |
687 | return rcidm->num_retiring; | |
688 | } |