mirror of
https://github.com/openssl/openssl.git
synced 2024-12-15 06:01:37 +08:00
cbe7f586ad
Reviewed-by: Tomas Mraz <tomas@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from https://github.com/openssl/openssl/pull/19703)
299 lines
7.5 KiB
C
299 lines
7.5 KiB
C
/*
|
|
* Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
|
|
*
|
|
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
|
* this file except in compliance with the License. You can obtain a copy
|
|
* in the file LICENSE in the source distribution or at
|
|
* https://www.openssl.org/source/license.html
|
|
*/
|
|
|
|
#include "internal/quic_stream_map.h"
|
|
#include "internal/nelem.h"
|
|
|
|
/* QUIC Stream
|
|
* ===========
|
|
*/
|
|
|
|
int ossl_quic_stream_stop_sending(QUIC_STREAM *s, uint64_t aec)
|
|
{
|
|
if (s->stop_sending)
|
|
return 0;
|
|
|
|
s->stop_sending_aec = aec;
|
|
s->stop_sending = 1;
|
|
s->want_stop_sending = 1;
|
|
return 1;
|
|
}
|
|
|
|
int ossl_quic_stream_reset(QUIC_STREAM *s, uint64_t aec)
|
|
{
|
|
if (s->reset_stream)
|
|
return 0;
|
|
|
|
s->reset_stream_aec = aec;
|
|
s->reset_stream = 1;
|
|
s->want_reset_stream = 1;
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* QUIC Stream Map
|
|
* ===============
|
|
*/
|
|
DEFINE_LHASH_OF_EX(QUIC_STREAM);
|
|
|
|
/* Circular list management. */
|
|
static void list_insert_tail(QUIC_STREAM_LIST_NODE *l,
|
|
QUIC_STREAM_LIST_NODE *n)
|
|
{
|
|
/* Must not be in list. */
|
|
assert(n->prev == NULL && n->next == NULL);
|
|
|
|
n->prev = l->prev;
|
|
n->prev->next = n;
|
|
l->prev = n;
|
|
n->next = l;
|
|
}
|
|
|
|
static void list_remove(QUIC_STREAM_LIST_NODE *l,
|
|
QUIC_STREAM_LIST_NODE *n)
|
|
{
|
|
assert(n->prev != NULL && n->next != NULL
|
|
&& n->prev != n && n->next != n);
|
|
|
|
n->prev->next = n->next;
|
|
n->next->prev = n->prev;
|
|
n->next = n->prev = NULL;
|
|
}
|
|
|
|
static QUIC_STREAM *active_next(QUIC_STREAM_LIST_NODE *l, QUIC_STREAM *s)
|
|
{
|
|
QUIC_STREAM_LIST_NODE *n = s->active_node.next;
|
|
|
|
if (n == l)
|
|
n = n->next;
|
|
if (n == l)
|
|
return NULL;
|
|
return (QUIC_STREAM *)n;
|
|
}
|
|
|
|
static unsigned long hash_stream(const QUIC_STREAM *s)
|
|
{
|
|
return (unsigned long)s->id;
|
|
}
|
|
|
|
static int cmp_stream(const QUIC_STREAM *a, const QUIC_STREAM *b)
|
|
{
|
|
if (a->id < b->id)
|
|
return -1;
|
|
if (a->id > b->id)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
int ossl_quic_stream_map_init(QUIC_STREAM_MAP *qsm,
|
|
uint64_t (*get_stream_limit_cb)(int uni, void *arg),
|
|
void *get_stream_limit_cb_arg)
|
|
{
|
|
qsm->map = lh_QUIC_STREAM_new(hash_stream, cmp_stream);
|
|
qsm->active_list.prev = qsm->active_list.next = &qsm->active_list;
|
|
qsm->rr_stepping = 1;
|
|
qsm->rr_counter = 0;
|
|
qsm->rr_cur = NULL;
|
|
|
|
qsm->get_stream_limit_cb = get_stream_limit_cb;
|
|
qsm->get_stream_limit_cb_arg = get_stream_limit_cb_arg;
|
|
return 1;
|
|
}
|
|
|
|
static void release_each(QUIC_STREAM *stream, void *arg)
|
|
{
|
|
QUIC_STREAM_MAP *qsm = arg;
|
|
|
|
ossl_quic_stream_map_release(qsm, stream);
|
|
}
|
|
|
|
void ossl_quic_stream_map_cleanup(QUIC_STREAM_MAP *qsm)
|
|
{
|
|
ossl_quic_stream_map_visit(qsm, release_each, qsm);
|
|
|
|
lh_QUIC_STREAM_free(qsm->map);
|
|
qsm->map = NULL;
|
|
}
|
|
|
|
void ossl_quic_stream_map_visit(QUIC_STREAM_MAP *qsm,
|
|
void (*visit_cb)(QUIC_STREAM *stream, void *arg),
|
|
void *visit_cb_arg)
|
|
{
|
|
lh_QUIC_STREAM_doall_arg(qsm->map, visit_cb, visit_cb_arg);
|
|
}
|
|
|
|
QUIC_STREAM *ossl_quic_stream_map_alloc(QUIC_STREAM_MAP *qsm,
|
|
uint64_t stream_id,
|
|
int type)
|
|
{
|
|
QUIC_STREAM *s;
|
|
QUIC_STREAM key;
|
|
|
|
key.id = stream_id;
|
|
|
|
s = lh_QUIC_STREAM_retrieve(qsm->map, &key);
|
|
if (s != NULL)
|
|
return NULL;
|
|
|
|
s = OPENSSL_zalloc(sizeof(*s));
|
|
if (s == NULL)
|
|
return NULL;
|
|
|
|
s->id = stream_id;
|
|
s->type = type;
|
|
lh_QUIC_STREAM_insert(qsm->map, s);
|
|
return s;
|
|
}
|
|
|
|
void ossl_quic_stream_map_release(QUIC_STREAM_MAP *qsm, QUIC_STREAM *stream)
|
|
{
|
|
if (stream == NULL)
|
|
return;
|
|
|
|
ossl_quic_sstream_free(stream->sstream);
|
|
stream->sstream = NULL;
|
|
|
|
ossl_quic_rstream_free(stream->rstream);
|
|
stream->rstream = NULL;
|
|
|
|
lh_QUIC_STREAM_delete(qsm->map, stream);
|
|
OPENSSL_free(stream);
|
|
}
|
|
|
|
QUIC_STREAM *ossl_quic_stream_map_get_by_id(QUIC_STREAM_MAP *qsm,
|
|
uint64_t stream_id)
|
|
{
|
|
QUIC_STREAM key;
|
|
|
|
key.id = stream_id;
|
|
|
|
return lh_QUIC_STREAM_retrieve(qsm->map, &key);
|
|
}
|
|
|
|
static void stream_map_mark_active(QUIC_STREAM_MAP *qsm, QUIC_STREAM *s)
|
|
{
|
|
if (s->active)
|
|
return;
|
|
|
|
list_insert_tail(&qsm->active_list, &s->active_node);
|
|
|
|
if (qsm->rr_cur == NULL)
|
|
qsm->rr_cur = s;
|
|
|
|
s->active = 1;
|
|
}
|
|
|
|
static void stream_map_mark_inactive(QUIC_STREAM_MAP *qsm, QUIC_STREAM *s)
|
|
{
|
|
if (!s->active)
|
|
return;
|
|
|
|
list_remove(&qsm->active_list, &s->active_node);
|
|
|
|
if (qsm->rr_cur == s)
|
|
qsm->rr_cur = active_next(&qsm->active_list, s);
|
|
if (qsm->rr_cur == s)
|
|
qsm->rr_cur = NULL;
|
|
|
|
s->active = 0;
|
|
}
|
|
|
|
void ossl_quic_stream_map_set_rr_stepping(QUIC_STREAM_MAP *qsm, size_t stepping)
|
|
{
|
|
qsm->rr_stepping = stepping;
|
|
qsm->rr_counter = 0;
|
|
}
|
|
|
|
static int stream_has_data_to_send(QUIC_STREAM *s)
|
|
{
|
|
OSSL_QUIC_FRAME_STREAM shdr;
|
|
OSSL_QTX_IOVEC iov[2];
|
|
size_t num_iov;
|
|
uint64_t fc_credit, fc_swm, fc_limit;
|
|
|
|
if (s->sstream == NULL)
|
|
return 0;
|
|
|
|
/*
|
|
* We cannot determine if we have data to send simply by checking if
|
|
* ossl_quic_txfc_get_credit() is zero, because we may also have older
|
|
* stream data we need to retransmit. The SSTREAM returns older data first,
|
|
* so we do a simple comparison of the next chunk the SSTREAM wants to send
|
|
* against the TXFC CWM.
|
|
*/
|
|
num_iov = OSSL_NELEM(iov);
|
|
if (!ossl_quic_sstream_get_stream_frame(s->sstream, 0, &shdr, iov,
|
|
&num_iov))
|
|
return 0;
|
|
|
|
fc_credit = ossl_quic_txfc_get_credit(&s->txfc);
|
|
fc_swm = ossl_quic_txfc_get_swm(&s->txfc);
|
|
fc_limit = fc_swm + fc_credit;
|
|
|
|
return (shdr.is_fin && shdr.len == 0) || shdr.offset < fc_limit;
|
|
}
|
|
|
|
void ossl_quic_stream_map_update_state(QUIC_STREAM_MAP *qsm, QUIC_STREAM *s)
|
|
{
|
|
int should_be_active, allowed_by_stream_limit = 1;
|
|
|
|
if (qsm->get_stream_limit_cb != NULL
|
|
&& (s->type & QUIC_STREAM_INITIATOR_CLIENT) != 0) {
|
|
int uni = ((s->type & QUIC_STREAM_DIR_UNI) != 0);
|
|
uint64_t stream_limit, stream_ordinal = s->id >> 2;
|
|
|
|
stream_limit
|
|
= qsm->get_stream_limit_cb(uni, qsm->get_stream_limit_cb_arg);
|
|
|
|
allowed_by_stream_limit = (stream_ordinal < stream_limit);
|
|
}
|
|
|
|
should_be_active
|
|
= allowed_by_stream_limit
|
|
&& !s->peer_stop_sending
|
|
&& !s->peer_reset_stream
|
|
&& ((s->rstream != NULL
|
|
&& (s->want_max_stream_data
|
|
|| ossl_quic_rxfc_has_cwm_changed(&s->rxfc, 0)))
|
|
|| s->want_stop_sending
|
|
|| s->want_reset_stream
|
|
|| stream_has_data_to_send(s));
|
|
|
|
if (should_be_active)
|
|
stream_map_mark_active(qsm, s);
|
|
else
|
|
stream_map_mark_inactive(qsm, s);
|
|
}
|
|
|
|
/*
|
|
* QUIC Stream Iterator
|
|
* ====================
|
|
*/
|
|
void ossl_quic_stream_iter_init(QUIC_STREAM_ITER *it, QUIC_STREAM_MAP *qsm,
|
|
int advance_rr)
|
|
{
|
|
it->qsm = qsm;
|
|
it->stream = it->first_stream = qsm->rr_cur;
|
|
if (advance_rr && it->stream != NULL
|
|
&& ++qsm->rr_counter >= qsm->rr_stepping) {
|
|
qsm->rr_counter = 0;
|
|
qsm->rr_cur = active_next(&qsm->active_list, qsm->rr_cur);
|
|
}
|
|
}
|
|
|
|
void ossl_quic_stream_iter_next(QUIC_STREAM_ITER *it)
|
|
{
|
|
if (it->stream == NULL)
|
|
return;
|
|
|
|
it->stream = active_next(&it->qsm->active_list, it->stream);
|
|
if (it->stream == it->first_stream)
|
|
it->stream = NULL;
|
|
}
|