]> git.ipfire.org Git - thirdparty/openssl.git/blob - ssl/quic/quic_sstream.c
Copyright year updates
[thirdparty/openssl.git] / ssl / quic / quic_sstream.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_stream.h"
11 #include "internal/uint_set.h"
12 #include "internal/common.h"
13 #include "internal/ring_buf.h"
14
15 /*
16 * ==================================================================
17 * QUIC Send Stream
18 */
19 struct quic_sstream_st {
20 struct ring_buf ring_buf;
21
22 /*
23 * Any logical byte in the stream is in one of these states:
24 *
25 * - NEW: The byte has not yet been transmitted, or has been lost and is
26 * in need of retransmission.
27 *
28 * - IN_FLIGHT: The byte has been transmitted but is awaiting
29 * acknowledgement. We continue to store the data in case we return
30 * to the NEW state.
31 *
32 * - ACKED: The byte has been acknowledged and we can cease storing it.
33 * We do not necessarily cull it immediately, so there may be a delay
34 * between reaching the ACKED state and the buffer space actually being
35 * recycled.
36 *
37 * A logical byte in the stream is
38 *
39 * - in the NEW state if it is in new_set;
40 * - is in the ACKED state if it is in acked_set
41 * (and may or may not have been culled);
42 * - is in the IN_FLIGHT state otherwise.
43 *
44 * Invariant: No logical byte is ever in both new_set and acked_set.
45 */
46 UINT_SET new_set, acked_set;
47
48 /*
49 * The current size of the stream is ring_buf.head_offset. If
50 * have_final_size is true, this is also the final size of the stream.
51 */
52 unsigned int have_final_size : 1;
53 unsigned int sent_final_size : 1;
54 unsigned int acked_final_size : 1;
55 unsigned int cleanse : 1;
56 };
57
58 static void qss_cull(QUIC_SSTREAM *qss);
59
60 QUIC_SSTREAM *ossl_quic_sstream_new(size_t init_buf_size)
61 {
62 QUIC_SSTREAM *qss;
63
64 qss = OPENSSL_zalloc(sizeof(QUIC_SSTREAM));
65 if (qss == NULL)
66 return NULL;
67
68 ring_buf_init(&qss->ring_buf);
69 if (!ring_buf_resize(&qss->ring_buf, init_buf_size, 0)) {
70 ring_buf_destroy(&qss->ring_buf, 0);
71 OPENSSL_free(qss);
72 return NULL;
73 }
74
75 ossl_uint_set_init(&qss->new_set);
76 ossl_uint_set_init(&qss->acked_set);
77 return qss;
78 }
79
80 void ossl_quic_sstream_free(QUIC_SSTREAM *qss)
81 {
82 if (qss == NULL)
83 return;
84
85 ossl_uint_set_destroy(&qss->new_set);
86 ossl_uint_set_destroy(&qss->acked_set);
87 ring_buf_destroy(&qss->ring_buf, qss->cleanse);
88 OPENSSL_free(qss);
89 }
90
91 int ossl_quic_sstream_get_stream_frame(QUIC_SSTREAM *qss,
92 size_t skip,
93 OSSL_QUIC_FRAME_STREAM *hdr,
94 OSSL_QTX_IOVEC *iov,
95 size_t *num_iov)
96 {
97 size_t num_iov_ = 0, src_len = 0, total_len = 0, i;
98 uint64_t max_len;
99 const unsigned char *src = NULL;
100 UINT_SET_ITEM *range = ossl_list_uint_set_head(&qss->new_set);
101
102 if (*num_iov < 2)
103 return 0;
104
105 for (i = 0; i < skip && range != NULL; ++i)
106 range = ossl_list_uint_set_next(range);
107
108 if (range == NULL) {
109 if (i < skip)
110 /* Don't return FIN for infinitely increasing skip */
111 return 0;
112
113 /* No new bytes to send, but we might have a FIN */
114 if (!qss->have_final_size || qss->sent_final_size)
115 return 0;
116
117 hdr->offset = qss->ring_buf.head_offset;
118 hdr->len = 0;
119 hdr->is_fin = 1;
120 *num_iov = 0;
121 return 1;
122 }
123
124 /*
125 * We can only send a contiguous range of logical bytes in a single
126 * stream frame, so limit ourselves to the range of the first set entry.
127 *
128 * Set entries never have 'adjacent' entries so we don't have to worry
129 * about them here.
130 */
131 max_len = range->range.end - range->range.start + 1;
132
133 for (i = 0;; ++i) {
134 if (total_len >= max_len)
135 break;
136
137 if (!ring_buf_get_buf_at(&qss->ring_buf,
138 range->range.start + total_len,
139 &src, &src_len))
140 return 0;
141
142 if (src_len == 0)
143 break;
144
145 assert(i < 2);
146
147 if (total_len + src_len > max_len)
148 src_len = (size_t)(max_len - total_len);
149
150 iov[num_iov_].buf = src;
151 iov[num_iov_].buf_len = src_len;
152
153 total_len += src_len;
154 ++num_iov_;
155 }
156
157 hdr->offset = range->range.start;
158 hdr->len = total_len;
159 hdr->is_fin = qss->have_final_size
160 && hdr->offset + hdr->len == qss->ring_buf.head_offset;
161
162 *num_iov = num_iov_;
163 return 1;
164 }
165
166 int ossl_quic_sstream_has_pending(QUIC_SSTREAM *qss)
167 {
168 OSSL_QUIC_FRAME_STREAM shdr;
169 OSSL_QTX_IOVEC iov[2];
170 size_t num_iov = OSSL_NELEM(iov);
171
172 return ossl_quic_sstream_get_stream_frame(qss, 0, &shdr, iov, &num_iov);
173 }
174
175 uint64_t ossl_quic_sstream_get_cur_size(QUIC_SSTREAM *qss)
176 {
177 return qss->ring_buf.head_offset;
178 }
179
180 int ossl_quic_sstream_mark_transmitted(QUIC_SSTREAM *qss,
181 uint64_t start,
182 uint64_t end)
183 {
184 UINT_RANGE r;
185
186 r.start = start;
187 r.end = end;
188
189 if (!ossl_uint_set_remove(&qss->new_set, &r))
190 return 0;
191
192 return 1;
193 }
194
195 int ossl_quic_sstream_mark_transmitted_fin(QUIC_SSTREAM *qss,
196 uint64_t final_size)
197 {
198 /*
199 * We do not really need final_size since we already know the size of the
200 * stream, but this serves as a sanity check.
201 */
202 if (!qss->have_final_size || final_size != qss->ring_buf.head_offset)
203 return 0;
204
205 qss->sent_final_size = 1;
206 return 1;
207 }
208
209 int ossl_quic_sstream_mark_lost(QUIC_SSTREAM *qss,
210 uint64_t start,
211 uint64_t end)
212 {
213 UINT_RANGE r;
214 r.start = start;
215 r.end = end;
216
217 /*
218 * We lost a range of stream data bytes, so reinsert them into the new set,
219 * so that they are returned once more by ossl_quic_sstream_get_stream_frame.
220 */
221 if (!ossl_uint_set_insert(&qss->new_set, &r))
222 return 0;
223
224 return 1;
225 }
226
227 int ossl_quic_sstream_mark_lost_fin(QUIC_SSTREAM *qss)
228 {
229 if (qss->acked_final_size)
230 /* Does not make sense to lose a FIN after it has been ACKed */
231 return 0;
232
233 /* FIN was lost, so we need to transmit it again. */
234 qss->sent_final_size = 0;
235 return 1;
236 }
237
238 int ossl_quic_sstream_mark_acked(QUIC_SSTREAM *qss,
239 uint64_t start,
240 uint64_t end)
241 {
242 UINT_RANGE r;
243 r.start = start;
244 r.end = end;
245
246 if (!ossl_uint_set_insert(&qss->acked_set, &r))
247 return 0;
248
249 qss_cull(qss);
250 return 1;
251 }
252
253 int ossl_quic_sstream_mark_acked_fin(QUIC_SSTREAM *qss)
254 {
255 if (!qss->have_final_size)
256 /* Cannot ack final size before we have a final size */
257 return 0;
258
259 qss->acked_final_size = 1;
260 return 1;
261 }
262
263 void ossl_quic_sstream_fin(QUIC_SSTREAM *qss)
264 {
265 if (qss->have_final_size)
266 return;
267
268 qss->have_final_size = 1;
269 }
270
271 int ossl_quic_sstream_get_final_size(QUIC_SSTREAM *qss, uint64_t *final_size)
272 {
273 if (!qss->have_final_size)
274 return 0;
275
276 if (final_size != NULL)
277 *final_size = qss->ring_buf.head_offset;
278
279 return 1;
280 }
281
282 int ossl_quic_sstream_append(QUIC_SSTREAM *qss,
283 const unsigned char *buf,
284 size_t buf_len,
285 size_t *consumed)
286 {
287 size_t l, consumed_ = 0;
288 UINT_RANGE r;
289 struct ring_buf old_ring_buf = qss->ring_buf;
290
291 if (qss->have_final_size) {
292 *consumed = 0;
293 return 0;
294 }
295
296 /*
297 * Note: It is assumed that ossl_quic_sstream_append will be called during a
298 * call to e.g. SSL_write and this function is therefore designed to support
299 * such semantics. In particular, the buffer pointed to by buf is only
300 * assumed to be valid for the duration of this call, therefore we must copy
301 * the data here. We will later copy-and-encrypt the data during packet
302 * encryption, so this is a two-copy design. Supporting a one-copy design in
303 * the future will require applications to use a different kind of API.
304 * Supporting such changes in future will require corresponding enhancements
305 * to this code.
306 */
307 while (buf_len > 0) {
308 l = ring_buf_push(&qss->ring_buf, buf, buf_len);
309 if (l == 0)
310 break;
311
312 buf += l;
313 buf_len -= l;
314 consumed_ += l;
315 }
316
317 if (consumed_ > 0) {
318 r.start = old_ring_buf.head_offset;
319 r.end = r.start + consumed_ - 1;
320 assert(r.end + 1 == qss->ring_buf.head_offset);
321 if (!ossl_uint_set_insert(&qss->new_set, &r)) {
322 qss->ring_buf = old_ring_buf;
323 *consumed = 0;
324 return 0;
325 }
326 }
327
328 *consumed = consumed_;
329 return 1;
330 }
331
332 static void qss_cull(QUIC_SSTREAM *qss)
333 {
334 UINT_SET_ITEM *h = ossl_list_uint_set_head(&qss->acked_set);
335
336 /*
337 * Potentially cull data from our ring buffer. This can happen once data has
338 * been ACKed and we know we are never going to have to transmit it again.
339 *
340 * Since we use a ring buffer design for simplicity, we cannot cull byte n +
341 * k (for k > 0) from the ring buffer until byte n has also been culled.
342 * This means if parts of the stream get acknowledged out of order we might
343 * keep around some data we technically don't need to for a while. The
344 * impact of this is likely to be small and limited to quite a short
345 * duration, and doesn't justify the use of a more complex design.
346 */
347
348 /*
349 * We only need to check the first range entry in the integer set because we
350 * can only cull contiguous areas at the start of the ring buffer anyway.
351 */
352 if (h != NULL)
353 ring_buf_cpop_range(&qss->ring_buf, h->range.start, h->range.end,
354 qss->cleanse);
355 }
356
357 int ossl_quic_sstream_set_buffer_size(QUIC_SSTREAM *qss, size_t num_bytes)
358 {
359 return ring_buf_resize(&qss->ring_buf, num_bytes, qss->cleanse);
360 }
361
362 size_t ossl_quic_sstream_get_buffer_size(QUIC_SSTREAM *qss)
363 {
364 return qss->ring_buf.alloc;
365 }
366
367 size_t ossl_quic_sstream_get_buffer_used(QUIC_SSTREAM *qss)
368 {
369 return ring_buf_used(&qss->ring_buf);
370 }
371
372 size_t ossl_quic_sstream_get_buffer_avail(QUIC_SSTREAM *qss)
373 {
374 return ring_buf_avail(&qss->ring_buf);
375 }
376
377 int ossl_quic_sstream_is_totally_acked(QUIC_SSTREAM *qss)
378 {
379 UINT_RANGE r;
380 uint64_t cur_size;
381
382 if ((qss->have_final_size && !qss->acked_final_size)
383 || ossl_list_uint_set_num(&qss->acked_set) != 1)
384 return 0;
385
386 r = ossl_list_uint_set_head(&qss->acked_set)->range;
387 cur_size = qss->ring_buf.head_offset;
388
389 /*
390 * The invariants of UINT_SET guarantee a single list element if we have a
391 * single contiguous range, which is what we should have if everything has
392 * been acked.
393 */
394 assert(r.end + 1 <= cur_size);
395 return r.start == 0 && r.end + 1 == cur_size;
396 }
397
398 void ossl_quic_sstream_adjust_iov(size_t len,
399 OSSL_QTX_IOVEC *iov,
400 size_t num_iov)
401 {
402 size_t running = 0, i, iovlen;
403
404 for (i = 0, running = 0; i < num_iov; ++i) {
405 iovlen = iov[i].buf_len;
406
407 if (running >= len)
408 iov[i].buf_len = 0;
409 else if (running + iovlen > len)
410 iov[i].buf_len = len - running;
411
412 running += iovlen;
413 }
414 }
415
416 void ossl_quic_sstream_set_cleanse(QUIC_SSTREAM *qss, int cleanse)
417 {
418 qss->cleanse = cleanse;
419 }