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