]> git.ipfire.org Git - thirdparty/openssl.git/blob - ssl/quic/quic_fifd.c
89e0c3ca01b7637a4debba054ce41f89ca26c975
[thirdparty/openssl.git] / ssl / quic / quic_fifd.c
1 /*
2 * Copyright 2022-2023 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 }