2 * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
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
10 #include "internal/quic_fifd.h"
11 #include "internal/quic_wire.h"
12 #include "internal/qlog_event_helpers.h"
14 DEFINE_LIST_OF(tx_history
, OSSL_ACKM_TX_PKT
);
16 int ossl_quic_fifd_init(QUIC_FIFD
*fifd
,
20 /* stream_id is UINT64_MAX for the crypto stream */
21 QUIC_SSTREAM
*(*get_sstream_by_id
)(uint64_t stream_id
,
24 void *get_sstream_by_id_arg
,
25 /* stream_id is UINT64_MAX if not applicable */
26 void (*regen_frame
)(uint64_t frame_type
,
30 void *regen_frame_arg
,
31 void (*confirm_frame
)(uint64_t frame_type
,
35 void *confirm_frame_arg
,
36 void (*sstream_updated
)(uint64_t stream_id
,
38 void *sstream_updated_arg
,
39 QLOG
*(*get_qlog_cb
)(void *arg
),
40 void *get_qlog_cb_arg
)
42 if (cfq
== NULL
|| ackm
== NULL
|| txpim
== NULL
43 || get_sstream_by_id
== NULL
|| regen_frame
== NULL
)
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
;
62 void ossl_quic_fifd_cleanup(QUIC_FIFD
*fifd
)
67 static void on_acked(void *arg
)
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
;
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
);
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
);
89 if (chunks
[i
].has_fin
&& chunks
[i
].stream_id
!= UINT64_MAX
)
90 ossl_quic_sstream_mark_acked_fin(sstream
);
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
);
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
);
102 if (ossl_quic_sstream_is_totally_acked(sstream
))
103 fifd
->sstream_updated(chunks
[i
].stream_id
, fifd
->sstream_updated_arg
);
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
);
112 ossl_quic_txpim_pkt_release(fifd
->txpim
, pkt
);
115 static QLOG
*fifd_get_qlog(QUIC_FIFD
*fifd
)
117 if (fifd
->get_qlog_cb
== NULL
)
120 return fifd
->get_qlog_cb(fifd
->get_qlog_cb_arg
);
123 static void on_lost(void *arg
)
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
;
133 ossl_qlog_event_recovery_packet_lost(fifd_get_qlog(fifd
), pkt
);
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
);
145 if (chunks
[i
].end
>= chunks
[i
].start
) {
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.
152 ossl_quic_sstream_mark_lost(sstream
,
153 chunks
[i
].start
, chunks
[i
].end
);
157 if (chunks
[i
].has_fin
&& chunks
[i
].stream_id
!= UINT64_MAX
) {
158 ossl_quic_sstream_mark_lost_fin(sstream
);
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
);
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
);
173 * Inform caller that stream needs an FC frame.
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
182 fifd
->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA
,
185 fifd
->regen_frame_arg
);
187 if (sstream_updated
&& chunks
[i
].stream_id
!= UINT64_MAX
)
188 fifd
->sstream_updated(chunks
[i
].stream_id
,
189 fifd
->sstream_updated_arg
);
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
);
198 /* Regenerate flag frames */
199 if (pkt
->had_handshake_done_frame
)
200 fifd
->regen_frame(OSSL_QUIC_FRAME_TYPE_HANDSHAKE_DONE
,
202 fifd
->regen_frame_arg
);
204 if (pkt
->had_max_data_frame
)
205 fifd
->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_DATA
,
207 fifd
->regen_frame_arg
);
209 if (pkt
->had_max_streams_bidi_frame
)
210 fifd
->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI
,
212 fifd
->regen_frame_arg
);
214 if (pkt
->had_max_streams_uni_frame
)
215 fifd
->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_UNI
,
217 fifd
->regen_frame_arg
);
219 if (pkt
->had_ack_frame
)
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.
225 fifd
->regen_frame(OSSL_QUIC_FRAME_TYPE_ACK_WITH_ECN
,
227 fifd
->regen_frame_arg
);
229 ossl_quic_txpim_pkt_release(fifd
->txpim
, pkt
);
232 static void on_discarded(void *arg
)
234 QUIC_TXPIM_PKT
*pkt
= arg
;
235 QUIC_FIFD
*fifd
= pkt
->fifd
;
236 QUIC_CFQ_ITEM
*cfq_item
, *cfq_item_next
;
239 * Don't need to do anything to SSTREAMs for STREAM and CRYPTO streams, as
240 * we assume caller will clean them up.
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
);
249 ossl_quic_txpim_pkt_release(fifd
->txpim
, pkt
);
252 int ossl_quic_fifd_pkt_commit(QUIC_FIFD
*fifd
, QUIC_TXPIM_PKT
*pkt
)
254 QUIC_CFQ_ITEM
*cfq_item
;
255 const QUIC_TXPIM_CHUNK
*chunks
;
256 size_t i
, num_chunks
;
257 QUIC_SSTREAM
*sstream
;
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
;
266 ossl_list_tx_history_init_elem(&pkt
->ackm_pkt
);
267 pkt
->ackm_pkt
.anext
= pkt
->ackm_pkt
.lnext
= NULL
;
270 * Mark the CFQ items which have been added to this packet as having been
273 for (cfq_item
= pkt
->retx_head
;
275 cfq_item
= cfq_item
->pkt_next
)
276 ossl_quic_cfq_mark_tx(fifd
->cfq
, cfq_item
);
279 * Mark the send stream chunks which have been added to the packet as having
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
);
291 if (chunks
[i
].end
>= chunks
[i
].start
292 && !ossl_quic_sstream_mark_transmitted(sstream
,
297 if (chunks
[i
].has_fin
298 && !ossl_quic_sstream_mark_transmitted_fin(sstream
,
303 /* Inform the ACKM. */
304 return ossl_ackm_on_tx_packet(fifd
->ackm
, &pkt
->ackm_pkt
);
307 void ossl_quic_fifd_set_qlog_cb(QUIC_FIFD
*fifd
, QLOG
*(*get_qlog_cb
)(void *arg
),
308 void *get_qlog_cb_arg
)
310 fifd
->get_qlog_cb
= get_qlog_cb
;
311 fifd
->get_qlog_cb_arg
= get_qlog_cb_arg
;