summaryrefslogtreecommitdiffstats
path: root/ssl
diff options
context:
space:
mode:
authorHugo Landau <hlandau@openssl.org>2022-11-18 17:20:20 +0000
committerHugo Landau <hlandau@openssl.org>2023-01-13 13:20:17 +0000
commitd7668ff21328c03f137d665b37f228e7c1f7a32a (patch)
treec013d1dcb523ac6b12f5e3b34bd848d20f709ab6 /ssl
parent6292519cd8102983e9924b6b0d3f298ac5f93e80 (diff)
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)
Diffstat (limited to 'ssl')
-rw-r--r--ssl/quic/quic_channel.c2
-rw-r--r--ssl/quic/quic_demux.c105
2 files changed, 99 insertions, 8 deletions
diff --git a/ssl/quic/quic_channel.c b/ssl/quic/quic_channel.c
index 795dca23f7..7da3095ee1 100644
--- a/ssl/quic/quic_channel.c
+++ b/ssl/quic/quic_channel.c
@@ -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;
diff --git a/ssl/quic/quic_demux.c b/ssl/quic/quic_demux.c
index 9ac4a1a415..810be8af03 100644
--- a/ssl/quic/quic_demux.c
+++ b/ssl/quic/quic_demux.c
@@ -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;
}