1 | /*
|
---|
2 | * Copyright 2022-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 | #ifndef OSSL_QUIC_DEMUX_H
|
---|
11 | # define OSSL_QUIC_DEMUX_H
|
---|
12 |
|
---|
13 | # include <openssl/ssl.h>
|
---|
14 | # include "internal/quic_types.h"
|
---|
15 | # include "internal/quic_predef.h"
|
---|
16 | # include "internal/bio_addr.h"
|
---|
17 | # include "internal/time.h"
|
---|
18 | # include "internal/list.h"
|
---|
19 |
|
---|
20 | # ifndef OPENSSL_NO_QUIC
|
---|
21 |
|
---|
22 | /*
|
---|
23 | * QUIC Demuxer
|
---|
24 | * ============
|
---|
25 | *
|
---|
26 | * The QUIC connection demuxer is the entity responsible for receiving datagrams
|
---|
27 | * from the network via a datagram BIO. It parses the headers of the first
|
---|
28 | * packet in the datagram to determine that packet's DCID and hands off
|
---|
29 | * processing of the entire datagram to a single callback function which can
|
---|
30 | * decide how to handle and route the datagram, for example by looking up
|
---|
31 | * a QRX instance and injecting the URXE into that QRX.
|
---|
32 | *
|
---|
33 | * A QRX will typically be instantiated per QUIC connection and contains the
|
---|
34 | * cryptographic resources needed to decrypt QUIC packets for that connection.
|
---|
35 | * However, it is up to the callback function to handle routing, for example by
|
---|
36 | * consulting a LCIDM instance. Thus the demuxer has no specific knowledge of
|
---|
37 | * any QRX and is not coupled to it. All CID knowledge is also externalised into
|
---|
38 | * a LCIDM or other CID state tracking object, without the DEMUX being coupled
|
---|
39 | * to any particular DCID resolution mechanism.
|
---|
40 | *
|
---|
41 | * URX Queue
|
---|
42 | * ---------
|
---|
43 | *
|
---|
44 | * Since the demuxer must handle the initial reception of datagrams from the OS,
|
---|
45 | * RX queue management for new, unprocessed datagrams is also handled by the
|
---|
46 | * demuxer.
|
---|
47 | *
|
---|
48 | * The demuxer maintains a queue of Unprocessed RX Entries (URXEs), which store
|
---|
49 | * unprocessed (i.e., encrypted, unvalidated) data received from the network.
|
---|
50 | * The URXE queue is designed to allow multiple datagrams to be received in a
|
---|
51 | * single call to BIO_recvmmsg, where supported.
|
---|
52 | *
|
---|
53 | * One URXE is used per received datagram. Each datagram may contain multiple
|
---|
54 | * packets, however, this is not the demuxer's concern. QUIC prohibits different
|
---|
55 | * packets in the same datagram from containing different DCIDs; the demuxer
|
---|
56 | * only considers the DCID of the first packet in a datagram when deciding how
|
---|
57 | * to route a received datagram, and it is the responsibility of the QRX to
|
---|
58 | * enforce this rule. Packets other than the first packet in a datagram are not
|
---|
59 | * examined by the demuxer, and the demuxer does not perform validation of
|
---|
60 | * packet headers other than to the minimum extent necessary to extract the
|
---|
61 | * DCID; further parsing and validation of packet headers is the responsibility
|
---|
62 | * of the QRX.
|
---|
63 | *
|
---|
64 | * Rather than defining an opaque interface, the URXE structure internals
|
---|
65 | * are exposed. Since the demuxer is only exposed to other parts of the QUIC
|
---|
66 | * implementation internals, this poses no problem, and has a number of
|
---|
67 | * advantages:
|
---|
68 | *
|
---|
69 | * - Fields in the URXE can be allocated to support requirements in other
|
---|
70 | * components, like the QRX, which would otherwise have to allocate extra
|
---|
71 | * memory corresponding to each URXE.
|
---|
72 | *
|
---|
73 | * - Other components, like the QRX, can keep the URXE in queues of its own
|
---|
74 | * when it is not being managed by the demuxer.
|
---|
75 | *
|
---|
76 | * URX Queue Structure
|
---|
77 | * -------------------
|
---|
78 | *
|
---|
79 | * The URXE queue is maintained as a simple doubly-linked list. URXE entries are
|
---|
80 | * moved between different lists in their lifecycle (for example, from a free
|
---|
81 | * list to a pending list and vice versa). The buffer into which datagrams are
|
---|
82 | * received immediately follows this URXE header structure and is part of the
|
---|
83 | * same allocation.
|
---|
84 | */
|
---|
85 |
|
---|
86 | /* Maximum number of packets we allow to exist in one datagram. */
|
---|
87 | #define QUIC_MAX_PKT_PER_URXE (sizeof(uint64_t) * 8)
|
---|
88 |
|
---|
89 | struct quic_urxe_st {
|
---|
90 | OSSL_LIST_MEMBER(urxe, QUIC_URXE);
|
---|
91 |
|
---|
92 | /*
|
---|
93 | * The URXE data starts after this structure so we don't need a pointer.
|
---|
94 | * data_len stores the current length (i.e., the length of the received
|
---|
95 | * datagram) and alloc_len stores the allocation length. The URXE will be
|
---|
96 | * reallocated if we need a larger allocation than is available, though this
|
---|
97 | * should not be common as we will have a good idea of worst-case MTUs up
|
---|
98 | * front.
|
---|
99 | */
|
---|
100 | size_t data_len, alloc_len;
|
---|
101 |
|
---|
102 | /*
|
---|
103 | * Bitfields per packet. processed indicates the packet has been processed
|
---|
104 | * and must not be processed again, hpr_removed indicates header protection
|
---|
105 | * has already been removed. Used by QRX only; not used by the demuxer.
|
---|
106 | */
|
---|
107 | uint64_t processed, hpr_removed;
|
---|
108 |
|
---|
109 | /*
|
---|
110 | * This monotonically increases with each datagram received. It is used for
|
---|
111 | * diagnostic purposes only.
|
---|
112 | */
|
---|
113 | uint64_t datagram_id;
|
---|
114 |
|
---|
115 | /*
|
---|
116 | * Address of peer we received the datagram from, and the local interface
|
---|
117 | * address we received it on. If local address support is not enabled, local
|
---|
118 | * is zeroed.
|
---|
119 | */
|
---|
120 | BIO_ADDR peer, local;
|
---|
121 |
|
---|
122 | /*
|
---|
123 | * Time at which datagram was received (or ossl_time_zero()) if a now
|
---|
124 | * function was not provided).
|
---|
125 | */
|
---|
126 | OSSL_TIME time;
|
---|
127 |
|
---|
128 | /*
|
---|
129 | * Used by the QRX to mark whether a datagram has been deferred. Used by the
|
---|
130 | * QRX only; not used by the demuxer.
|
---|
131 | */
|
---|
132 | char deferred;
|
---|
133 |
|
---|
134 | /*
|
---|
135 | * Used by the DEMUX to track if a URXE has been handed out. Used primarily
|
---|
136 | * for debugging purposes.
|
---|
137 | */
|
---|
138 | char demux_state;
|
---|
139 | };
|
---|
140 |
|
---|
141 | /* Accessors for URXE buffer. */
|
---|
142 | static ossl_unused ossl_inline unsigned char *
|
---|
143 | ossl_quic_urxe_data(const QUIC_URXE *e)
|
---|
144 | {
|
---|
145 | return (unsigned char *)&e[1];
|
---|
146 | }
|
---|
147 |
|
---|
148 | static ossl_unused ossl_inline unsigned char *
|
---|
149 | ossl_quic_urxe_data_end(const QUIC_URXE *e)
|
---|
150 | {
|
---|
151 | return ossl_quic_urxe_data(e) + e->data_len;
|
---|
152 | }
|
---|
153 |
|
---|
154 | /* List structure tracking a queue of URXEs. */
|
---|
155 | DEFINE_LIST_OF(urxe, QUIC_URXE);
|
---|
156 | typedef OSSL_LIST(urxe) QUIC_URXE_LIST;
|
---|
157 |
|
---|
158 | /*
|
---|
159 | * List management helpers. These are used by the demuxer but can also be used
|
---|
160 | * by users of the demuxer to manage URXEs.
|
---|
161 | */
|
---|
162 | void ossl_quic_urxe_remove(QUIC_URXE_LIST *l, QUIC_URXE *e);
|
---|
163 | void ossl_quic_urxe_insert_head(QUIC_URXE_LIST *l, QUIC_URXE *e);
|
---|
164 | void ossl_quic_urxe_insert_tail(QUIC_URXE_LIST *l, QUIC_URXE *e);
|
---|
165 |
|
---|
166 | /*
|
---|
167 | * Called when a datagram is received for a given connection ID.
|
---|
168 | *
|
---|
169 | * e is a URXE containing the datagram payload. It is permissible for the callee
|
---|
170 | * to mutate this buffer; once the demuxer calls this callback, it will never
|
---|
171 | * read the buffer again.
|
---|
172 | *
|
---|
173 | * If a DCID was identified for the datagram, dcid is non-NULL; otherwise
|
---|
174 | * it is NULL.
|
---|
175 | *
|
---|
176 | * The callee must arrange for ossl_quic_demux_release_urxe or
|
---|
177 | * ossl_quic_demux_reinject_urxe to be called on the URXE at some point in the
|
---|
178 | * future (this need not be before the callback returns).
|
---|
179 | *
|
---|
180 | * At the time the callback is made, the URXE will not be in any queue,
|
---|
181 | * therefore the callee can use the prev and next fields as it wishes.
|
---|
182 | */
|
---|
183 | typedef void (ossl_quic_demux_cb_fn)(QUIC_URXE *e, void *arg,
|
---|
184 | const QUIC_CONN_ID *dcid);
|
---|
185 |
|
---|
186 | /*
|
---|
187 | * Creates a new demuxer. The given BIO is used to receive datagrams from the
|
---|
188 | * network using BIO_recvmmsg. short_conn_id_len is the length of destination
|
---|
189 | * connection IDs used in RX'd packets; it must have the same value for all
|
---|
190 | * connections used on a socket. default_urxe_alloc_len is the buffer size to
|
---|
191 | * receive datagrams into; it should be a value large enough to contain any
|
---|
192 | * received datagram according to local MTUs, etc.
|
---|
193 | *
|
---|
194 | * now is an optional function used to determine the time a datagram was
|
---|
195 | * received. now_arg is an opaque argument passed to the function. If now is
|
---|
196 | * NULL, ossl_time_zero() is used as the datagram reception time.
|
---|
197 | */
|
---|
198 | QUIC_DEMUX *ossl_quic_demux_new(BIO *net_bio,
|
---|
199 | size_t short_conn_id_len,
|
---|
200 | OSSL_TIME (*now)(void *arg),
|
---|
201 | void *now_arg);
|
---|
202 |
|
---|
203 | /*
|
---|
204 | * Destroy a demuxer. All URXEs must have been released back to the demuxer
|
---|
205 | * before calling this. No-op if demux is NULL.
|
---|
206 | */
|
---|
207 | void ossl_quic_demux_free(QUIC_DEMUX *demux);
|
---|
208 |
|
---|
209 | /*
|
---|
210 | * Changes the BIO which the demuxer reads from. This also sets the MTU if the
|
---|
211 | * BIO supports querying the MTU.
|
---|
212 | */
|
---|
213 | void ossl_quic_demux_set_bio(QUIC_DEMUX *demux, BIO *net_bio);
|
---|
214 |
|
---|
215 | /*
|
---|
216 | * Changes the MTU in bytes we use to receive datagrams.
|
---|
217 | */
|
---|
218 | int ossl_quic_demux_set_mtu(QUIC_DEMUX *demux, unsigned int mtu);
|
---|
219 |
|
---|
220 | /*
|
---|
221 | * Set the default packet handler. This is used for incoming packets which don't
|
---|
222 | * match a registered DCID. This is only needed for servers. If a default packet
|
---|
223 | * handler is not set, a packet which doesn't match a registered DCID is
|
---|
224 | * silently dropped. A default packet handler may be unset by passing NULL.
|
---|
225 | *
|
---|
226 | * The handler is responsible for ensuring that ossl_quic_demux_reinject_urxe or
|
---|
227 | * ossl_quic_demux_release_urxe is called on the passed packet at some point in
|
---|
228 | * the future, which may or may not be before the handler returns.
|
---|
229 | */
|
---|
230 | void ossl_quic_demux_set_default_handler(QUIC_DEMUX *demux,
|
---|
231 | ossl_quic_demux_cb_fn *cb,
|
---|
232 | void *cb_arg);
|
---|
233 |
|
---|
234 | /*
|
---|
235 | * Releases a URXE back to the demuxer. No reference must be made to the URXE or
|
---|
236 | * its buffer after calling this function. The URXE must not be in any queue;
|
---|
237 | * that is, its prev and next pointers must be NULL.
|
---|
238 | */
|
---|
239 | void ossl_quic_demux_release_urxe(QUIC_DEMUX *demux,
|
---|
240 | QUIC_URXE *e);
|
---|
241 |
|
---|
242 | /*
|
---|
243 | * Reinjects a URXE which was issued to a registered DCID callback or the
|
---|
244 | * default packet handler callback back into the pending queue. This is useful
|
---|
245 | * when a packet has been handled by the default packet handler callback such
|
---|
246 | * that a DCID has now been registered and can be dispatched normally by DCID.
|
---|
247 | * Once this has been called, the caller must not touch the URXE anymore and
|
---|
248 | * must not also call ossl_quic_demux_release_urxe().
|
---|
249 | *
|
---|
250 | * The URXE is reinjected at the head of the queue, so it will be reprocessed
|
---|
251 | * immediately.
|
---|
252 | */
|
---|
253 | void ossl_quic_demux_reinject_urxe(QUIC_DEMUX *demux,
|
---|
254 | QUIC_URXE *e);
|
---|
255 |
|
---|
256 | /*
|
---|
257 | * Process any unprocessed RX'd datagrams, by calling registered callbacks by
|
---|
258 | * connection ID, reading more datagrams from the BIO if necessary.
|
---|
259 | *
|
---|
260 | * Returns one of the following values:
|
---|
261 | *
|
---|
262 | * QUIC_DEMUX_PUMP_RES_OK
|
---|
263 | * At least one incoming datagram was processed.
|
---|
264 | *
|
---|
265 | * QUIC_DEMUX_PUMP_RES_TRANSIENT_FAIL
|
---|
266 | * No more incoming datagrams are currently available.
|
---|
267 | * Call again later.
|
---|
268 | *
|
---|
269 | * QUIC_DEMUX_PUMP_RES_PERMANENT_FAIL
|
---|
270 | * Either the network read BIO has failed in a non-transient fashion, or
|
---|
271 | * the QUIC implementation has encountered an internal state, assertion
|
---|
272 | * or allocation error. The caller should tear down the connection
|
---|
273 | * similarly to in the case of a protocol violation.
|
---|
274 | *
|
---|
275 | */
|
---|
276 | #define QUIC_DEMUX_PUMP_RES_OK 1
|
---|
277 | #define QUIC_DEMUX_PUMP_RES_TRANSIENT_FAIL (-1)
|
---|
278 | #define QUIC_DEMUX_PUMP_RES_PERMANENT_FAIL (-2)
|
---|
279 |
|
---|
280 | int ossl_quic_demux_pump(QUIC_DEMUX *demux);
|
---|
281 |
|
---|
282 | /*
|
---|
283 | * Artificially inject a packet into the demuxer for testing purposes. The
|
---|
284 | * buffer must not exceed the URXE size being used by the demuxer.
|
---|
285 | *
|
---|
286 | * If peer or local are NULL, their respective fields are zeroed in the injected
|
---|
287 | * URXE.
|
---|
288 | *
|
---|
289 | * Returns 1 on success or 0 on failure.
|
---|
290 | */
|
---|
291 | int ossl_quic_demux_inject(QUIC_DEMUX *demux,
|
---|
292 | const unsigned char *buf,
|
---|
293 | size_t buf_len,
|
---|
294 | const BIO_ADDR *peer,
|
---|
295 | const BIO_ADDR *local);
|
---|
296 |
|
---|
297 | /*
|
---|
298 | * Returns 1 if there are any pending URXEs.
|
---|
299 | */
|
---|
300 | int ossl_quic_demux_has_pending(const QUIC_DEMUX *demux);
|
---|
301 |
|
---|
302 | # endif
|
---|
303 |
|
---|
304 | #endif
|
---|