Path: blob/main/crypto/openssl/ssl/quic/quic_fifd.c
48261 views
/*1* Copyright 2022-2024 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_fifd.h"10#include "internal/quic_wire.h"11#include "internal/qlog_event_helpers.h"1213DEFINE_LIST_OF(tx_history, OSSL_ACKM_TX_PKT);1415int ossl_quic_fifd_init(QUIC_FIFD *fifd,16QUIC_CFQ *cfq,17OSSL_ACKM *ackm,18QUIC_TXPIM *txpim,19/* stream_id is UINT64_MAX for the crypto stream */20QUIC_SSTREAM *(*get_sstream_by_id)(uint64_t stream_id,21uint32_t pn_space,22void *arg),23void *get_sstream_by_id_arg,24/* stream_id is UINT64_MAX if not applicable */25void (*regen_frame)(uint64_t frame_type,26uint64_t stream_id,27QUIC_TXPIM_PKT *pkt,28void *arg),29void *regen_frame_arg,30void (*confirm_frame)(uint64_t frame_type,31uint64_t stream_id,32QUIC_TXPIM_PKT *pkt,33void *arg),34void *confirm_frame_arg,35void (*sstream_updated)(uint64_t stream_id,36void *arg),37void *sstream_updated_arg,38QLOG *(*get_qlog_cb)(void *arg),39void *get_qlog_cb_arg)40{41if (cfq == NULL || ackm == NULL || txpim == NULL42|| get_sstream_by_id == NULL || regen_frame == NULL)43return 0;4445fifd->cfq = cfq;46fifd->ackm = ackm;47fifd->txpim = txpim;48fifd->get_sstream_by_id = get_sstream_by_id;49fifd->get_sstream_by_id_arg = get_sstream_by_id_arg;50fifd->regen_frame = regen_frame;51fifd->regen_frame_arg = regen_frame_arg;52fifd->confirm_frame = confirm_frame;53fifd->confirm_frame_arg = confirm_frame_arg;54fifd->sstream_updated = sstream_updated;55fifd->sstream_updated_arg = sstream_updated_arg;56fifd->get_qlog_cb = get_qlog_cb;57fifd->get_qlog_cb_arg = get_qlog_cb_arg;58return 1;59}6061void ossl_quic_fifd_cleanup(QUIC_FIFD *fifd)62{63/* No-op. */64}6566static void on_acked(void *arg)67{68QUIC_TXPIM_PKT *pkt = arg;69QUIC_FIFD *fifd = pkt->fifd;70const QUIC_TXPIM_CHUNK *chunks = ossl_quic_txpim_pkt_get_chunks(pkt);71size_t i, num_chunks = ossl_quic_txpim_pkt_get_num_chunks(pkt);72QUIC_SSTREAM *sstream;73QUIC_CFQ_ITEM *cfq_item, *cfq_item_next;7475/* STREAM and CRYPTO stream chunks, FINs and stream FC frames */76for (i = 0; i < num_chunks; ++i) {77sstream = fifd->get_sstream_by_id(chunks[i].stream_id,78pkt->ackm_pkt.pkt_space,79fifd->get_sstream_by_id_arg);80if (sstream == NULL)81continue;8283if (chunks[i].end >= chunks[i].start)84/* coverity[check_return]: Best effort - we cannot fail here. */85ossl_quic_sstream_mark_acked(sstream,86chunks[i].start, chunks[i].end);8788if (chunks[i].has_fin && chunks[i].stream_id != UINT64_MAX)89ossl_quic_sstream_mark_acked_fin(sstream);9091if (chunks[i].has_stop_sending && chunks[i].stream_id != UINT64_MAX)92fifd->confirm_frame(OSSL_QUIC_FRAME_TYPE_STOP_SENDING,93chunks[i].stream_id, pkt,94fifd->confirm_frame_arg);9596if (chunks[i].has_reset_stream && chunks[i].stream_id != UINT64_MAX)97fifd->confirm_frame(OSSL_QUIC_FRAME_TYPE_RESET_STREAM,98chunks[i].stream_id, pkt,99fifd->confirm_frame_arg);100101if (ossl_quic_sstream_is_totally_acked(sstream))102fifd->sstream_updated(chunks[i].stream_id, fifd->sstream_updated_arg);103}104105/* GCR */106for (cfq_item = pkt->retx_head; cfq_item != NULL; cfq_item = cfq_item_next) {107cfq_item_next = cfq_item->pkt_next;108ossl_quic_cfq_release(fifd->cfq, cfq_item);109}110111ossl_quic_txpim_pkt_release(fifd->txpim, pkt);112}113114static QLOG *fifd_get_qlog(QUIC_FIFD *fifd)115{116if (fifd->get_qlog_cb == NULL)117return NULL;118119return fifd->get_qlog_cb(fifd->get_qlog_cb_arg);120}121122static void on_lost(void *arg)123{124QUIC_TXPIM_PKT *pkt = arg;125QUIC_FIFD *fifd = pkt->fifd;126const QUIC_TXPIM_CHUNK *chunks = ossl_quic_txpim_pkt_get_chunks(pkt);127size_t i, num_chunks = ossl_quic_txpim_pkt_get_num_chunks(pkt);128QUIC_SSTREAM *sstream;129QUIC_CFQ_ITEM *cfq_item, *cfq_item_next;130int sstream_updated;131132ossl_qlog_event_recovery_packet_lost(fifd_get_qlog(fifd), pkt);133134/* STREAM and CRYPTO stream chunks, FIN and stream FC frames */135for (i = 0; i < num_chunks; ++i) {136sstream = fifd->get_sstream_by_id(chunks[i].stream_id,137pkt->ackm_pkt.pkt_space,138fifd->get_sstream_by_id_arg);139if (sstream == NULL)140continue;141142sstream_updated = 0;143144if (chunks[i].end >= chunks[i].start) {145/*146* Note: If the stream is being reset, we do not need to retransmit147* old data as this is pointless. In this case this will be handled148* by (sstream == NULL) above as the QSM will free the QUIC_SSTREAM149* and our call to get_sstream_by_id above will return NULL.150*/151ossl_quic_sstream_mark_lost(sstream,152chunks[i].start, chunks[i].end);153sstream_updated = 1;154}155156if (chunks[i].has_fin && chunks[i].stream_id != UINT64_MAX) {157ossl_quic_sstream_mark_lost_fin(sstream);158sstream_updated = 1;159}160161if (chunks[i].has_stop_sending && chunks[i].stream_id != UINT64_MAX)162fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_STOP_SENDING,163chunks[i].stream_id, pkt,164fifd->regen_frame_arg);165166if (chunks[i].has_reset_stream && chunks[i].stream_id != UINT64_MAX)167fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_RESET_STREAM,168chunks[i].stream_id, pkt,169fifd->regen_frame_arg);170171/*172* Inform caller that stream needs an FC frame.173*174* Note: We could track whether an FC frame was sent originally for the175* stream to determine if it really needs to be regenerated or not.176* However, if loss has occurred, it's probably better to ensure the177* peer has up-to-date flow control data for the stream. Given that178* these frames are extremely small, we may as well always send it when179* handling loss.180*/181fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA,182chunks[i].stream_id,183pkt,184fifd->regen_frame_arg);185186if (sstream_updated && chunks[i].stream_id != UINT64_MAX)187fifd->sstream_updated(chunks[i].stream_id,188fifd->sstream_updated_arg);189}190191/* GCR */192for (cfq_item = pkt->retx_head; cfq_item != NULL; cfq_item = cfq_item_next) {193cfq_item_next = cfq_item->pkt_next;194ossl_quic_cfq_mark_lost(fifd->cfq, cfq_item, UINT32_MAX);195}196197/* Regenerate flag frames */198if (pkt->had_handshake_done_frame)199fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_HANDSHAKE_DONE,200UINT64_MAX, pkt,201fifd->regen_frame_arg);202203if (pkt->had_max_data_frame)204fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_DATA,205UINT64_MAX, pkt,206fifd->regen_frame_arg);207208if (pkt->had_max_streams_bidi_frame)209fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI,210UINT64_MAX, pkt,211fifd->regen_frame_arg);212213if (pkt->had_max_streams_uni_frame)214fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_UNI,215UINT64_MAX, pkt,216fifd->regen_frame_arg);217218if (pkt->had_ack_frame)219/*220* We always use the ACK_WITH_ECN frame type to represent the ACK frame221* type in our callback; we assume it is the caller's job to decide222* whether it wants to send ECN data or not.223*/224fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_ACK_WITH_ECN,225UINT64_MAX, pkt,226fifd->regen_frame_arg);227228ossl_quic_txpim_pkt_release(fifd->txpim, pkt);229}230231static void on_discarded(void *arg)232{233QUIC_TXPIM_PKT *pkt = arg;234QUIC_FIFD *fifd = pkt->fifd;235QUIC_CFQ_ITEM *cfq_item, *cfq_item_next;236237/*238* Don't need to do anything to SSTREAMs for STREAM and CRYPTO streams, as239* we assume caller will clean them up.240*/241242/* GCR */243for (cfq_item = pkt->retx_head; cfq_item != NULL; cfq_item = cfq_item_next) {244cfq_item_next = cfq_item->pkt_next;245ossl_quic_cfq_release(fifd->cfq, cfq_item);246}247248ossl_quic_txpim_pkt_release(fifd->txpim, pkt);249}250251int ossl_quic_fifd_pkt_commit(QUIC_FIFD *fifd, QUIC_TXPIM_PKT *pkt)252{253QUIC_CFQ_ITEM *cfq_item;254const QUIC_TXPIM_CHUNK *chunks;255size_t i, num_chunks;256QUIC_SSTREAM *sstream;257258pkt->fifd = fifd;259260pkt->ackm_pkt.on_lost = on_lost;261pkt->ackm_pkt.on_acked = on_acked;262pkt->ackm_pkt.on_discarded = on_discarded;263pkt->ackm_pkt.cb_arg = pkt;264265ossl_list_tx_history_init_elem(&pkt->ackm_pkt);266pkt->ackm_pkt.anext = pkt->ackm_pkt.lnext = NULL;267268/*269* Mark the CFQ items which have been added to this packet as having been270* transmitted.271*/272for (cfq_item = pkt->retx_head;273cfq_item != NULL;274cfq_item = cfq_item->pkt_next)275ossl_quic_cfq_mark_tx(fifd->cfq, cfq_item);276277/*278* Mark the send stream chunks which have been added to the packet as having279* been transmitted.280*/281chunks = ossl_quic_txpim_pkt_get_chunks(pkt);282num_chunks = ossl_quic_txpim_pkt_get_num_chunks(pkt);283for (i = 0; i < num_chunks; ++i) {284sstream = fifd->get_sstream_by_id(chunks[i].stream_id,285pkt->ackm_pkt.pkt_space,286fifd->get_sstream_by_id_arg);287if (sstream == NULL)288continue;289290if (chunks[i].end >= chunks[i].start291&& !ossl_quic_sstream_mark_transmitted(sstream,292chunks[i].start,293chunks[i].end))294return 0;295296if (chunks[i].has_fin297&& !ossl_quic_sstream_mark_transmitted_fin(sstream,298chunks[i].end + 1))299return 0;300}301302/* Inform the ACKM. */303return ossl_ackm_on_tx_packet(fifd->ackm, &pkt->ackm_pkt);304}305306void ossl_quic_fifd_set_qlog_cb(QUIC_FIFD *fifd, QLOG *(*get_qlog_cb)(void *arg),307void *get_qlog_cb_arg)308{309fifd->get_qlog_cb = get_qlog_cb;310fifd->get_qlog_cb_arg = get_qlog_cb_arg;311}312313314