diff options
author | Hugo Landau <hlandau@openssl.org> | 2022-11-18 17:20:20 +0000 |
---|---|---|
committer | Hugo Landau <hlandau@openssl.org> | 2023-01-13 13:20:17 +0000 |
commit | d7668ff21328c03f137d665b37f228e7c1f7a32a (patch) | |
tree | c013d1dcb523ac6b12f5e3b34bd848d20f709ab6 /ssl | |
parent | 6292519cd8102983e9924b6b0d3f298ac5f93e80 (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.c | 2 | ||||
-rw-r--r-- | ssl/quic/quic_demux.c | 105 |
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; } |