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 | #define PACKETS_PER_RCID 10000
|
---|
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 | *
|
---|
91 | * RETIRING
|
---|
92 | * Invariants:
|
---|
93 | * rcid->state == RCID_STATE_RETIRING;
|
---|
94 | * rcid->pq_idx == SIZE_MAX (debug assert only);
|
---|
95 | * the RCID is not the current RCID, rcidm->cur_rcid != rcid;
|
---|
96 | * the RCID is not in the priority queue;
|
---|
97 | * the RCID is in the retiring_list.
|
---|
98 | *
|
---|
99 | * Invariant: At most one RCID object is in the CURRENT state at any one time.
|
---|
100 | *
|
---|
101 | * (If no RCID object is in the CURRENT state, this means either
|
---|
102 | * an unnumbered RCID is being used as the preferred RCID
|
---|
103 | * or we currently have no preferred RCID.)
|
---|
104 | *
|
---|
105 | * All of the above states can be considered substates of the 'ACTIVE' state
|
---|
106 | * for an RCID as specified in RFC 9000. A CID only ceases to be active
|
---|
107 | * when we send a RETIRE_CONN_ID frame, which is the responsibility of the
|
---|
108 | * user of the RCIDM and happens after the above state machine is terminated.
|
---|
109 | */
|
---|
110 | enum {
|
---|
111 | RCID_STATE_PENDING,
|
---|
112 | RCID_STATE_CUR,
|
---|
113 | RCID_STATE_RETIRING
|
---|
114 | };
|
---|
115 |
|
---|
116 | enum {
|
---|
117 | RCID_TYPE_INITIAL, /* CID is from an peer INITIAL packet (seq 0) */
|
---|
118 | RCID_TYPE_PREF_ADDR, /* CID is from a preferred_address TPARAM (seq 1) */
|
---|
119 | RCID_TYPE_NCID /* CID is from a NCID frame */
|
---|
120 | /*
|
---|
121 | * INITIAL_ODCID and RETRY_ODCID also conceptually exist but are tracked
|
---|
122 | * separately.
|
---|
123 | */
|
---|
124 | };
|
---|
125 |
|
---|
126 | typedef struct rcid_st {
|
---|
127 | OSSL_LIST_MEMBER(retiring, struct rcid_st); /* valid iff RETIRING */
|
---|
128 |
|
---|
129 | QUIC_CONN_ID cid; /* The actual CID string for this RCID */
|
---|
130 | uint64_t seq_num;
|
---|
131 | size_t pq_idx; /* Index of entry into priority queue */
|
---|
132 | unsigned int state : 2; /* RCID_STATE_* */
|
---|
133 | unsigned int type : 2; /* RCID_TYPE_* */
|
---|
134 | } RCID;
|
---|
135 |
|
---|
136 | DEFINE_PRIORITY_QUEUE_OF(RCID);
|
---|
137 | DEFINE_LIST_OF(retiring, RCID);
|
---|
138 |
|
---|
139 | /*
|
---|
140 | * RCID Manager
|
---|
141 | * ============
|
---|
142 | *
|
---|
143 | * The following "business logic" invariants also apply to the RCIDM
|
---|
144 | * as a whole:
|
---|
145 | *
|
---|
146 | * Invariant: An RCID of INITIAL type has a sequence number of 0.
|
---|
147 | * Invariant: An RCID of PREF_ADDR type has a sequence number of 1.
|
---|
148 | *
|
---|
149 | * Invariant: There is never more than one Initial ODCID
|
---|
150 | * added throughout the lifetime of an RCIDM.
|
---|
151 | * Invariant: There is never more than one Retry ODCID
|
---|
152 | * added throughout the lifetime of an RCIDM.
|
---|
153 | * Invariant: There is never more than one INITIAL RCID created
|
---|
154 | * throughout the lifetime of an RCIDM.
|
---|
155 | * Invariant: There is never more than one PREF_ADDR RCID created
|
---|
156 | * throughout the lifetime of an RCIDM.
|
---|
157 | * Invariant: No INITIAL or PREF_ADDR RCID may be added after
|
---|
158 | * the handshake is completed.
|
---|
159 | *
|
---|
160 | */
|
---|
161 | struct quic_rcidm_st {
|
---|
162 | /*
|
---|
163 | * The current RCID we prefer to use (value undefined if
|
---|
164 | * !have_preferred_rcid).
|
---|
165 | *
|
---|
166 | * This is preferentially set to a numbered RCID (represented by an RCID
|
---|
167 | * object) if we have one (in which case preferred_rcid == cur_rcid->cid);
|
---|
168 | * otherwise it is set to one of the unnumbered RCIDs (the Initial ODCID or
|
---|
169 | * Retry ODCID) if available (and cur_rcid == NULL).
|
---|
170 | */
|
---|
171 | QUIC_CONN_ID preferred_rcid;
|
---|
172 |
|
---|
173 | /*
|
---|
174 | * These are initialized if the corresponding added_ flags are set.
|
---|
175 | */
|
---|
176 | QUIC_CONN_ID initial_odcid, retry_odcid;
|
---|
177 |
|
---|
178 | /*
|
---|
179 | * Total number of packets sent since we last made a packet count-based RCID
|
---|
180 | * update decision.
|
---|
181 | */
|
---|
182 | uint64_t packets_sent;
|
---|
183 |
|
---|
184 | /* Number of post-handshake RCID changes we have performed. */
|
---|
185 | uint64_t num_changes;
|
---|
186 |
|
---|
187 | /*
|
---|
188 | * The Retire Prior To watermark value; max(retire_prior_to) of all received
|
---|
189 | * NCID frames.
|
---|
190 | */
|
---|
191 | uint64_t retire_prior_to;
|
---|
192 |
|
---|
193 | /* (SORT BY seq_num ASC) -> (RCID *) */
|
---|
194 | PRIORITY_QUEUE_OF(RCID) *rcids;
|
---|
195 |
|
---|
196 | /*
|
---|
197 | * Current RCID object we are using. This may differ from the first item in
|
---|
198 | * the priority queue if we received NCID frames out of order. For example
|
---|
199 | * if we get seq 5, switch to it immediately, then get seq 4, we want to
|
---|
200 | * keep using seq 5 until we decide to roll again rather than immediately
|
---|
201 | * switch to seq 4. Never points to an object on the retiring_list.
|
---|
202 | */
|
---|
203 | RCID *cur_rcid;
|
---|
204 |
|
---|
205 | /*
|
---|
206 | * When a RCID becomes pending-retirement, it is moved to the retiring_list,
|
---|
207 | * then freed when it is popped from the retired queue. We use a list for
|
---|
208 | * this rather than a priority queue as the order in which items are freed
|
---|
209 | * does not matter. We always append to the tail of the list in order to
|
---|
210 | * maintain the guarantee that the head (if present) only changes when a
|
---|
211 | * caller calls pop().
|
---|
212 | */
|
---|
213 | OSSL_LIST(retiring) retiring_list;
|
---|
214 |
|
---|
215 | /* Number of entries on the retiring_list. */
|
---|
216 | size_t num_retiring;
|
---|
217 |
|
---|
218 | /* preferred_rcid has been changed? */
|
---|
219 | unsigned int preferred_rcid_changed : 1;
|
---|
220 |
|
---|
221 | /* Do we have any RCID we can use currently? */
|
---|
222 | unsigned int have_preferred_rcid : 1;
|
---|
223 |
|
---|
224 | /* QUIC handshake has been completed? */
|
---|
225 | unsigned int handshake_complete : 1;
|
---|
226 |
|
---|
227 | /* odcid was set (not necessarily still valid as a RCID)? */
|
---|
228 | unsigned int added_initial_odcid : 1;
|
---|
229 | /* retry_odcid was set (not necessarily still valid as a RCID?) */
|
---|
230 | unsigned int added_retry_odcid : 1;
|
---|
231 | /* An initial RCID was added as an RCID structure? */
|
---|
232 | unsigned int added_initial_rcid : 1;
|
---|
233 | /* Has a RCID roll been manually requested? */
|
---|
234 | unsigned int roll_requested : 1;
|
---|
235 | };
|
---|
236 |
|
---|
237 | /*
|
---|
238 | * Caller must periodically pop retired RCIDs and handle them. If the caller
|
---|
239 | * fails to do so, fail safely rather than start exhibiting integer rollover.
|
---|
240 | * Limit the total number of numbered RCIDs to an implausibly large but safe
|
---|
241 | * value.
|
---|
242 | */
|
---|
243 | #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 | {
|
---|
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);
|
---|
269 | assert(rcid->state != RCID_STATE_RETIRING || rcidm->num_retiring > 0);
|
---|
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 |
|
---|
347 | if (cid->id_len < 1 || cid->id_len > QUIC_MAX_CONN_ID_LEN
|
---|
348 | || seq_num > OSSL_QUIC_VLINT_MAX
|
---|
349 | || ossl_pqueue_RCID_num(rcidm->rcids) + rcidm->num_retiring
|
---|
350 | > MAX_NUMBERED_RCIDS)
|
---|
351 | return NULL;
|
---|
352 |
|
---|
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;
|
---|
359 |
|
---|
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)) {
|
---|
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);
|
---|
372 | ++rcidm->num_retiring;
|
---|
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);
|
---|
408 | ++rcidm->num_retiring;
|
---|
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);
|
---|
430 | --rcidm->num_retiring;
|
---|
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 |
|
---|
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);
|
---|
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 | 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);
|
---|
462 |
|
---|
463 | rcidm->retire_prior_to = retire_prior_to;
|
---|
464 | }
|
---|
465 |
|
---|
466 | /*
|
---|
467 | * Decision Logic
|
---|
468 | * ==============
|
---|
469 | */
|
---|
470 |
|
---|
471 | static void rcidm_roll(QUIC_RCIDM *rcidm)
|
---|
472 | {
|
---|
473 | RCID *rcid;
|
---|
474 |
|
---|
475 | if ((rcid = ossl_pqueue_RCID_peek(rcidm->rcids)) == NULL)
|
---|
476 | return;
|
---|
477 |
|
---|
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 | {
|
---|
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 |
|
---|
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 | */
|
---|
513 | if (rcidm->added_retry_odcid && !rcidm->handshake_complete) {
|
---|
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 |
|
---|
615 | rcid = rcidm_create_rcid(rcidm, ncid->seq_num, &ncid->conn_id, RCID_TYPE_NCID);
|
---|
616 | if (rcid == NULL)
|
---|
617 | return 0;
|
---|
618 |
|
---|
619 | rcidm_handle_retire_prior_to(rcidm, ncid->retire_prior_to);
|
---|
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 | }
|
---|
677 |
|
---|
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 | }
|
---|