Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/openssl/ssl/quic/quic_fifd.c
48261 views
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
314