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