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/packet.h" 11 #include "internal/quic_txpim.h" 12 #include "internal/quic_fifd.h" 13 #include "testutil.h" 14 15 static OSSL_TIME cur_time; 16 17 static OSSL_TIME fake_now(void *arg) { 18 return cur_time; 19 } 20 21 static void step_time(uint64_t ms) { 22 cur_time = ossl_time_add(cur_time, ossl_ms2time(ms)); 23 } 24 25 static QUIC_SSTREAM *(*get_sstream_by_id_p)(uint64_t stream_id, uint32_t pn_space, 26 void *arg); 27 28 static QUIC_SSTREAM *get_sstream_by_id(uint64_t stream_id, uint32_t pn_space, 29 void *arg) 30 { 31 return get_sstream_by_id_p(stream_id, pn_space, arg); 32 } 33 34 static void (*regen_frame_p)(uint64_t frame_type, uint64_t stream_id, 35 QUIC_TXPIM_PKT *pkt, void *arg); 36 37 static void regen_frame(uint64_t frame_type, uint64_t stream_id, 38 QUIC_TXPIM_PKT *pkt, void *arg) 39 { 40 regen_frame_p(frame_type, stream_id, pkt, arg); 41 } 42 43 static void confirm_frame(uint64_t frame_type, uint64_t stream_id, 44 QUIC_TXPIM_PKT *pkt, void *arg) 45 {} 46 47 static void sstream_updated(uint64_t stream_id, void *arg) 48 {} 49 50 typedef struct info_st { 51 QUIC_FIFD fifd; 52 OSSL_ACKM *ackm; 53 QUIC_CFQ *cfq; 54 QUIC_TXPIM *txpim; 55 OSSL_STATM statm; 56 OSSL_CC_DATA *ccdata; 57 QUIC_SSTREAM *sstream[4]; 58 } INFO; 59 60 static INFO *cur_info; 61 static int cb_fail; 62 static int cfq_freed; 63 64 /* ---------------------------------------------------------------------- 65 * 1. Test that a submitted packet, on ack, acks all streams inside of it 66 * Test that a submitted packet, on ack, calls the get by ID function 67 * correctly 68 * Test that a submitted packet, on ack, acks all fins inside it 69 * Test that a submitted packet, on ack, releases the TXPIM packet 70 */ 71 static QUIC_SSTREAM *sstream_expect(uint64_t stream_id, uint32_t pn_space, 72 void *arg) 73 { 74 if (stream_id == 42 || stream_id == 43) 75 return cur_info->sstream[stream_id - 42]; 76 77 cb_fail = 1; 78 return NULL; 79 } 80 81 static uint64_t regen_frame_type[16]; 82 static uint64_t regen_stream_id[16]; 83 static size_t regen_count; 84 85 static void regen_expect(uint64_t frame_type, uint64_t stream_id, 86 QUIC_TXPIM_PKT *pkt, void *arg) 87 { 88 regen_frame_type[regen_count] = frame_type; 89 regen_stream_id[regen_count] = stream_id; 90 ++regen_count; 91 } 92 93 static const unsigned char placeholder_data[] = "placeholder"; 94 95 static void cfq_free_cb_(unsigned char *buf, size_t buf_len, void *arg) 96 { 97 if (buf == placeholder_data && buf_len == sizeof(placeholder_data)) 98 cfq_freed = 1; 99 } 100 101 #define TEST_KIND_ACK 0 102 #define TEST_KIND_LOSS 1 103 #define TEST_KIND_DISCARD 2 104 #define TEST_KIND_NUM 3 105 106 static int test_generic(INFO *info, int kind) 107 { 108 int testresult = 0; 109 size_t i, consumed = 0; 110 QUIC_TXPIM_PKT *pkt = NULL, *pkt2 = NULL; 111 OSSL_QUIC_FRAME_STREAM hdr = {0}; 112 OSSL_QTX_IOVEC iov[2]; 113 size_t num_iov; 114 QUIC_TXPIM_CHUNK chunk = {42, 0, 11, 0}; 115 OSSL_QUIC_FRAME_ACK ack = {0}; 116 OSSL_QUIC_ACK_RANGE ack_ranges[1] = {0}; 117 QUIC_CFQ_ITEM *cfq_item = NULL; 118 uint32_t pn_space = (kind == TEST_KIND_DISCARD) 119 ? QUIC_PN_SPACE_HANDSHAKE : QUIC_PN_SPACE_APP; 120 121 cur_time = ossl_seconds2time(1000); 122 regen_count = 0; 123 124 get_sstream_by_id_p = sstream_expect; 125 regen_frame_p = regen_expect; 126 127 if (!TEST_ptr(pkt = ossl_quic_txpim_pkt_alloc(info->txpim))) 128 goto err; 129 130 for (i = 0; i < 2; ++i) { 131 num_iov = OSSL_NELEM(iov); 132 if (!TEST_true(ossl_quic_sstream_append(info->sstream[i], 133 (unsigned char *)"Test message", 134 12, &consumed)) 135 || !TEST_size_t_eq(consumed, 12)) 136 goto err; 137 138 if (i == 1) 139 ossl_quic_sstream_fin(info->sstream[i]); 140 141 if (!TEST_true(ossl_quic_sstream_get_stream_frame(info->sstream[i], 0, 142 &hdr, iov, &num_iov)) 143 || !TEST_int_eq(hdr.is_fin, i == 1) 144 || !TEST_uint64_t_eq(hdr.offset, 0) 145 || !TEST_uint64_t_eq(hdr.len, 12) 146 || !TEST_size_t_eq(ossl_quic_sstream_get_buffer_used(info->sstream[i]), 12) 147 || !TEST_true(ossl_quic_sstream_mark_transmitted(info->sstream[i], 148 hdr.offset, 149 hdr.offset + hdr.len - 1))) 150 goto err; 151 152 if (i == 1 && !TEST_true(ossl_quic_sstream_mark_transmitted_fin(info->sstream[i], 153 hdr.offset + hdr.len))) 154 goto err; 155 156 chunk.has_fin = hdr.is_fin; 157 chunk.stream_id = 42 + i; 158 if (!TEST_true(ossl_quic_txpim_pkt_append_chunk(pkt, &chunk))) 159 goto err; 160 } 161 162 cfq_freed = 0; 163 if (!TEST_ptr(cfq_item = ossl_quic_cfq_add_frame(info->cfq, 10, 164 pn_space, 165 OSSL_QUIC_FRAME_TYPE_NEW_CONN_ID, 0, 166 placeholder_data, 167 sizeof(placeholder_data), 168 cfq_free_cb_, NULL)) 169 || !TEST_ptr_eq(cfq_item, ossl_quic_cfq_get_priority_head(info->cfq, pn_space))) 170 goto err; 171 172 ossl_quic_txpim_pkt_add_cfq_item(pkt, cfq_item); 173 174 pkt->ackm_pkt.pkt_num = 0; 175 pkt->ackm_pkt.pkt_space = pn_space; 176 pkt->ackm_pkt.largest_acked = QUIC_PN_INVALID; 177 pkt->ackm_pkt.num_bytes = 50; 178 pkt->ackm_pkt.time = cur_time; 179 pkt->ackm_pkt.is_inflight = 1; 180 pkt->ackm_pkt.is_ack_eliciting = 1; 181 if (kind == TEST_KIND_LOSS) { 182 pkt->had_handshake_done_frame = 1; 183 pkt->had_max_data_frame = 1; 184 pkt->had_max_streams_bidi_frame = 1; 185 pkt->had_max_streams_uni_frame = 1; 186 pkt->had_ack_frame = 1; 187 } 188 189 ack_ranges[0].start = 0; 190 ack_ranges[0].end = 0; 191 ack.ack_ranges = ack_ranges; 192 ack.num_ack_ranges = 1; 193 194 if (!TEST_true(ossl_quic_fifd_pkt_commit(&info->fifd, pkt))) 195 goto err; 196 197 /* CFQ item should have been marked as transmitted */ 198 if (!TEST_ptr_null(ossl_quic_cfq_get_priority_head(info->cfq, pn_space))) 199 goto err; 200 201 switch (kind) { 202 case TEST_KIND_ACK: 203 if (!TEST_true(ossl_ackm_on_rx_ack_frame(info->ackm, &ack, 204 pn_space, 205 cur_time))) 206 goto err; 207 208 for (i = 0; i < 2; ++i) 209 if (!TEST_size_t_eq(ossl_quic_sstream_get_buffer_used(info->sstream[i]), 0)) 210 goto err; 211 212 /* This should fail, which proves the FIN was acked */ 213 if (!TEST_false(ossl_quic_sstream_mark_lost_fin(info->sstream[1]))) 214 goto err; 215 216 /* CFQ item must have been released */ 217 if (!TEST_true(cfq_freed)) 218 goto err; 219 220 /* No regen calls should have been made */ 221 if (!TEST_size_t_eq(regen_count, 0)) 222 goto err; 223 224 break; 225 226 case TEST_KIND_LOSS: 227 /* Trigger loss detection via packet threshold. */ 228 if (!TEST_ptr(pkt2 = ossl_quic_txpim_pkt_alloc(info->txpim))) 229 goto err; 230 231 step_time(10000); 232 pkt2->ackm_pkt.pkt_num = 50; 233 pkt2->ackm_pkt.pkt_space = pn_space; 234 pkt2->ackm_pkt.largest_acked = QUIC_PN_INVALID; 235 pkt2->ackm_pkt.num_bytes = 50; 236 pkt2->ackm_pkt.time = cur_time; 237 pkt2->ackm_pkt.is_inflight = 1; 238 pkt2->ackm_pkt.is_ack_eliciting = 1; 239 240 ack_ranges[0].start = 50; 241 ack_ranges[0].end = 50; 242 ack.ack_ranges = ack_ranges; 243 ack.num_ack_ranges = 1; 244 245 if (!TEST_true(ossl_quic_fifd_pkt_commit(&info->fifd, pkt2)) 246 || !TEST_true(ossl_ackm_on_rx_ack_frame(info->ackm, &ack, 247 pn_space, cur_time))) 248 goto err; 249 250 for (i = 0; i < 2; ++i) { 251 num_iov = OSSL_NELEM(iov); 252 /* 253 * Stream data we sent must have been marked as lost; check by 254 * ensuring it is returned again 255 */ 256 if (!TEST_true(ossl_quic_sstream_get_stream_frame(info->sstream[i], 0, 257 &hdr, iov, &num_iov)) 258 || !TEST_uint64_t_eq(hdr.offset, 0) 259 || !TEST_uint64_t_eq(hdr.len, 12)) 260 goto err; 261 } 262 263 /* FC frame should have regenerated for each stream */ 264 if (!TEST_size_t_eq(regen_count, 7) 265 || !TEST_uint64_t_eq(regen_stream_id[0], 42) 266 || !TEST_uint64_t_eq(regen_frame_type[0], OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA) 267 || !TEST_uint64_t_eq(regen_stream_id[1], 43) 268 || !TEST_uint64_t_eq(regen_frame_type[1], OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA) 269 || !TEST_uint64_t_eq(regen_frame_type[2], OSSL_QUIC_FRAME_TYPE_HANDSHAKE_DONE) 270 || !TEST_uint64_t_eq(regen_stream_id[2], UINT64_MAX) 271 || !TEST_uint64_t_eq(regen_frame_type[3], OSSL_QUIC_FRAME_TYPE_MAX_DATA) 272 || !TEST_uint64_t_eq(regen_stream_id[3], UINT64_MAX) 273 || !TEST_uint64_t_eq(regen_frame_type[4], OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI) 274 || !TEST_uint64_t_eq(regen_stream_id[4], UINT64_MAX) 275 || !TEST_uint64_t_eq(regen_frame_type[5], OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_UNI) 276 || !TEST_uint64_t_eq(regen_stream_id[5], UINT64_MAX) 277 || !TEST_uint64_t_eq(regen_frame_type[6], OSSL_QUIC_FRAME_TYPE_ACK_WITH_ECN) 278 || !TEST_uint64_t_eq(regen_stream_id[6], UINT64_MAX)) 279 goto err; 280 281 /* CFQ item should have been marked as lost */ 282 if (!TEST_ptr_eq(cfq_item, ossl_quic_cfq_get_priority_head(info->cfq, pn_space))) 283 goto err; 284 285 /* FIN should have been marked as lost */ 286 num_iov = OSSL_NELEM(iov); 287 if (!TEST_true(ossl_quic_sstream_get_stream_frame(info->sstream[1], 1, 288 &hdr, iov, &num_iov)) 289 || !TEST_true(hdr.is_fin) 290 || !TEST_uint64_t_eq(hdr.len, 0)) 291 goto err; 292 293 break; 294 295 case TEST_KIND_DISCARD: 296 if (!TEST_true(ossl_ackm_on_pkt_space_discarded(info->ackm, pn_space))) 297 goto err; 298 299 /* CFQ item must have been released */ 300 if (!TEST_true(cfq_freed)) 301 goto err; 302 303 break; 304 305 default: 306 goto err; 307 } 308 309 /* TXPIM must have been released */ 310 if (!TEST_size_t_eq(ossl_quic_txpim_get_in_use(info->txpim), 0)) 311 goto err; 312 313 testresult = 1; 314 err: 315 return testresult; 316 } 317 318 static int test_fifd(int idx) 319 { 320 int testresult = 0; 321 INFO info = {0}; 322 size_t i; 323 324 cur_info = &info; 325 cb_fail = 0; 326 327 if (!TEST_true(ossl_statm_init(&info.statm)) 328 || !TEST_ptr(info.ccdata = ossl_cc_dummy_method.new(fake_now, NULL)) 329 || !TEST_ptr(info.ackm = ossl_ackm_new(fake_now, NULL, 330 &info.statm, 331 &ossl_cc_dummy_method, 332 info.ccdata)) 333 || !TEST_true(ossl_ackm_on_handshake_confirmed(info.ackm)) 334 || !TEST_ptr(info.cfq = ossl_quic_cfq_new()) 335 || !TEST_ptr(info.txpim = ossl_quic_txpim_new()) 336 || !TEST_true(ossl_quic_fifd_init(&info.fifd, info.cfq, info.ackm, 337 info.txpim, 338 get_sstream_by_id, NULL, 339 regen_frame, NULL, 340 confirm_frame, NULL, 341 sstream_updated, NULL, 342 NULL, NULL))) 343 goto err; 344 345 for (i = 0; i < OSSL_NELEM(info.sstream); ++i) 346 if (!TEST_ptr(info.sstream[i] = ossl_quic_sstream_new(1024))) 347 goto err; 348 349 ossl_statm_update_rtt(&info.statm, ossl_time_zero(), ossl_ms2time(1)); 350 351 if (!TEST_true(test_generic(&info, idx)) 352 || !TEST_false(cb_fail)) 353 goto err; 354 355 testresult = 1; 356 err: 357 ossl_quic_fifd_cleanup(&info.fifd); 358 ossl_quic_cfq_free(info.cfq); 359 ossl_quic_txpim_free(info.txpim); 360 ossl_ackm_free(info.ackm); 361 ossl_statm_destroy(&info.statm); 362 if (info.ccdata != NULL) 363 ossl_cc_dummy_method.free(info.ccdata); 364 for (i = 0; i < OSSL_NELEM(info.sstream); ++i) 365 ossl_quic_sstream_free(info.sstream[i]); 366 cur_info = NULL; 367 return testresult; 368 } 369 370 int setup_tests(void) 371 { 372 ADD_ALL_TESTS(test_fifd, TEST_KIND_NUM); 373 return 1; 374 } 375