QUIC DEMUX: Allow MTU to vary over time and autodetect MTU

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/19703)
This commit is contained in:
Hugo Landau 2022-11-18 17:20:20 +00:00
parent 6292519cd8
commit d7668ff213
5 changed files with 113 additions and 12 deletions

View File

@ -126,6 +126,12 @@ struct quic_urxe_st {
* QRX only; not used by the demuxer.
*/
char deferred;
/*
* Used by the DEMUX to track if a URXE has been handed out. Used primarily
* for debugging purposes.
*/
char demux_state;
};
/* Accessors for URXE buffer. */
@ -185,7 +191,6 @@ typedef void (ossl_quic_demux_cb_fn)(QUIC_URXE *e, void *arg);
*/
QUIC_DEMUX *ossl_quic_demux_new(BIO *net_bio,
size_t short_conn_id_len,
size_t default_urxe_alloc_len,
OSSL_TIME (*now)(void *arg),
void *now_arg);
@ -196,10 +201,16 @@ QUIC_DEMUX *ossl_quic_demux_new(BIO *net_bio,
void ossl_quic_demux_free(QUIC_DEMUX *demux);
/*
* Changes the BIO which the demuxer reads from.
* Changes the BIO which the demuxer reads from. This also sets the MTU if the
* BIO supports querying the MTU.
*/
void ossl_quic_demux_set_bio(QUIC_DEMUX *demux, BIO *net_bio);
/*
* Changes the MTU in bytes we use to receive datagrams.
*/
int ossl_quic_demux_set_mtu(QUIC_DEMUX *demux, unsigned int mtu);
/*
* Register a datagram handler callback for a connection ID.
*

View File

@ -159,7 +159,7 @@ static int ch_init(QUIC_CHANNEL *ch)
goto err;
if ((ch->demux = ossl_quic_demux_new(/*BIO=*/NULL, /*Short CID Len=*/0,
1200, get_time, NULL)) == NULL)
get_time, NULL)) == NULL)
goto err;
qrx_args.demux = ch->demux;

View File

