Path: blob/main/crypto/openssl/ssl/quic/quic_sstream.c
48261 views
/*1* Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.2*3* Licensed under the Apache License 2.0 (the "License"). You may not use4* this file except in compliance with the License. You can obtain a copy5* in the file LICENSE in the source distribution or at6* https://www.openssl.org/source/license.html7*/89#include "internal/quic_stream.h"10#include "internal/uint_set.h"11#include "internal/common.h"12#include "internal/ring_buf.h"1314/*15* ==================================================================16* QUIC Send Stream17*/18struct quic_sstream_st {19struct ring_buf ring_buf;2021/*22* Any logical byte in the stream is in one of these states:23*24* - NEW: The byte has not yet been transmitted, or has been lost and is25* in need of retransmission.26*27* - IN_FLIGHT: The byte has been transmitted but is awaiting28* acknowledgement. We continue to store the data in case we return29* to the NEW state.30*31* - ACKED: The byte has been acknowledged and we can cease storing it.32* We do not necessarily cull it immediately, so there may be a delay33* between reaching the ACKED state and the buffer space actually being34* recycled.35*36* A logical byte in the stream is37*38* - in the NEW state if it is in new_set;39* - is in the ACKED state if it is in acked_set40* (and may or may not have been culled);41* - is in the IN_FLIGHT state otherwise.42*43* Invariant: No logical byte is ever in both new_set and acked_set.44*/45UINT_SET new_set, acked_set;4647/*48* The current size of the stream is ring_buf.head_offset. If49* have_final_size is true, this is also the final size of the stream.50*/51unsigned int have_final_size : 1;52unsigned int sent_final_size : 1;53unsigned int acked_final_size : 1;54unsigned int cleanse : 1;55};5657static void qss_cull(QUIC_SSTREAM *qss);5859QUIC_SSTREAM *ossl_quic_sstream_new(size_t init_buf_size)60{61QUIC_SSTREAM *qss;6263qss = OPENSSL_zalloc(sizeof(QUIC_SSTREAM));64if (qss == NULL)65return NULL;6667ring_buf_init(&qss->ring_buf);68if (!ring_buf_resize(&qss->ring_buf, init_buf_size, 0)) {69ring_buf_destroy(&qss->ring_buf, 0);70OPENSSL_free(qss);71return NULL;72}7374ossl_uint_set_init(&qss->new_set);75ossl_uint_set_init(&qss->acked_set);76return qss;77}7879void ossl_quic_sstream_free(QUIC_SSTREAM *qss)80{81if (qss == NULL)82return;8384ossl_uint_set_destroy(&qss->new_set);85ossl_uint_set_destroy(&qss->acked_set);86ring_buf_destroy(&qss->ring_buf, qss->cleanse);87OPENSSL_free(qss);88}8990int ossl_quic_sstream_get_stream_frame(QUIC_SSTREAM *qss,91size_t skip,92OSSL_QUIC_FRAME_STREAM *hdr,93OSSL_QTX_IOVEC *iov,94size_t *num_iov)95{96size_t num_iov_ = 0, src_len = 0, total_len = 0, i;97uint64_t max_len;98const unsigned char *src = NULL;99UINT_SET_ITEM *range = ossl_list_uint_set_head(&qss->new_set);100101if (*num_iov < 2)102return 0;103104for (i = 0; i < skip && range != NULL; ++i)105range = ossl_list_uint_set_next(range);106107if (range == NULL) {108if (i < skip)109/* Don't return FIN for infinitely increasing skip */110return 0;111112/* No new bytes to send, but we might have a FIN */113if (!qss->have_final_size || qss->sent_final_size)114return 0;115116hdr->offset = qss->ring_buf.head_offset;117hdr->len = 0;118hdr->is_fin = 1;119*num_iov = 0;120return 1;121}122123/*124* We can only send a contiguous range of logical bytes in a single125* stream frame, so limit ourselves to the range of the first set entry.126*127* Set entries never have 'adjacent' entries so we don't have to worry128* about them here.129*/130max_len = range->range.end - range->range.start + 1;131132for (i = 0;; ++i) {133if (total_len >= max_len)134break;135136if (!ring_buf_get_buf_at(&qss->ring_buf,137range->range.start + total_len,138&src, &src_len))139return 0;140141if (src_len == 0)142break;143144assert(i < 2);145146if (total_len + src_len > max_len)147src_len = (size_t)(max_len - total_len);148149iov[num_iov_].buf = src;150iov[num_iov_].buf_len = src_len;151152total_len += src_len;153++num_iov_;154}155156hdr->offset = range->range.start;157hdr->len = total_len;158hdr->is_fin = qss->have_final_size159&& hdr->offset + hdr->len == qss->ring_buf.head_offset;160161*num_iov = num_iov_;162return 1;163}164165int ossl_quic_sstream_has_pending(QUIC_SSTREAM *qss)166{167OSSL_QUIC_FRAME_STREAM shdr;168OSSL_QTX_IOVEC iov[2];169size_t num_iov = OSSL_NELEM(iov);170171return ossl_quic_sstream_get_stream_frame(qss, 0, &shdr, iov, &num_iov);172}173174uint64_t ossl_quic_sstream_get_cur_size(QUIC_SSTREAM *qss)175{176return qss->ring_buf.head_offset;177}178179int ossl_quic_sstream_mark_transmitted(QUIC_SSTREAM *qss,180uint64_t start,181uint64_t end)182{183UINT_RANGE r;184185r.start = start;186r.end = end;187188if (!ossl_uint_set_remove(&qss->new_set, &r))189return 0;190191return 1;192}193194int ossl_quic_sstream_mark_transmitted_fin(QUIC_SSTREAM *qss,195uint64_t final_size)196{197/*198* We do not really need final_size since we already know the size of the199* stream, but this serves as a sanity check.200*/201if (!qss->have_final_size || final_size != qss->ring_buf.head_offset)202return 0;203204qss->sent_final_size = 1;205return 1;206}207208int ossl_quic_sstream_mark_lost(QUIC_SSTREAM *qss,209uint64_t start,210uint64_t end)211{212UINT_RANGE r;213r.start = start;214r.end = end;215216/*217* We lost a range of stream data bytes, so reinsert them into the new set,218* so that they are returned once more by ossl_quic_sstream_get_stream_frame.219*/220if (!ossl_uint_set_insert(&qss->new_set, &r))221return 0;222223return 1;224}225226int ossl_quic_sstream_mark_lost_fin(QUIC_SSTREAM *qss)227{228if (qss->acked_final_size)229/* Does not make sense to lose a FIN after it has been ACKed */230return 0;231232/* FIN was lost, so we need to transmit it again. */233qss->sent_final_size = 0;234return 1;235}236237int ossl_quic_sstream_mark_acked(QUIC_SSTREAM *qss,238uint64_t start,239uint64_t end)240{241UINT_RANGE r;242r.start = start;243r.end = end;244245if (!ossl_uint_set_insert(&qss->acked_set, &r))246return 0;247248qss_cull(qss);249return 1;250}251252int ossl_quic_sstream_mark_acked_fin(QUIC_SSTREAM *qss)253{254if (!qss->have_final_size)255/* Cannot ack final size before we have a final size */256return 0;257258qss->acked_final_size = 1;259return 1;260}261262void ossl_quic_sstream_fin(QUIC_SSTREAM *qss)263{264if (qss->have_final_size)265return;266267qss->have_final_size = 1;268}269270int ossl_quic_sstream_get_final_size(QUIC_SSTREAM *qss, uint64_t *final_size)271{272if (!qss->have_final_size)273return 0;274275if (final_size != NULL)276*final_size = qss->ring_buf.head_offset;277278return 1;279}280281int ossl_quic_sstream_append(QUIC_SSTREAM *qss,282const unsigned char *buf,283size_t buf_len,284size_t *consumed)285{286size_t l, consumed_ = 0;287UINT_RANGE r;288struct ring_buf old_ring_buf = qss->ring_buf;289290if (qss->have_final_size) {291*consumed = 0;292return 0;293}294295/*296* Note: It is assumed that ossl_quic_sstream_append will be called during a297* call to e.g. SSL_write and this function is therefore designed to support298* such semantics. In particular, the buffer pointed to by buf is only299* assumed to be valid for the duration of this call, therefore we must copy300* the data here. We will later copy-and-encrypt the data during packet301* encryption, so this is a two-copy design. Supporting a one-copy design in302* the future will require applications to use a different kind of API.303* Supporting such changes in future will require corresponding enhancements304* to this code.305*/306while (buf_len > 0) {307l = ring_buf_push(&qss->ring_buf, buf, buf_len);308if (l == 0)309break;310311buf += l;312buf_len -= l;313consumed_ += l;314}315316if (consumed_ > 0) {317r.start = old_ring_buf.head_offset;318r.end = r.start + consumed_ - 1;319assert(r.end + 1 == qss->ring_buf.head_offset);320if (!ossl_uint_set_insert(&qss->new_set, &r)) {321qss->ring_buf = old_ring_buf;322*consumed = 0;323return 0;324}325}326327*consumed = consumed_;328return 1;329}330331static void qss_cull(QUIC_SSTREAM *qss)332{333UINT_SET_ITEM *h = ossl_list_uint_set_head(&qss->acked_set);334335/*336* Potentially cull data from our ring buffer. This can happen once data has337* been ACKed and we know we are never going to have to transmit it again.338*339* Since we use a ring buffer design for simplicity, we cannot cull byte n +340* k (for k > 0) from the ring buffer until byte n has also been culled.341* This means if parts of the stream get acknowledged out of order we might342* keep around some data we technically don't need to for a while. The343* impact of this is likely to be small and limited to quite a short344* duration, and doesn't justify the use of a more complex design.345*/346347/*348* We only need to check the first range entry in the integer set because we349* can only cull contiguous areas at the start of the ring buffer anyway.350*/351if (h != NULL)352ring_buf_cpop_range(&qss->ring_buf, h->range.start, h->range.end,353qss->cleanse);354}355356int ossl_quic_sstream_set_buffer_size(QUIC_SSTREAM *qss, size_t num_bytes)357{358return ring_buf_resize(&qss->ring_buf, num_bytes, qss->cleanse);359}360361size_t ossl_quic_sstream_get_buffer_size(QUIC_SSTREAM *qss)362{363return qss->ring_buf.alloc;364}365366size_t ossl_quic_sstream_get_buffer_used(QUIC_SSTREAM *qss)367{368return ring_buf_used(&qss->ring_buf);369}370371size_t ossl_quic_sstream_get_buffer_avail(QUIC_SSTREAM *qss)372{373return ring_buf_avail(&qss->ring_buf);374}375376int ossl_quic_sstream_is_totally_acked(QUIC_SSTREAM *qss)377{378UINT_RANGE r;379uint64_t cur_size;380381if (qss->have_final_size && !qss->acked_final_size)382return 0;383384if (ossl_quic_sstream_get_cur_size(qss) == 0)385return 1;386387if (ossl_list_uint_set_num(&qss->acked_set) != 1)388return 0;389390r = ossl_list_uint_set_head(&qss->acked_set)->range;391cur_size = qss->ring_buf.head_offset;392393/*394* The invariants of UINT_SET guarantee a single list element if we have a395* single contiguous range, which is what we should have if everything has396* been acked.397*/398assert(r.end + 1 <= cur_size);399return r.start == 0 && r.end + 1 == cur_size;400}401402void ossl_quic_sstream_adjust_iov(size_t len,403OSSL_QTX_IOVEC *iov,404size_t num_iov)405{406size_t running = 0, i, iovlen;407408for (i = 0, running = 0; i < num_iov; ++i) {409iovlen = iov[i].buf_len;410411if (running >= len)412iov[i].buf_len = 0;413else if (running + iovlen > len)414iov[i].buf_len = len - running;415416running += iovlen;417}418}419420void ossl_quic_sstream_set_cleanse(QUIC_SSTREAM *qss, int cleanse)421{422qss->cleanse = cleanse;423}424425426