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 | #ifndef OSSL_INTERNAL_QUIC_LCIDM_H
|
---|
11 | # define OSSL_INTERNAL_QUIC_LCIDM_H
|
---|
12 | # pragma once
|
---|
13 |
|
---|
14 | # include "internal/e_os.h"
|
---|
15 | # include "internal/time.h"
|
---|
16 | # include "internal/quic_types.h"
|
---|
17 | # include "internal/quic_wire.h"
|
---|
18 | # include "internal/quic_predef.h"
|
---|
19 |
|
---|
20 | # ifndef OPENSSL_NO_QUIC
|
---|
21 |
|
---|
22 | /*
|
---|
23 | * QUIC Local Connection ID Manager
|
---|
24 | * ================================
|
---|
25 | *
|
---|
26 | * This manages connection IDs for the RX side, which is to say that it issues
|
---|
27 | * local CIDs (LCIDs) to a peer which that peer can then use to address us via a
|
---|
28 | * packet DCID. This is as opposed to CID management for the TX side, which
|
---|
29 | * determines which CIDs we use to transmit based on remote CIDs (RCIDs) the
|
---|
30 | * peer sent to us.
|
---|
31 | *
|
---|
32 | * An opaque pointer can be associated with each LCID. Pointer identity
|
---|
33 | * (equality) is used to distinguish distinct connections.
|
---|
34 | *
|
---|
35 | * LCIDs fall into three categories:
|
---|
36 | *
|
---|
37 | * 1. A client's Initial ODCID (1)
|
---|
38 | * 2. Our local Initial SCID (1)
|
---|
39 | * 3. A CID issued via a NEW_CONNECTION_ID frame (n)
|
---|
40 | * 4. A server's Retry SCID (0..1)
|
---|
41 | *
|
---|
42 | * (1) is enrolled using ossl_quic_lcidm_enrol_odcid() and retired by the time
|
---|
43 | * of handshake completion at the latest. It is needed in case the first
|
---|
44 | * response packet from a server is lost and the client keeps using its Initial
|
---|
45 | * ODCID. There is never more than one of these, and no sequence number is
|
---|
46 | * associated with this temporary LCID.
|
---|
47 | *
|
---|
48 | * (2) is created by a client when it begins connecting, or by a server when it
|
---|
49 | * responds to a new connection request. In the latter case, it is generated by
|
---|
50 | * the server as the preferred DCID for traffic directed towards it. A client
|
---|
51 | * should switch to using this as a RCID as soon as it receives a valid packet
|
---|
52 | * from the server. This LCID has a sequence number of 0.
|
---|
53 | *
|
---|
54 | * (3) is created when we issue a NEW_CONNECTION_ID frame. Arbitrarily many of
|
---|
55 | * these can exist.
|
---|
56 | *
|
---|
57 | * (4) is a special case. When a server issues a retry it generates a new SCID
|
---|
58 | * much as it does for (2). However since retries are supposed to be stateless,
|
---|
59 | * we don't actually register it as an LCID. When the client subsequently
|
---|
60 | * replies with an Initial packet with token in response to the Retry, the
|
---|
61 | * server will handle this as a new connection attempt due to not recognising
|
---|
62 | * the DCID, which is what we want anyway. (The Retry SCID is subsequently
|
---|
63 | * validated as matching the new Initial ODCID via attestation in the encrypted
|
---|
64 | * contents of the opaque retry token.) Thus, the LCIDM is not actually involved
|
---|
65 | * at all here.
|
---|
66 | *
|
---|
67 | * Retirement is as follows:
|
---|
68 | *
|
---|
69 | * (1) is retired automatically when we know it won't be needed anymore. This is
|
---|
70 | * when the handshake is completed at the latest, and could potentially be
|
---|
71 | * earlier.
|
---|
72 | *
|
---|
73 | * Both (2) and (3) are retired normally via RETIRE_CONNECTION_ID frames, as it
|
---|
74 | * has a sequence number of 0.
|
---|
75 | *
|
---|
76 | *
|
---|
77 | * ODCID Peculiarities
|
---|
78 | * -------------------
|
---|
79 | *
|
---|
80 | * Almost all LCIDs are issued by the receiver responsible for routing them,
|
---|
81 | * which means that almost all LCIDs will have the same length (specified in
|
---|
82 | * lcid_len below). The only exception to this is (1); the ODCID is the only
|
---|
83 | * case where we recognise an LCID we didn't ourselves generate. Since an ODCID
|
---|
84 | * is chosen by the peer, it can be any length and doesn't necessarily match the
|
---|
85 | * length we use for LCIDs we generate ourselves.
|
---|
86 | *
|
---|
87 | * Since DCID decoding for short-header packets requires an implicitly known
|
---|
88 | * DCID length, it logically follows that an ODCID can never be used in a 1-RTT
|
---|
89 | * packet. This is fine as by the time the 1-RTT EL is reached the peer should
|
---|
90 | * already have switched away from the ODCID to a CID we generated ourselves,
|
---|
91 | * and if this has not happened we can consider that a protocol violation.
|
---|
92 | *
|
---|
93 | * In any case, this means that the LCIDM must necessarily support LCIDs of
|
---|
94 | * different lengths, even if it always generates LCIDs of a given length.
|
---|
95 | *
|
---|
96 | * An ODCID has no sequence number associated with it. It is the only CID to
|
---|
97 | * lack one.
|
---|
98 | */
|
---|
99 |
|
---|
100 | /*
|
---|
101 | * Creates a new LCIDM. lcid_len is the length to use for LCIDs in bytes, which
|
---|
102 | * may be zero.
|
---|
103 | *
|
---|
104 | * Returns NULL on failure.
|
---|
105 | */
|
---|
106 | QUIC_LCIDM *ossl_quic_lcidm_new(OSSL_LIB_CTX *libctx, size_t lcid_len);
|
---|
107 |
|
---|
108 | /* Frees a LCIDM. */
|
---|
109 | void ossl_quic_lcidm_free(QUIC_LCIDM *lcidm);
|
---|
110 |
|
---|
111 | /* Gets the local CID length this LCIDM was configured to use. */
|
---|
112 | size_t ossl_quic_lcidm_get_lcid_len(const QUIC_LCIDM *lcidm);
|
---|
113 |
|
---|
114 | /*
|
---|
115 | * Determines the number of active LCIDs (i.e,. LCIDs which can be used for
|
---|
116 | * reception) currently associated with the given opaque pointer.
|
---|
117 | */
|
---|
118 | size_t ossl_quic_lcidm_get_num_active_lcid(const QUIC_LCIDM *lcidm,
|
---|
119 | void *opaque);
|
---|
120 |
|
---|
121 | /*
|
---|
122 | * Enrol an Initial ODCID sent by the peer. This is the DCID in the first
|
---|
123 | * Initial packet sent by a client. When we receive a client's first Initial
|
---|
124 | * packet, we immediately respond with our own SCID (generated using
|
---|
125 | * ossl_quic_lcidm_generate_initial) to tell the client to switch to using that,
|
---|
126 | * so ideally the ODCID will only be used for a single packet. However since
|
---|
127 | * that response might be lost, we also need to accept additional packets using
|
---|
128 | * the ODCID and need to make sure they get routed to the same connection and
|
---|
129 | * not interpreted as another new connection attempt. Thus before the CID
|
---|
130 | * switchover is confirmed, we also have to handle incoming packets addressed to
|
---|
131 | * the ODCID. This function is used to temporarily enroll the ODCID for a
|
---|
132 | * connection. Such a LCID is considered to have a sequence number of
|
---|
133 | * LCIDM_ODCID_SEQ_NUM internally for our purposes.
|
---|
134 | *
|
---|
135 | * Note that this is the *only* circumstance where we recognise an LCID we did
|
---|
136 | * not generate ourselves, or allow an LCID with a different length to lcid_len.
|
---|
137 | *
|
---|
138 | * An ODCID MUST be at least 8 bytes in length (RFC 9000 s. 7.2).
|
---|
139 | *
|
---|
140 | * This function may only be called once for a given connection.
|
---|
141 | * Returns 1 on success or 0 on failure.
|
---|
142 | */
|
---|
143 | int ossl_quic_lcidm_enrol_odcid(QUIC_LCIDM *lcidm, void *opaque,
|
---|
144 | const QUIC_CONN_ID *initial_odcid);
|
---|
145 |
|
---|
146 | /*
|
---|
147 | * Retire a previously enrolled ODCID for a connection. This is generally done
|
---|
148 | * when we know the peer won't be using it any more (when the handshake is
|
---|
149 | * completed at the absolute latest, possibly earlier).
|
---|
150 | *
|
---|
151 | * Returns 1 if there was an enrolled ODCID which was retired and 0 if there was
|
---|
152 | * not or on other failure.
|
---|
153 | */
|
---|
154 | int ossl_quic_lcidm_retire_odcid(QUIC_LCIDM *lcidm, void *opaque);
|
---|
155 |
|
---|
156 | /*
|
---|
157 | * Create the first LCID for a given opaque pointer. The generated LCID is
|
---|
158 | * written to *initial_lcid and associated with the given opaque pointer.
|
---|
159 | *
|
---|
160 | * After this function returns successfully, the caller can for example
|
---|
161 | * register the new LCID with a DEMUX.
|
---|
162 | *
|
---|
163 | * May not be called more than once for a given opaque pointer value.
|
---|
164 | */
|
---|
165 | int ossl_quic_lcidm_generate_initial(QUIC_LCIDM *lcidm,
|
---|
166 | void *opaque,
|
---|
167 | QUIC_CONN_ID *initial_lcid);
|
---|
168 |
|
---|
169 | /*
|
---|
170 | * Create a subsequent LCID for a given opaque pointer. The information needed
|
---|
171 | * for a NEW_CONN_ID frame informing the peer of the new LCID, including the
|
---|
172 | * LCID itself, is written to *ncid_frame.
|
---|
173 | *
|
---|
174 | * ncid_frame->stateless_reset is not initialised and the caller is responsible
|
---|
175 | * for setting it.
|
---|
176 | *
|
---|
177 | * After this function returns successfully, the caller can for example
|
---|
178 | * register the new LCID with a DEMUX and queue the NEW_CONN_ID frame.
|
---|
179 | */
|
---|
180 | int ossl_quic_lcidm_generate(QUIC_LCIDM *lcidm,
|
---|
181 | void *opaque,
|
---|
182 | OSSL_QUIC_FRAME_NEW_CONN_ID *ncid_frame);
|
---|
183 |
|
---|
184 | /*
|
---|
185 | * Retire up to one LCID for a given opaque pointer value. Called repeatedly to
|
---|
186 | * handle a RETIRE_CONN_ID frame.
|
---|
187 | *
|
---|
188 | * If containing_pkt_dcid is non-NULL, this function enforces the requirement
|
---|
189 | * that a CID not be retired by a packet using that CID as the DCID. If
|
---|
190 | * containing_pkt_dcid is NULL, this check is skipped.
|
---|
191 | *
|
---|
192 | * If a LCID is retired as a result of a call to this function, the LCID which
|
---|
193 | * was retired is written to *retired_lcid, the sequence number of the LCID is
|
---|
194 | * written to *retired_seq_num and *did_retire is set to 1. Otherwise,
|
---|
195 | * *did_retire is set to 0. This enables a caller to e.g. unregister the LCID
|
---|
196 | * from a DEMUX. A caller should call this function repeatedly until the
|
---|
197 | * function returns with *did_retire set to 0.
|
---|
198 | *
|
---|
199 | * This call is likely to cause the value returned by
|
---|
200 | * ossl_quic_lcidm_get_num_active_lcid() to go down. A caller may wish to call
|
---|
201 | * ossl_quic_lcidm_generate() repeatedly to bring the number of active LCIDs
|
---|
202 | * back up to some threshold in response after calling this function.
|
---|
203 | *
|
---|
204 | * Returns 1 on success and 0 on failure. If arguments are valid but zero LCIDs
|
---|
205 | * are retired, this is considered a success condition.
|
---|
206 | */
|
---|
207 | int ossl_quic_lcidm_retire(QUIC_LCIDM *lcidm,
|
---|
208 | void *opaque,
|
---|
209 | uint64_t retire_prior_to,
|
---|
210 | const QUIC_CONN_ID *containing_pkt_dcid,
|
---|
211 | QUIC_CONN_ID *retired_lcid,
|
---|
212 | uint64_t *retired_seq_num,
|
---|
213 | int *did_retire);
|
---|
214 |
|
---|
215 | /*
|
---|
216 | * Cull all LCIDM state relating to a given opaque pointer value. This is useful
|
---|
217 | * if connection state is spontaneously freed. The caller is responsible for
|
---|
218 | * e.g. DEMUX state updates.
|
---|
219 | */
|
---|
220 | int ossl_quic_lcidm_cull(QUIC_LCIDM *lcidm, void *opaque);
|
---|
221 |
|
---|
222 | /*
|
---|
223 | * Lookup a LCID. If the LCID is found, writes the associated opaque pointer to
|
---|
224 | * *opaque and the associated sequence number to *seq_num. Returns 1 on success
|
---|
225 | * and 0 if an entry is not found. An output argument may be set to NULL if its
|
---|
226 | * value is not required.
|
---|
227 | *
|
---|
228 | * If the LCID is for an Initial ODCID, *seq_num is set to
|
---|
229 | * LCIDM_ODCID_SEQ_NUM.
|
---|
230 | */
|
---|
231 | #define LCIDM_ODCID_SEQ_NUM UINT64_MAX
|
---|
232 |
|
---|
233 | int ossl_quic_lcidm_lookup(QUIC_LCIDM *lcidm,
|
---|
234 | const QUIC_CONN_ID *lcid,
|
---|
235 | uint64_t *seq_num,
|
---|
236 | void **opaque);
|
---|
237 |
|
---|
238 | /*
|
---|
239 | * Debug call to manually remove a specific LCID. Should not be needed in normal
|
---|
240 | * usage. Returns 1 if the LCID was successfully found and removed and 0
|
---|
241 | * otherwise.
|
---|
242 | */
|
---|
243 | int ossl_quic_lcidm_debug_remove(QUIC_LCIDM *lcidm,
|
---|
244 | const QUIC_CONN_ID *lcid);
|
---|
245 |
|
---|
246 | /*
|
---|
247 | * Debug call to manually add a numbered LCID with a specific CID value and
|
---|
248 | * sequence number. Should not be needed in normal usage. Returns 1 on success
|
---|
249 | * and 0 on failure.
|
---|
250 | */
|
---|
251 | int ossl_quic_lcidm_debug_add(QUIC_LCIDM *lcidm, void *opaque,
|
---|
252 | const QUIC_CONN_ID *lcid,
|
---|
253 | uint64_t seq_num);
|
---|
254 |
|
---|
255 | # endif
|
---|
256 |
|
---|
257 | #endif
|
---|