QUIC DEMUX: Remove legacy routing code

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/22674)
This commit is contained in:
Hugo Landau 2023-11-09 10:27:14 +00:00
parent ef95d8ddca
commit da15093a31
2 changed files with 27 additions and 261 deletions

View File

@ -23,20 +23,19 @@
* ============
*
* 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).
* 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 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.
* 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
* ---------
@ -224,59 +223,6 @@ void ossl_quic_demux_set_bio(QUIC_DEMUX *demux, BIO *net_bio);
*/
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
@ -286,11 +232,6 @@ void ossl_quic_demux_unregister_by_cb(QUIC_DEMUX *demux,
* 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,

View File

@ -21,37 +21,6 @@
#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;
@ -73,9 +42,6 @@ struct quic_demux_st {
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;
@ -121,13 +87,6 @@ QUIC_DEMUX *ossl_quic_demux_new(BIO *net_bio,
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))
@ -136,11 +95,6 @@ QUIC_DEMUX *ossl_quic_demux_new(BIO *net_bio,
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;
@ -157,10 +111,6 @@ void ossl_quic_demux_free(QUIC_DEMUX *demux)
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);
@ -195,104 +145,6 @@ int ossl_quic_demux_set_mtu(QUIC_DEMUX *demux, unsigned int mtu)
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)
@ -480,29 +332,12 @@ static int demux_identify_conn_id(QUIC_DEMUX *demux,
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;
@ -535,35 +370,25 @@ static int demux_process_pending_urxe(QUIC_DEMUX *demux, QUIC_URXE *e)
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. */