1 /* 2 * Copyright 2022-2024 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the Apache License 2.0 (the "License"). You may not use 5 * this file except in compliance with the License. You can obtain a copy 6 * in the file LICENSE in the source distribution or at 7 * https://www.openssl.org/source/license.html 8 */ 9 10 #include "internal/quic_fifd.h" 11 #include "internal/quic_wire.h" 12 #include "internal/qlog_event_helpers.h" 13 14 DEFINE_LIST_OF(tx_history, OSSL_ACKM_TX_PKT); 15 16 int ossl_quic_fifd_init(QUIC_FIFD *fifd, 17 QUIC_CFQ *cfq, 18 OSSL_ACKM *ackm, 19 QUIC_TXPIM *txpim, 20 /* stream_id is UINT64_MAX for the crypto stream */ 21 QUIC_SSTREAM *(*get_sstream_by_id)(uint64_t stream_id, 22 uint32_t pn_space, 23 void *arg), 24 void *get_sstream_by_id_arg, 25 /* stream_id is UINT64_MAX if not applicable */ 26 void (*regen_frame)(uint64_t frame_type, 27 uint64_t stream_id, 28 QUIC_TXPIM_PKT *pkt, 29 void *arg), 30 void *regen_frame_arg, 31 void (*confirm_frame)(uint64_t frame_type, 32 uint64_t stream_id, 33 QUIC_TXPIM_PKT *pkt, 34 void *arg), 35 void *confirm_frame_arg, 36 void (*sstream_updated)(uint64_t stream_id, 37 void *arg), 38 void *sstream_updated_arg, 39 QLOG *(*get_qlog_cb)(void *arg), 40 void *get_qlog_cb_arg) 41 { 42 if (cfq == NULL || ackm == NULL || txpim == NULL 43 || get_sstream_by_id == NULL || regen_frame == NULL) 44 return 0; 45 46 fifd->cfq = cfq; 47 fifd->ackm = ackm; 48 fifd->txpim = txpim; 49 fifd->get_sstream_by_id = get_sstream_by_id; 50 fifd->get_sstream_by_id_arg = get_sstream_by_id_arg; 51 fifd->regen_frame = regen_frame; 52 fifd->regen_frame_arg = regen_frame_arg; 53 fifd->confirm_frame = confirm_frame; 54 fifd->confirm_frame_arg = confirm_frame_arg; 55 fifd->sstream_updated = sstream_updated; 56 fifd->sstream_updated_arg = sstream_updated_arg; 57 fifd->get_qlog_cb = get_qlog_cb; 58 fifd->get_qlog_cb_arg = get_qlog_cb_arg; 59 return 1; 60 } 61 62 void ossl_quic_fifd_cleanup(QUIC_FIFD *fifd) 63 { 64 /* No-op. */ 65 } 66 67 static void on_acked(void *arg) 68 { 69 QUIC_TXPIM_PKT *pkt = arg; 70 QUIC_FIFD *fifd = pkt->fifd; 71 const QUIC_TXPIM_CHUNK *chunks = ossl_quic_txpim_pkt_get_chunks(pkt); 72 size_t i, num_chunks = ossl_quic_txpim_pkt_get_num_chunks(pkt); 73 QUIC_SSTREAM *sstream; 74 QUIC_CFQ_ITEM *cfq_item, *cfq_item_next; 75 76 /* STREAM and CRYPTO stream chunks, FINs and stream FC frames */ 77 for (i = 0; i < num_chunks; ++i) { 78 sstream = fifd->get_sstream_by_id(chunks[i].stream_id, 79 pkt->ackm_pkt.pkt_space, 80 fifd->get_sstream_by_id_arg); 81 if (sstream == NULL) 82 continue; 83 84 if (chunks[i].end >= chunks[i].start) 85 /* coverity[check_return]: Best effort - we cannot fail here. */ 86 ossl_quic_sstream_mark_acked(sstream, 87 chunks[i].start, chunks[i].end); 88 89 if (chunks[i].has_fin && chunks[i].stream_id != UINT64_MAX) 90 ossl_quic_sstream_mark_acked_fin(sstream); 91 92 if (chunks[i].has_stop_sending && chunks[i].stream_id != UINT64_MAX) 93 fifd->confirm_frame(OSSL_QUIC_FRAME_TYPE_STOP_SENDING, 94 chunks[i].stream_id, pkt, 95 fifd->confirm_frame_arg); 96 97 if (chunks[i].has_reset_stream && chunks[i].stream_id != UINT64_MAX) 98 fifd->confirm_frame(OSSL_QUIC_FRAME_TYPE_RESET_STREAM, 99 chunks[i].stream_id, pkt, 100 fifd->confirm_frame_arg); 101 102 if (ossl_quic_sstream_is_totally_acked(sstream)) 103 fifd->sstream_updated(chunks[i].stream_id, fifd->sstream_updated_arg); 104 } 105 106 /* GCR */ 107 for (cfq_item = pkt->retx_head; cfq_item != NULL; cfq_item = cfq_item_next) { 108 cfq_item_next = cfq_item->pkt_next; 109 ossl_quic_cfq_release(fifd->cfq, cfq_item); 110 } 111 112 ossl_quic_txpim_pkt_release(fifd->txpim, pkt); 113 } 114 115 static QLOG *fifd_get_qlog(QUIC_FIFD *fifd) 116 { 117 if (fifd->get_qlog_cb == NULL) 118 return NULL; 119 120 return fifd->get_qlog_cb(fifd->get_qlog_cb_arg); 121 } 122 123 static void on_lost(void *arg) 124 { 125 QUIC_TXPIM_PKT *pkt = arg; 126 QUIC_FIFD *fifd = pkt->fifd; 127 const QUIC_TXPIM_CHUNK *chunks = ossl_quic_txpim_pkt_get_chunks(pkt); 128 size_t i, num_chunks = ossl_quic_txpim_pkt_get_num_chunks(pkt); 129 QUIC_SSTREAM *sstream; 130 QUIC_CFQ_ITEM *cfq_item, *cfq_item_next; 131 int sstream_updated; 132 133 ossl_qlog_event_recovery_packet_lost(fifd_get_qlog(fifd), pkt); 134 135 /* STREAM and CRYPTO stream chunks, FIN and stream FC frames */ 136 for (i = 0; i < num_chunks; ++i) { 137 sstream = fifd->get_sstream_by_id(chunks[i].stream_id, 138 pkt->ackm_pkt.pkt_space, 139 fifd->get_sstream_by_id_arg); 140 if (sstream == NULL) 141 continue; 142 143 sstream_updated = 0; 144 145 if (chunks[i].end >= chunks[i].start) { 146 /* 147 * Note: If the stream is being reset, we do not need to retransmit 148 * old data as this is pointless. In this case this will be handled 149 * by (sstream == NULL) above as the QSM will free the QUIC_SSTREAM 150 * and our call to get_sstream_by_id above will return NULL. 151 */ 152 ossl_quic_sstream_mark_lost(sstream, 153 chunks[i].start, chunks[i].end); 154 sstream_updated = 1; 155 } 156 157 if (chunks[i].has_fin && chunks[i].stream_id != UINT64_MAX) { 158 ossl_quic_sstream_mark_lost_fin(sstream); 159 sstream_updated = 1; 160 } 161 162 if (chunks[i].has_stop_sending && chunks[i].stream_id != UINT64_MAX) 163 fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_STOP_SENDING, 164 chunks[i].stream_id, pkt, 165 fifd->regen_frame_arg); 166 167 if (chunks[i].has_reset_stream && chunks[i].stream_id != UINT64_MAX) 168 fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_RESET_STREAM, 169 chunks[i].stream_id, pkt, 170 fifd->regen_frame_arg); 171 172 /* 173 * Inform caller that stream needs an FC frame. 174 * 175 * Note: We could track whether an FC frame was sent originally for the 176 * stream to determine if it really needs to be regenerated or not. 177 * However, if loss has occurred, it's probably better to ensure the 178 * peer has up-to-date flow control data for the stream. Given that 179 * these frames are extremely small, we may as well always send it when 180 * handling loss. 181 */ 182 fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA, 183 chunks[i].stream_id, 184 pkt, 185 fifd->regen_frame_arg); 186 187 if (sstream_updated && chunks[i].stream_id != UINT64_MAX) 188 fifd->sstream_updated(chunks[i].stream_id, 189 fifd->sstream_updated_arg); 190 } 191 192 /* GCR */ 193 for (cfq_item = pkt->retx_head; cfq_item != NULL; cfq_item = cfq_item_next) { 194 cfq_item_next = cfq_item->pkt_next; 195 ossl_quic_cfq_mark_lost(fifd->cfq, cfq_item, UINT32_MAX); 196 } 197 198 /* Regenerate flag frames */ 199 if (pkt->had_handshake_done_frame) 200 fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_HANDSHAKE_DONE, 201 UINT64_MAX, pkt, 202 fifd->regen_frame_arg); 203 204 if (pkt->had_max_data_frame) 205 fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_DATA, 206 UINT64_MAX, pkt, 207 fifd->regen_frame_arg); 208 209 if (pkt->had_max_streams_bidi_frame) 210 fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI, 211 UINT64_MAX, pkt, 212 fifd->regen_frame_arg); 213 214 if (pkt->had_max_streams_uni_frame) 215 fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_UNI, 216 UINT64_MAX, pkt, 217 fifd->regen_frame_arg); 218 219 if (pkt->had_ack_frame) 220 /* 221 * We always use the ACK_WITH_ECN frame type to represent the ACK frame 222 * type in our callback; we assume it is the caller's job to decide 223 * whether it wants to send ECN data or not. 224 */ 225 fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_ACK_WITH_ECN, 226 UINT64_MAX, pkt, 227 fifd->regen_frame_arg); 228 229 ossl_quic_txpim_pkt_release(fifd->txpim, pkt); 230 } 231 232 static void on_discarded(void *arg) 233 { 234 QUIC_TXPIM_PKT *pkt = arg; 235 QUIC_FIFD *fifd = pkt->fifd; 236 QUIC_CFQ_ITEM *cfq_item, *cfq_item_next; 237 238 /* 239 * Don't need to do anything to SSTREAMs for STREAM and CRYPTO streams, as 240 * we assume caller will clean them up. 241 */ 242 243 /* GCR */ 244 for (cfq_item = pkt->retx_head; cfq_item != NULL; cfq_item = cfq_item_next) { 245 cfq_item_next = cfq_item->pkt_next; 246 ossl_quic_cfq_release(fifd->cfq, cfq_item); 247 } 248 249 ossl_quic_txpim_pkt_release(fifd->txpim, pkt); 250 } 251 252 int ossl_quic_fifd_pkt_commit(QUIC_FIFD *fifd, QUIC_TXPIM_PKT *pkt) 253 { 254 QUIC_CFQ_ITEM *cfq_item; 255 const QUIC_TXPIM_CHUNK *chunks; 256 size_t i, num_chunks; 257 QUIC_SSTREAM *sstream; 258 259 pkt->fifd = fifd; 260 261 pkt->ackm_pkt.on_lost = on_lost; 262 pkt->ackm_pkt.on_acked = on_acked; 263 pkt->ackm_pkt.on_discarded = on_discarded; 264 pkt->ackm_pkt.cb_arg = pkt; 265 266 ossl_list_tx_history_init_elem(&pkt->ackm_pkt); 267 pkt->ackm_pkt.anext = pkt->ackm_pkt.lnext = NULL; 268 269 /* 270 * Mark the CFQ items which have been added to this packet as having been 271 * transmitted. 272 */ 273 for (cfq_item = pkt->retx_head; 274 cfq_item != NULL; 275 cfq_item = cfq_item->pkt_next) 276 ossl_quic_cfq_mark_tx(fifd->cfq, cfq_item); 277 278 /* 279 * Mark the send stream chunks which have been added to the packet as having 280 * been transmitted. 281 */ 282 chunks = ossl_quic_txpim_pkt_get_chunks(pkt); 283 num_chunks = ossl_quic_txpim_pkt_get_num_chunks(pkt); 284 for (i = 0; i < num_chunks; ++i) { 285 sstream = fifd->get_sstream_by_id(chunks[i].stream_id, 286 pkt->ackm_pkt.pkt_space, 287 fifd->get_sstream_by_id_arg); 288 if (sstream == NULL) 289 continue; 290 291 if (chunks[i].end >= chunks[i].start 292 && !ossl_quic_sstream_mark_transmitted(sstream, 293 chunks[i].start, 294 chunks[i].end)) 295 return 0; 296 297 if (chunks[i].has_fin 298 && !ossl_quic_sstream_mark_transmitted_fin(sstream, 299 chunks[i].end + 1)) 300 return 0; 301 } 302 303 /* Inform the ACKM. */ 304 return ossl_ackm_on_tx_packet(fifd->ackm, &pkt->ackm_pkt); 305 } 306 307 void ossl_quic_fifd_set_qlog_cb(QUIC_FIFD *fifd, QLOG *(*get_qlog_cb)(void *arg), 308 void *get_qlog_cb_arg) 309 { 310 fifd->get_qlog_cb = get_qlog_cb; 311 fifd->get_qlog_cb_arg = get_qlog_cb_arg; 312 } 313