2 * Copyright 2022 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/packet.h"
11 #include "internal/quic_txpim.h"
12 #include "internal/quic_fifd.h"
15 static OSSL_TIME cur_time;
17 static OSSL_TIME fake_now(void *arg) {
21 static void step_time(uint64_t ms) {
22 cur_time = ossl_time_add(cur_time, ossl_ms2time(ms));
25 static QUIC_SSTREAM *(*get_sstream_by_id_p)(uint64_t stream_id, uint32_t pn_space,
28 static QUIC_SSTREAM *get_sstream_by_id(uint64_t stream_id, uint32_t pn_space,
31 return get_sstream_by_id_p(stream_id, pn_space, arg);
34 static void (*regen_frame_p)(uint64_t frame_type, uint64_t stream_id,
35 QUIC_TXPIM_PKT *pkt, void *arg);
37 static void regen_frame(uint64_t frame_type, uint64_t stream_id,
38 QUIC_TXPIM_PKT *pkt, void *arg)
40 regen_frame_p(frame_type, stream_id, pkt, arg);
43 typedef struct info_st {
50 QUIC_SSTREAM *sstream[4];
53 static INFO *cur_info;
57 /* ----------------------------------------------------------------------
58 * 1. Test that a submitted packet, on ack, acks all streams inside of it
59 * Test that a submitted packet, on ack, calls the get by ID function
61 * Test that a submitted packet, on ack, acks all fins inside it
62 * Test that a submitted packet, on ack, releases the TXPIM packet
64 static QUIC_SSTREAM *sstream_expect(uint64_t stream_id, uint32_t pn_space,
67 if (stream_id == 42 || stream_id == 43)
68 return cur_info->sstream[stream_id - 42];
74 static uint64_t regen_frame_type[16];
75 static uint64_t regen_stream_id[16];
76 static size_t regen_count;
78 static void regen_expect(uint64_t frame_type, uint64_t stream_id,
79 QUIC_TXPIM_PKT *pkt, void *arg)
81 regen_frame_type[regen_count] = frame_type;
82 regen_stream_id[regen_count] = stream_id;
86 static const unsigned char placeholder_data[] = "placeholder";
88 static void cfq_free_cb_(unsigned char *buf, size_t buf_len, void *arg)
90 if (buf == placeholder_data && buf_len == sizeof(placeholder_data))
94 #define TEST_KIND_ACK 0
95 #define TEST_KIND_LOSS 1
96 #define TEST_KIND_DISCARD 2
97 #define TEST_KIND_NUM 3
99 static int test_generic(INFO *info, int kind)
102 size_t i, consumed = 0;
103 QUIC_TXPIM_PKT *pkt = NULL, *pkt2 = NULL;
104 OSSL_QUIC_FRAME_STREAM hdr = {0};
105 OSSL_QTX_IOVEC iov[2];
107 QUIC_TXPIM_CHUNK chunk = {42, 0, 11, 0};
108 OSSL_QUIC_FRAME_ACK ack = {0};
109 OSSL_QUIC_ACK_RANGE ack_ranges[1] = {0};
110 QUIC_CFQ_ITEM *cfq_item = NULL;
111 uint32_t pn_space = (kind == TEST_KIND_DISCARD)
112 ? QUIC_PN_SPACE_HANDSHAKE : QUIC_PN_SPACE_APP;
114 cur_time = ossl_seconds2time(1000);
117 get_sstream_by_id_p = sstream_expect;
118 regen_frame_p = regen_expect;
120 if (!TEST_ptr(pkt = ossl_quic_txpim_pkt_alloc(info->txpim)))
123 for (i = 0; i < 2; ++i) {
124 num_iov = OSSL_NELEM(iov);
125 if (!TEST_true(ossl_quic_sstream_append(info->sstream[i],
126 (unsigned char *)"Test message",
128 || !TEST_size_t_eq(consumed, 12))
132 ossl_quic_sstream_fin(info->sstream[i]);
134 if (!TEST_true(ossl_quic_sstream_get_stream_frame(info->sstream[i], 0,
135 &hdr, iov, &num_iov))
136 || !TEST_int_eq(hdr.is_fin, i == 1)
137 || !TEST_uint64_t_eq(hdr.offset, 0)
138 || !TEST_uint64_t_eq(hdr.len, 12)
139 || !TEST_size_t_eq(ossl_quic_sstream_get_buffer_used(info->sstream[i]), 12)
140 || !TEST_true(ossl_quic_sstream_mark_transmitted(info->sstream[i],
142 hdr.offset + hdr.len - 1)))
145 if (i == 1 && !TEST_true(ossl_quic_sstream_mark_transmitted_fin(info->sstream[i],
146 hdr.offset + hdr.len)))
149 chunk.has_fin = hdr.is_fin;
150 chunk.stream_id = 42 + i;
151 if (!TEST_true(ossl_quic_txpim_pkt_append_chunk(pkt, &chunk)))
156 if (!TEST_ptr(cfq_item = ossl_quic_cfq_add_frame(info->cfq, 10,
158 OSSL_QUIC_FRAME_TYPE_NEW_CONN_ID,
160 sizeof(placeholder_data),
162 || !TEST_ptr_eq(cfq_item, ossl_quic_cfq_get_priority_head(info->cfq, pn_space)))
165 ossl_quic_txpim_pkt_add_cfq_item(pkt, cfq_item);
167 pkt->ackm_pkt.pkt_num = 0;
168 pkt->ackm_pkt.pkt_space = pn_space;
169 pkt->ackm_pkt.largest_acked = QUIC_PN_INVALID;
170 pkt->ackm_pkt.num_bytes = 50;
171 pkt->ackm_pkt.time = cur_time;
172 pkt->ackm_pkt.is_inflight = 1;
173 pkt->ackm_pkt.is_ack_eliciting = 1;
174 if (kind == TEST_KIND_LOSS) {
175 pkt->had_handshake_done_frame = 1;
176 pkt->had_max_data_frame = 1;
177 pkt->had_max_streams_bidi_frame = 1;
178 pkt->had_max_streams_uni_frame = 1;
179 pkt->had_ack_frame = 1;
182 ack_ranges[0].start = 0;
183 ack_ranges[0].end = 0;
184 ack.ack_ranges = ack_ranges;
185 ack.num_ack_ranges = 1;
187 if (!TEST_true(ossl_quic_fifd_pkt_commit(&info->fifd, pkt)))
190 /* CFQ item should have been marked as transmitted */
191 if (!TEST_ptr_null(ossl_quic_cfq_get_priority_head(info->cfq, pn_space)))
196 if (!TEST_true(ossl_ackm_on_rx_ack_frame(info->ackm, &ack,
201 for (i = 0; i < 2; ++i)
202 if (!TEST_size_t_eq(ossl_quic_sstream_get_buffer_used(info->sstream[i]), 0))
205 /* This should fail, which proves the FIN was acked */
206 if (!TEST_false(ossl_quic_sstream_mark_lost_fin(info->sstream[1])))
209 /* CFQ item must have been released */
210 if (!TEST_true(cfq_freed))
213 /* No regen calls should have been made */
214 if (!TEST_size_t_eq(regen_count, 0))
220 /* Trigger loss detection via packet threshold. */
221 if (!TEST_ptr(pkt2 = ossl_quic_txpim_pkt_alloc(info->txpim)))
225 pkt2->ackm_pkt.pkt_num = 50;
226 pkt2->ackm_pkt.pkt_space = pn_space;
227 pkt2->ackm_pkt.largest_acked = QUIC_PN_INVALID;
228 pkt2->ackm_pkt.num_bytes = 50;
229 pkt2->ackm_pkt.time = cur_time;
230 pkt2->ackm_pkt.is_inflight = 1;
231 pkt2->ackm_pkt.is_ack_eliciting = 1;
233 ack_ranges[0].start = 50;
234 ack_ranges[0].end = 50;
235 ack.ack_ranges = ack_ranges;
236 ack.num_ack_ranges = 1;
238 if (!TEST_true(ossl_quic_fifd_pkt_commit(&info->fifd, pkt2))
239 || !TEST_true(ossl_ackm_on_rx_ack_frame(info->ackm, &ack,
240 pn_space, cur_time)))
243 for (i = 0; i < 2; ++i) {
244 num_iov = OSSL_NELEM(iov);
246 * Stream data we sent must have been marked as lost; check by
247 * ensuring it is returned again
249 if (!TEST_true(ossl_quic_sstream_get_stream_frame(info->sstream[i], 0,
250 &hdr, iov, &num_iov))
251 || !TEST_uint64_t_eq(hdr.offset, 0)
252 || !TEST_uint64_t_eq(hdr.len, 12))
256 /* FC frame should have regenerated for each stream */
257 if (!TEST_size_t_eq(regen_count, 7)
258 || !TEST_uint64_t_eq(regen_stream_id[0], 42)
259 || !TEST_uint64_t_eq(regen_frame_type[0], OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA)
260 || !TEST_uint64_t_eq(regen_stream_id[1], 43)
261 || !TEST_uint64_t_eq(regen_frame_type[1], OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA)
262 || !TEST_uint64_t_eq(regen_frame_type[2], OSSL_QUIC_FRAME_TYPE_HANDSHAKE_DONE)
263 || !TEST_uint64_t_eq(regen_stream_id[2], UINT64_MAX)
264 || !TEST_uint64_t_eq(regen_frame_type[3], OSSL_QUIC_FRAME_TYPE_MAX_DATA)
265 || !TEST_uint64_t_eq(regen_stream_id[3], UINT64_MAX)
266 || !TEST_uint64_t_eq(regen_frame_type[4], OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI)
267 || !TEST_uint64_t_eq(regen_stream_id[4], UINT64_MAX)
268 || !TEST_uint64_t_eq(regen_frame_type[5], OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_UNI)
269 || !TEST_uint64_t_eq(regen_stream_id[5], UINT64_MAX)
270 || !TEST_uint64_t_eq(regen_frame_type[6], OSSL_QUIC_FRAME_TYPE_ACK_WITH_ECN)
271 || !TEST_uint64_t_eq(regen_stream_id[6], UINT64_MAX))
274 /* CFQ item should have been marked as lost */
275 if (!TEST_ptr_eq(cfq_item, ossl_quic_cfq_get_priority_head(info->cfq, pn_space)))
278 /* FIN should have been marked as lost */
279 num_iov = OSSL_NELEM(iov);
280 if (!TEST_true(ossl_quic_sstream_get_stream_frame(info->sstream[1], 10,
281 &hdr, iov, &num_iov))
282 || !TEST_true(hdr.is_fin)
283 || !TEST_uint64_t_eq(hdr.len, 0))
288 case TEST_KIND_DISCARD:
289 if (!TEST_true(ossl_ackm_on_pkt_space_discarded(info->ackm, pn_space)))
292 /* CFQ item must have been released */
293 if (!TEST_true(cfq_freed))
302 /* TXPIM must have been released */
303 if (!TEST_size_t_eq(ossl_quic_txpim_get_in_use(info->txpim), 0))
311 static int test_fifd(int idx)
320 if (!TEST_true(ossl_statm_init(&info.statm))
321 || !TEST_ptr(info.ccdata = ossl_cc_dummy_method.new(NULL, NULL, NULL))
322 || !TEST_ptr(info.ackm = ossl_ackm_new(fake_now, NULL,
324 &ossl_cc_dummy_method,
326 || !TEST_true(ossl_ackm_on_handshake_confirmed(info.ackm))
327 || !TEST_ptr(info.cfq = ossl_quic_cfq_new())
328 || !TEST_ptr(info.txpim = ossl_quic_txpim_new())
329 || !TEST_true(ossl_quic_fifd_init(&info.fifd, info.cfq, info.ackm,
331 get_sstream_by_id, NULL,
335 for (i = 0; i < OSSL_NELEM(info.sstream); ++i)
336 if (!TEST_ptr(info.sstream[i] = ossl_quic_sstream_new(1024)))
339 ossl_statm_update_rtt(&info.statm, ossl_time_zero(), ossl_ms2time(1));
341 if (!TEST_true(test_generic(&info, idx))
342 || !TEST_false(cb_fail))
347 ossl_quic_fifd_cleanup(&info.fifd);
348 ossl_quic_cfq_free(info.cfq);
349 ossl_quic_txpim_free(info.txpim);
350 ossl_ackm_free(info.ackm);
351 ossl_statm_destroy(&info.statm);
352 if (info.ccdata != NULL)
353 ossl_cc_dummy_method.free(info.ccdata);
354 for (i = 0; i < OSSL_NELEM(info.sstream); ++i)
355 ossl_quic_sstream_free(info.sstream[i]);
360 int setup_tests(void)
362 ADD_ALL_TESTS(test_fifd, TEST_KIND_NUM);