123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- /*
- * Copyright 2022-2024 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_fifd.h"
- #include "internal/quic_wire.h"
- #include "internal/qlog_event_helpers.h"
- DEFINE_LIST_OF(tx_history, OSSL_ACKM_TX_PKT);
- int ossl_quic_fifd_init(QUIC_FIFD *fifd,
- QUIC_CFQ *cfq,
- OSSL_ACKM *ackm,
- QUIC_TXPIM *txpim,
- /* stream_id is UINT64_MAX for the crypto stream */
- QUIC_SSTREAM *(*get_sstream_by_id)(uint64_t stream_id,
- uint32_t pn_space,
- void *arg),
- void *get_sstream_by_id_arg,
- /* stream_id is UINT64_MAX if not applicable */
- void (*regen_frame)(uint64_t frame_type,
- uint64_t stream_id,
- QUIC_TXPIM_PKT *pkt,
- void *arg),
- void *regen_frame_arg,
- void (*confirm_frame)(uint64_t frame_type,
- uint64_t stream_id,
- QUIC_TXPIM_PKT *pkt,
- void *arg),
- void *confirm_frame_arg,
- void (*sstream_updated)(uint64_t stream_id,
- void *arg),
- void *sstream_updated_arg,
- QLOG *(*get_qlog_cb)(void *arg),
- void *get_qlog_cb_arg)
- {
- if (cfq == NULL || ackm == NULL || txpim == NULL
- || get_sstream_by_id == NULL || regen_frame == NULL)
- return 0;
- fifd->cfq = cfq;
- fifd->ackm = ackm;
- fifd->txpim = txpim;
- fifd->get_sstream_by_id = get_sstream_by_id;
- fifd->get_sstream_by_id_arg = get_sstream_by_id_arg;
- fifd->regen_frame = regen_frame;
- fifd->regen_frame_arg = regen_frame_arg;
- fifd->confirm_frame = confirm_frame;
- fifd->confirm_frame_arg = confirm_frame_arg;
- fifd->sstream_updated = sstream_updated;
- fifd->sstream_updated_arg = sstream_updated_arg;
- fifd->get_qlog_cb = get_qlog_cb;
- fifd->get_qlog_cb_arg = get_qlog_cb_arg;
- return 1;
- }
- void ossl_quic_fifd_cleanup(QUIC_FIFD *fifd)
- {
- /* No-op. */
- }
- static void on_acked(void *arg)
- {
- QUIC_TXPIM_PKT *pkt = arg;
- QUIC_FIFD *fifd = pkt->fifd;
- const QUIC_TXPIM_CHUNK *chunks = ossl_quic_txpim_pkt_get_chunks(pkt);
- size_t i, num_chunks = ossl_quic_txpim_pkt_get_num_chunks(pkt);
- QUIC_SSTREAM *sstream;
- QUIC_CFQ_ITEM *cfq_item, *cfq_item_next;
- /* STREAM and CRYPTO stream chunks, FINs and stream FC frames */
- for (i = 0; i < num_chunks; ++i) {
- sstream = fifd->get_sstream_by_id(chunks[i].stream_id,
- pkt->ackm_pkt.pkt_space,
- fifd->get_sstream_by_id_arg);
- if (sstream == NULL)
- continue;
- if (chunks[i].end >= chunks[i].start)
- /* coverity[check_return]: Best effort - we cannot fail here. */
- ossl_quic_sstream_mark_acked(sstream,
- chunks[i].start, chunks[i].end);
- if (chunks[i].has_fin && chunks[i].stream_id != UINT64_MAX)
- ossl_quic_sstream_mark_acked_fin(sstream);
- if (chunks[i].has_stop_sending && chunks[i].stream_id != UINT64_MAX)
- fifd->confirm_frame(OSSL_QUIC_FRAME_TYPE_STOP_SENDING,
- chunks[i].stream_id, pkt,
- fifd->confirm_frame_arg);
- if (chunks[i].has_reset_stream && chunks[i].stream_id != UINT64_MAX)
- fifd->confirm_frame(OSSL_QUIC_FRAME_TYPE_RESET_STREAM,
- chunks[i].stream_id, pkt,
- fifd->confirm_frame_arg);
- if (ossl_quic_sstream_is_totally_acked(sstream))
- fifd->sstream_updated(chunks[i].stream_id, fifd->sstream_updated_arg);
- }
- /* GCR */
- for (cfq_item = pkt->retx_head; cfq_item != NULL; cfq_item = cfq_item_next) {
- cfq_item_next = cfq_item->pkt_next;
- ossl_quic_cfq_release(fifd->cfq, cfq_item);
- }
- ossl_quic_txpim_pkt_release(fifd->txpim, pkt);
- }
- static QLOG *fifd_get_qlog(QUIC_FIFD *fifd)
- {
- if (fifd->get_qlog_cb == NULL)
- return NULL;
- return fifd->get_qlog_cb(fifd->get_qlog_cb_arg);
- }
- static void on_lost(void *arg)
- {
- QUIC_TXPIM_PKT *pkt = arg;
- QUIC_FIFD *fifd = pkt->fifd;
- const QUIC_TXPIM_CHUNK *chunks = ossl_quic_txpim_pkt_get_chunks(pkt);
- size_t i, num_chunks = ossl_quic_txpim_pkt_get_num_chunks(pkt);
- QUIC_SSTREAM *sstream;
- QUIC_CFQ_ITEM *cfq_item, *cfq_item_next;
- int sstream_updated;
- ossl_qlog_event_recovery_packet_lost(fifd_get_qlog(fifd), pkt);
- /* STREAM and CRYPTO stream chunks, FIN and stream FC frames */
- for (i = 0; i < num_chunks; ++i) {
- sstream = fifd->get_sstream_by_id(chunks[i].stream_id,
- pkt->ackm_pkt.pkt_space,
- fifd->get_sstream_by_id_arg);
- if (sstream == NULL)
- continue;
- sstream_updated = 0;
- if (chunks[i].end >= chunks[i].start) {
- /*
- * Note: If the stream is being reset, we do not need to retransmit
- * old data as this is pointless. In this case this will be handled
- * by (sstream == NULL) above as the QSM will free the QUIC_SSTREAM
- * and our call to get_sstream_by_id above will return NULL.
- */
- ossl_quic_sstream_mark_lost(sstream,
- chunks[i].start, chunks[i].end);
- sstream_updated = 1;
- }
- if (chunks[i].has_fin && chunks[i].stream_id != UINT64_MAX) {
- ossl_quic_sstream_mark_lost_fin(sstream);
- sstream_updated = 1;
- }
- if (chunks[i].has_stop_sending && chunks[i].stream_id != UINT64_MAX)
- fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_STOP_SENDING,
- chunks[i].stream_id, pkt,
- fifd->regen_frame_arg);
- if (chunks[i].has_reset_stream && chunks[i].stream_id != UINT64_MAX)
- fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_RESET_STREAM,
- chunks[i].stream_id, pkt,
- fifd->regen_frame_arg);
- /*
- * Inform caller that stream needs an FC frame.
- *
- * Note: We could track whether an FC frame was sent originally for the
- * stream to determine if it really needs to be regenerated or not.
- * However, if loss has occurred, it's probably better to ensure the
- * peer has up-to-date flow control data for the stream. Given that
- * these frames are extremely small, we may as well always send it when
- * handling loss.
- */
- fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA,
- chunks[i].stream_id,
- pkt,
- fifd->regen_frame_arg);
- if (sstream_updated && chunks[i].stream_id != UINT64_MAX)
- fifd->sstream_updated(chunks[i].stream_id,
- fifd->sstream_updated_arg);
- }
- /* GCR */
- for (cfq_item = pkt->retx_head; cfq_item != NULL; cfq_item = cfq_item_next) {
- cfq_item_next = cfq_item->pkt_next;
- ossl_quic_cfq_mark_lost(fifd->cfq, cfq_item, UINT32_MAX);
- }
- /* Regenerate flag frames */
- if (pkt->had_handshake_done_frame)
- fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_HANDSHAKE_DONE,
- UINT64_MAX, pkt,
- fifd->regen_frame_arg);
- if (pkt->had_max_data_frame)
- fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_DATA,
- UINT64_MAX, pkt,
- fifd->regen_frame_arg);
- if (pkt->had_max_streams_bidi_frame)
- fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI,
- UINT64_MAX, pkt,
- fifd->regen_frame_arg);
- if (pkt->had_max_streams_uni_frame)
- fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_UNI,
- UINT64_MAX, pkt,
- fifd->regen_frame_arg);
- if (pkt->had_ack_frame)
- /*
- * We always use the ACK_WITH_ECN frame type to represent the ACK frame
- * type in our callback; we assume it is the caller's job to decide
- * whether it wants to send ECN data or not.
- */
- fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_ACK_WITH_ECN,
- UINT64_MAX, pkt,
- fifd->regen_frame_arg);
- ossl_quic_txpim_pkt_release(fifd->txpim, pkt);
- }
- static void on_discarded(void *arg)
- {
- QUIC_TXPIM_PKT *pkt = arg;
- QUIC_FIFD *fifd = pkt->fifd;
- QUIC_CFQ_ITEM *cfq_item, *cfq_item_next;
- /*
- * Don't need to do anything to SSTREAMs for STREAM and CRYPTO streams, as
- * we assume caller will clean them up.
- */
- /* GCR */
- for (cfq_item = pkt->retx_head; cfq_item != NULL; cfq_item = cfq_item_next) {
- cfq_item_next = cfq_item->pkt_next;
- ossl_quic_cfq_release(fifd->cfq, cfq_item);
- }
- ossl_quic_txpim_pkt_release(fifd->txpim, pkt);
- }
- int ossl_quic_fifd_pkt_commit(QUIC_FIFD *fifd, QUIC_TXPIM_PKT *pkt)
- {
- QUIC_CFQ_ITEM *cfq_item;
- const QUIC_TXPIM_CHUNK *chunks;
- size_t i, num_chunks;
- QUIC_SSTREAM *sstream;
- pkt->fifd = fifd;
- pkt->ackm_pkt.on_lost = on_lost;
- pkt->ackm_pkt.on_acked = on_acked;
- pkt->ackm_pkt.on_discarded = on_discarded;
- pkt->ackm_pkt.cb_arg = pkt;
- ossl_list_tx_history_init_elem(&pkt->ackm_pkt);
- pkt->ackm_pkt.anext = pkt->ackm_pkt.lnext = NULL;
- /*
- * Mark the CFQ items which have been added to this packet as having been
- * transmitted.
- */
- for (cfq_item = pkt->retx_head;
- cfq_item != NULL;
- cfq_item = cfq_item->pkt_next)
- ossl_quic_cfq_mark_tx(fifd->cfq, cfq_item);
- /*
- * Mark the send stream chunks which have been added to the packet as having
- * been transmitted.
- */
- chunks = ossl_quic_txpim_pkt_get_chunks(pkt);
- num_chunks = ossl_quic_txpim_pkt_get_num_chunks(pkt);
- for (i = 0; i < num_chunks; ++i) {
- sstream = fifd->get_sstream_by_id(chunks[i].stream_id,
- pkt->ackm_pkt.pkt_space,
- fifd->get_sstream_by_id_arg);
- if (sstream == NULL)
- continue;
- if (chunks[i].end >= chunks[i].start
- && !ossl_quic_sstream_mark_transmitted(sstream,
- chunks[i].start,
- chunks[i].end))
- return 0;
- if (chunks[i].has_fin
- && !ossl_quic_sstream_mark_transmitted_fin(sstream,
- chunks[i].end + 1))
- return 0;
- }
- /* Inform the ACKM. */
- return ossl_ackm_on_tx_packet(fifd->ackm, &pkt->ackm_pkt);
- }
- void ossl_quic_fifd_set_qlog_cb(QUIC_FIFD *fifd, QLOG *(*get_qlog_cb)(void *arg),
- void *get_qlog_cb_arg)
- {
- fifd->get_qlog_cb = get_qlog_cb;
- fifd->get_qlog_cb_arg = get_qlog_cb_arg;
- }
|