* ============
*
* The QUIC connection demuxer is the entity responsible for receiving datagrams
- * from the network via a datagram BIO. It parses packet headers to determine
- * each packet's destination connection ID (DCID) and hands off processing of
- * the packet to the correct QUIC Record Layer (QRL)'s RX side (known as the
- * QRX).
- *
- * A QRX is instantiated per QUIC connection and contains the cryptographic
- * resources needed to decrypt QUIC packets for that connection. Received
- * datagrams are passed from the demuxer to the QRX via a callback registered
- * for a specific DCID by the QRX; thus the demuxer has no specific knowledge of
- * the QRX and is not coupled to it.
- *
- * A connection may have multiple connection IDs associated with it; a QRX
- * handles this simply by registering multiple connection IDs with the demuxer
- * via multiple register calls.
+ * from the network via a datagram BIO. It parses the headers of the first
+ * packet in the datagram to determine that packet's DCID and hands off
+ * processing of the entire datagram to a single callback function which can
+ * decide how to handle and route the datagram, for example by looking up
+ * a QRX instance and injecting the URXE into that QRX.
+ *
+ * A QRX will typically be instantiated per QUIC connection and contains the
+ * cryptographic resources needed to decrypt QUIC packets for that connection.
+ * However, it is up to the callback function to handle routing, for example by
+ * consulting a LCIDM instance. Thus the demuxer has no specific knowledge of
+ * any QRX and is not coupled to it. All CID knowledge is also externalised into
+ * a LCIDM or other CID state tracking object, without the DEMUX being coupled
+ * to any particular DCID resolution mechanism.
*
* URX Queue
* ---------
*/
int ossl_quic_demux_set_mtu(QUIC_DEMUX *demux, unsigned int mtu);
-/*
- * Register a datagram handler callback for a connection ID.
- *
- * ossl_quic_demux_pump will call the specified function if it receives a datagram
- * the first packet of which has the specified destination connection ID.
- *
- * It is assumed all packets in a datagram have the same destination connection
- * ID (as QUIC mandates this), but it is the user's responsibility to check for
- * this and reject subsequent packets in a datagram that violate this rule.
- *
- * dst_conn_id is a destination connection ID; it is copied and need not remain
- * valid after this function returns.
- *
- * cb_arg is passed to cb when it is called. For information on the callback,
- * see its typedef above.
- *
- * Only one handler can be set for a given connection ID. If a handler is
- * already set for the given connection ID, returns 0.
- *
- * TODO(QUIC SERVER): DEPRECATED in favour of explicit routing by QUIC_PORT with
- * reference to QUIC_LCIDM. To be removed.
- *
- * Returns 1 on success or 0 on failure.
- */
-int ossl_quic_demux_register(QUIC_DEMUX *demux,
- const QUIC_CONN_ID *dst_conn_id,
- ossl_quic_demux_cb_fn *cb,
- void *cb_arg);
-
-/*
- * Unregisters any datagram handler callback set for the given connection ID.
- * Fails if no handler is registered for the given connection ID.
- *
- * TODO(QUIC SERVER): DEPRECATED in favour of explicit routing by QUIC_PORT with
- * reference to QUIC_LCIDM. To be removed.
- *
- * Returns 1 on success or 0 on failure.
- */
-int ossl_quic_demux_unregister(QUIC_DEMUX *demux,
- const QUIC_CONN_ID *dst_conn_id);
-
-/*
- * Unregisters any datagram handler callback from all connection IDs it is used
- * for. cb and cb_arg must both match the values passed to
- * ossl_quic_demux_register.
- *
- * TODO(QUIC SERVER): DEPRECATED in favour of explicit routing by QUIC_PORT with
- * reference to QUIC_LCIDM. To be removed.
- */
-void ossl_quic_demux_unregister_by_cb(QUIC_DEMUX *demux,
- ossl_quic_demux_cb_fn *cb,
- void *cb_arg);
-
/*
* Set the default packet handler. This is used for incoming packets which don't
* match a registered DCID. This is only needed for servers. If a default packet
* The handler is responsible for ensuring that ossl_quic_demux_reinject_urxe or
* ossl_quic_demux_release_urxe is called on the passed packet at some point in
* the future, which may or may not be before the handler returns.
- *
- * TODO(QUIC SERVER): In the future all RX handling will go via this function
- * and the QUIC_PORT will be responsible for routing. DEMUX will then handle
- * URXE memory management and datagram DCID parsing only. The MVP LCID routing
- * functionality of the DEMUX will be removed in favour of LCIDM.
*/
void ossl_quic_demux_set_default_handler(QUIC_DEMUX *demux,
ossl_quic_demux_cb_fn *cb,
#define DEMUX_DEFAULT_MTU 1500
-/* Structure used to track a given connection ID. */
-typedef struct quic_demux_conn_st QUIC_DEMUX_CONN;
-
-struct quic_demux_conn_st {
- QUIC_DEMUX_CONN *next; /* used when unregistering only */
- QUIC_CONN_ID dst_conn_id;
- ossl_quic_demux_cb_fn *cb;
- void *cb_arg;
-};
-
-DEFINE_LHASH_OF_EX(QUIC_DEMUX_CONN);
-
-static unsigned long demux_conn_hash(const QUIC_DEMUX_CONN *conn)
-{
- size_t i;
- unsigned long v = 0;
-
- assert(conn->dst_conn_id.id_len <= QUIC_MAX_CONN_ID_LEN);
-
- for (i = 0; i < conn->dst_conn_id.id_len; ++i)
- v ^= ((unsigned long)conn->dst_conn_id.id[i])
- << ((i * 8) % (sizeof(unsigned long) * 8));
-
- return v;
-}
-
-static int demux_conn_cmp(const QUIC_DEMUX_CONN *a, const QUIC_DEMUX_CONN *b)
-{
- return !ossl_quic_conn_id_eq(&a->dst_conn_id, &b->dst_conn_id);
-}
-
struct quic_demux_st {
/* The underlying transport BIO with datagram semantics. */
BIO *net_bio;
OSSL_TIME (*now)(void *arg);
void *now_arg;
- /* Hashtable mapping connection IDs to QUIC_DEMUX_CONN structures. */
- LHASH_OF(QUIC_DEMUX_CONN) *conns_by_id;
-
/* The default packet handler, if any. */
ossl_quic_demux_cb_fn *default_cb;
void *default_cb_arg;
demux->now = now;
demux->now_arg = now_arg;
- demux->conns_by_id
- = lh_QUIC_DEMUX_CONN_new(demux_conn_hash, demux_conn_cmp);
- if (demux->conns_by_id == NULL) {
- OPENSSL_free(demux);
- return NULL;
- }
-
if (net_bio != NULL
&& BIO_dgram_get_local_addr_cap(net_bio)
&& BIO_dgram_set_local_addr_enable(net_bio, 1))
return demux;
}
-static void demux_free_conn_it(QUIC_DEMUX_CONN *conn, void *arg)
-{
- OPENSSL_free(conn);
-}
-
static void demux_free_urxl(QUIC_URXE_LIST *l)
{
QUIC_URXE *e, *enext;
if (demux == NULL)
return;
- /* Free all connection structures. */
- lh_QUIC_DEMUX_CONN_doall_arg(demux->conns_by_id, demux_free_conn_it, NULL);
- lh_QUIC_DEMUX_CONN_free(demux->conns_by_id);
-
/* Free all URXEs we are holding. */
demux_free_urxl(&demux->urx_free);
demux_free_urxl(&demux->urx_pending);
return 1;
}
-static QUIC_DEMUX_CONN *demux_get_by_conn_id(QUIC_DEMUX *demux,
- const QUIC_CONN_ID *dst_conn_id)
-{
- QUIC_DEMUX_CONN key;
-
- if (dst_conn_id->id_len > QUIC_MAX_CONN_ID_LEN)
- return NULL;
-
- key.dst_conn_id = *dst_conn_id;
- return lh_QUIC_DEMUX_CONN_retrieve(demux->conns_by_id, &key);
-}
-
-int ossl_quic_demux_register(QUIC_DEMUX *demux,
- const QUIC_CONN_ID *dst_conn_id,
- ossl_quic_demux_cb_fn *cb, void *cb_arg)
-{
- QUIC_DEMUX_CONN *conn;
-
- if (dst_conn_id == NULL
- || dst_conn_id->id_len > QUIC_MAX_CONN_ID_LEN
- || cb == NULL)
- return 0;
-
- /* Ensure not already registered. */
- if (demux_get_by_conn_id(demux, dst_conn_id) != NULL)
- /* Handler already registered with this connection ID. */
- return 0;
-
- conn = OPENSSL_zalloc(sizeof(QUIC_DEMUX_CONN));
- if (conn == NULL)
- return 0;
-
- conn->dst_conn_id = *dst_conn_id;
- conn->cb = cb;
- conn->cb_arg = cb_arg;
-
- lh_QUIC_DEMUX_CONN_insert(demux->conns_by_id, conn);
- return 1;
-}
-
-static void demux_unregister(QUIC_DEMUX *demux,
- QUIC_DEMUX_CONN *conn)
-{
- lh_QUIC_DEMUX_CONN_delete(demux->conns_by_id, conn);
- OPENSSL_free(conn);
-}
-
-int ossl_quic_demux_unregister(QUIC_DEMUX *demux,
- const QUIC_CONN_ID *dst_conn_id)
-{
- QUIC_DEMUX_CONN *conn;
-
- if (dst_conn_id == NULL
- || dst_conn_id->id_len > QUIC_MAX_CONN_ID_LEN)
- return 0;
-
- conn = demux_get_by_conn_id(demux, dst_conn_id);
- if (conn == NULL)
- return 0;
-
- demux_unregister(demux, conn);
- return 1;
-}
-
-struct unreg_arg {
- ossl_quic_demux_cb_fn *cb;
- void *cb_arg;
- QUIC_DEMUX_CONN *head;
-};
-
-static void demux_unregister_by_cb(QUIC_DEMUX_CONN *conn, void *arg_)
-{
- struct unreg_arg *arg = arg_;
-
- if (conn->cb == arg->cb && conn->cb_arg == arg->cb_arg) {
- conn->next = arg->head;
- arg->head = conn;
- }
-}
-
-void ossl_quic_demux_unregister_by_cb(QUIC_DEMUX *demux,
- ossl_quic_demux_cb_fn *cb,
- void *cb_arg)
-{
- QUIC_DEMUX_CONN *conn, *cnext;
- struct unreg_arg arg = {0};
- arg.cb = cb;
- arg.cb_arg = cb_arg;
-
- lh_QUIC_DEMUX_CONN_doall_arg(demux->conns_by_id,
- demux_unregister_by_cb, &arg);
-
- for (conn = arg.head; conn != NULL; conn = cnext) {
- cnext = conn->next;
- demux_unregister(demux, conn);
- }
-}
-
void ossl_quic_demux_set_default_handler(QUIC_DEMUX *demux,
ossl_quic_demux_cb_fn *cb,
void *cb_arg)
dst_conn_id);
}
-/* Identify the connection structure corresponding to a given URXE. */
-static QUIC_DEMUX_CONN *demux_identify_conn(QUIC_DEMUX *demux, QUIC_URXE *e,
- QUIC_CONN_ID *dst_conn_id,
- int *dst_conn_id_ok)
-{
- if (!demux_identify_conn_id(demux, e, dst_conn_id))
- /*
- * Datagram is so badly malformed we can't get the DCID from the first
- * packet in it, so just give up.
- */
- return NULL;
-
- *dst_conn_id_ok = 1;
- return demux_get_by_conn_id(demux, dst_conn_id);
-}
-
/*
* Process a single pending URXE.
* Returning 1 on success, 0 on failure and -1 on stateless reset.
*/
static int demux_process_pending_urxe(QUIC_DEMUX *demux, QUIC_URXE *e)
{
- QUIC_DEMUX_CONN *conn;
QUIC_CONN_ID dst_conn_id;
int r, dst_conn_id_ok = 0;
return 0;
}
- conn = demux_identify_conn(demux, e, &dst_conn_id, &dst_conn_id_ok);
- if (conn == NULL) {
+ /* Determine the DCID of the first packet in the datagram. */
+ dst_conn_id_ok = demux_identify_conn_id(demux, e, &dst_conn_id);
+
+ ossl_list_urxe_remove(&demux->urx_pending, e);
+ if (demux->default_cb != NULL) {
/*
- * We could not identify a connection. If we have a default packet
- * handler, pass it to the handler. Otherwise, we will never be able to
- * process this datagram, so get rid of it.
+ * Pass to default handler for routing. The URXE now belongs to the
+ * callback.
*/
- ossl_list_urxe_remove(&demux->urx_pending, e);
- if (demux->default_cb != NULL) {
- /* Pass to default handler. */
- e->demux_state = URXE_DEMUX_STATE_ISSUED;
- demux->default_cb(e, demux->default_cb_arg,
- dst_conn_id_ok ? &dst_conn_id : NULL);
- } else {
- /* Discard. */
- ossl_list_urxe_insert_tail(&demux->urx_free, e);
- e->demux_state = URXE_DEMUX_STATE_FREE;
- }
- return 1; /* keep processing pending URXEs */
+ e->demux_state = URXE_DEMUX_STATE_ISSUED;
+ demux->default_cb(e, demux->default_cb_arg,
+ dst_conn_id_ok ? &dst_conn_id : NULL);
+ } else {
+ /* Discard. */
+ ossl_list_urxe_insert_tail(&demux->urx_free, e);
+ e->demux_state = URXE_DEMUX_STATE_FREE;
}
- /*
- * Remove from list and invoke callback. The URXE now belongs to the
- * callback. (QUIC_DEMUX_CONN never has non-NULL cb.)
- */
- ossl_list_urxe_remove(&demux->urx_pending, e);
- e->demux_state = URXE_DEMUX_STATE_ISSUED;
- conn->cb(e, conn->cb_arg, dst_conn_id_ok ? &dst_conn_id : NULL);
- return 1;
+ return 1; /* keep processing pending URXEs */
}
/* Process pending URXEs to generate callbacks. */