@ -12,8 +12,14 @@
#include "internal/common.h"
#include <openssl/lhash.h>
#define URXE_DEMUX_STATE_FREE 0 /* on urx_free list */
#define URXE_DEMUX_STATE_PENDING 1 /* on urx_pending list */
#define URXE_DEMUX_STATE_ISSUED 2 /* on neither list */
#define DEMUX_MAX_MSGS_PER_CALL 32
#define DEMUX_DEFAULT_MTU 1500
/* Structure used to track a given connection ID. */
typedef struct quic_demux_conn_st QUIC_DEMUX_CONN;
@ -56,8 +62,11 @@ struct quic_demux_st {
*/
size_t short_conn_id_len;
/* Default URXE buffer size in bytes. */
size_t default_urxe_alloc_len;
/*
* Our current understanding of the upper bound on an incoming datagram size
* in bytes.
*/
size_t mtu;
/* Time retrieval callback. */
OSSL_TIME (*now)(void *arg);
@ -87,7 +96,6 @@ struct quic_demux_st {
QUIC_DEMUX *ossl_quic_demux_new(BIO *net_bio,
size_t short_conn_id_len,
size_t default_urxe_alloc_len,
OSSL_TIME (*now)(void *arg),
void *now_arg)
{
@ -99,7 +107,8 @@ QUIC_DEMUX *ossl_quic_demux_new(BIO *net_bio,
demux->net_bio = net_bio;
demux->short_conn_id_len = short_conn_id_len;
demux->default_urxe_alloc_len = default_urxe_alloc_len;
/* We update this if possible when we get a BIO. */
demux->mtu = DEMUX_DEFAULT_MTU;
demux->now = now;
demux->now_arg = now_arg;
@ -152,7 +161,29 @@ void ossl_quic_demux_free(QUIC_DEMUX *demux)
void ossl_quic_demux_set_bio(QUIC_DEMUX *demux, BIO *net_bio)
{
unsigned int mtu;
demux->net_bio = net_bio;
if (net_bio != NULL) {
/*
* Try to determine our MTU if possible. The BIO is not required to
* support this, in which case we remain at the last known MTU, or our
* initial default.
*/
mtu = BIO_dgram_get_mtu(net_bio);
if (mtu >= QUIC_MIN_INITIAL_DGRAM_LEN)
ossl_quic_demux_set_mtu(demux, mtu); /* best effort */
}
}
int ossl_quic_demux_set_mtu(QUIC_DEMUX *demux, unsigned int mtu)
{
if (mtu < QUIC_MIN_INITIAL_DGRAM_LEN)
return 0;
demux->mtu = mtu;
return 1;
}
static QUIC_DEMUX_CONN *demux_get_by_conn_id(QUIC_DEMUX *demux,
@ -265,21 +296,60 @@ static QUIC_URXE *demux_alloc_urxe(size_t alloc_len)
return NULL;
ossl_list_urxe_init_elem(e);
e->alloc_len = alloc_len;
e->data_len = 0;
e->alloc_len = alloc_len;
e->data_len = 0;
return e;
}
static QUIC_URXE *demux_resize_urxe(QUIC_DEMUX *demux, QUIC_URXE *e,
size_t new_alloc_len)
{
QUIC_URXE *e2, *prev;
if (!ossl_assert(e->demux_state == URXE_DEMUX_STATE_FREE))
/* Never attempt to resize a URXE which is not on the free list. */
return NULL;
prev = ossl_list_urxe_prev(e);
ossl_list_urxe_remove(&demux->urx_free, e);
e2 = OPENSSL_realloc(e, sizeof(QUIC_URXE) + new_alloc_len);
if (e2 == NULL) {
/* Failed to resize, abort. */
if (prev == NULL)
ossl_list_urxe_insert_head(&demux->urx_free, e);
else
ossl_list_urxe_insert_after(&demux->urx_free, prev, e);
return NULL;
}
if (prev == NULL)
ossl_list_urxe_insert_head(&demux->urx_free, e2);
else
ossl_list_urxe_insert_after(&demux->urx_free, prev, e2);
e2->alloc_len = new_alloc_len;
return e2;
}
static QUIC_URXE *demux_reserve_urxe(QUIC_DEMUX *demux, QUIC_URXE *e,
size_t alloc_len)
{
return e->alloc_len < alloc_len ? demux_resize_urxe(demux, e, alloc_len) : e;
}
static int demux_ensure_free_urxe(QUIC_DEMUX *demux, size_t min_num_free)
{
QUIC_URXE *e;
while (ossl_list_urxe_num(&demux->urx_free) < min_num_free) {
e = demux_alloc_urxe(demux->default_urxe_alloc_len);
e = demux_alloc_urxe(demux->mtu);
if (e == NULL)
return 0;
ossl_list_urxe_insert_tail(&demux->urx_free, e);
e->demux_state = URXE_DEMUX_STATE_FREE;
}
return 1;
@ -302,6 +372,7 @@ static int demux_recv(QUIC_DEMUX *demux)
/* This should never be called when we have any pending URXE. */
assert(ossl_list_urxe_head(&demux->urx_pending) == NULL);
assert(urxe->demux_state == URXE_DEMUX_STATE_FREE);
if (demux->net_bio == NULL)
return 0;
@ -320,6 +391,12 @@ static int demux_recv(QUIC_DEMUX *demux)
break;
}
/* Ensure the URXE is big enough. */
urxe = demux_reserve_urxe(demux, urxe, demux->mtu);
if (urxe == NULL)
/* Allocation error, fail. */
return 0;
/* Ensure we zero any fields added to BIO_MSG at a later date. */
memset(&msg[i], 0, sizeof(BIO_MSG));
msg[i].data = ossl_quic_urxe_data(urxe);
@ -346,6 +423,7 @@ static int demux_recv(QUIC_DEMUX *demux)
/* Move from free list to pending list. */
ossl_list_urxe_remove(&demux->urx_free, urxe);
ossl_list_urxe_insert_tail(&demux->urx_pending, urxe);
urxe->demux_state = URXE_DEMUX_STATE_PENDING;
}
return 1;
@ -386,6 +464,8 @@ static int demux_process_pending_urxe(QUIC_DEMUX *demux, QUIC_URXE *e)
if (!ossl_assert(e == ossl_list_urxe_head(&demux->urx_pending)))
return 0;
assert(e->demux_state == URXE_DEMUX_STATE_PENDING);
conn = demux_identify_conn(demux, e);
if (conn == NULL) {
/*
@ -394,6 +474,7 @@ static int demux_process_pending_urxe(QUIC_DEMUX *demux, QUIC_URXE *e)
*/
ossl_list_urxe_remove(&demux->urx_pending, e);
ossl_list_urxe_insert_tail(&demux->urx_free, e);
e->demux_state = URXE_DEMUX_STATE_FREE;
return 1; /* keep processing pending URXEs */
}
@ -402,6 +483,7 @@ static int demux_process_pending_urxe(QUIC_DEMUX *demux, QUIC_URXE *e)
* 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);
return 1;
}
@ -462,6 +544,12 @@ int ossl_quic_demux_inject(QUIC_DEMUX *demux,
if (buf_len > urxe->alloc_len)
return 0;
assert(urxe->demux_state == URXE_DEMUX_STATE_FREE);
urxe = demux_reserve_urxe(demux, urxe, buf_len);
if (urxe == NULL)
return 0;
memcpy(ossl_quic_urxe_data(urxe), buf, buf_len);
urxe->data_len = buf_len;
@ -478,6 +566,7 @@ int ossl_quic_demux_inject(QUIC_DEMUX *demux,
/* Move from free list to pending list. */
ossl_list_urxe_remove(&demux->urx_free, urxe);
ossl_list_urxe_insert_tail(&demux->urx_pending, urxe);
urxe->demux_state = URXE_DEMUX_STATE_PENDING;
return demux_process_pending_urxl(demux);
}
@ -487,5 +576,7 @@ void ossl_quic_demux_release_urxe(QUIC_DEMUX *demux,
QUIC_URXE *e)
{
assert(ossl_list_urxe_prev(e) == NULL && ossl_list_urxe_next(e) == NULL);
assert(e->demux_state == URXE_DEMUX_STATE_ISSUED);
ossl_list_urxe_insert_tail(&demux->urx_free, e);
e->demux_state = URXE_DEMUX_STATE_FREE;
}

View File

@ -1704,7 +1704,6 @@ static int rx_state_ensure(struct rx_state *s)
if (s->demux == NULL
&& !TEST_ptr(s->demux = ossl_quic_demux_new(NULL,
s->args.short_conn_id_len,
1500,
fake_time,
NULL)))
return 0;

View File

@ -183,7 +183,7 @@ static int helper_init(struct helper *h)
if (!TEST_ptr(h->txp = ossl_quic_tx_packetiser_new(&h->args)))
goto err;
if (!TEST_ptr(h->demux = ossl_quic_demux_new(h->bio2, 8, 1200,
if (!TEST_ptr(h->demux = ossl_quic_demux_new(h->bio2, 8,
fake_now, NULL)))
goto err;