123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- /*
- * Copyright 2022-2024 The OpenSSL Project Authors. All Rights Reserved.
- *
- * Licensed under the Apache License 2.0 (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution or at
- * https://www.openssl.org/source/license.html
- */
- #include "internal/packet.h"
- #include "internal/quic_txpim.h"
- #include "internal/quic_fifd.h"
- #include "testutil.h"
- static OSSL_TIME cur_time;
- static OSSL_TIME fake_now(void *arg) {
- return cur_time;
- }
- static void step_time(uint64_t ms) {
- cur_time = ossl_time_add(cur_time, ossl_ms2time(ms));
- }
- static QUIC_SSTREAM *(*get_sstream_by_id_p)(uint64_t stream_id, uint32_t pn_space,
- void *arg);
- static QUIC_SSTREAM *get_sstream_by_id(uint64_t stream_id, uint32_t pn_space,
- void *arg)
- {
- return get_sstream_by_id_p(stream_id, pn_space, arg);
- }
- static void (*regen_frame_p)(uint64_t frame_type, uint64_t stream_id,
- QUIC_TXPIM_PKT *pkt, void *arg);
- static void regen_frame(uint64_t frame_type, uint64_t stream_id,
- QUIC_TXPIM_PKT *pkt, void *arg)
- {
- regen_frame_p(frame_type, stream_id, pkt, arg);
- }
- static void confirm_frame(uint64_t frame_type, uint64_t stream_id,
- QUIC_TXPIM_PKT *pkt, void *arg)
- {}
- static void sstream_updated(uint64_t stream_id, void *arg)
- {}
- typedef struct info_st {
- QUIC_FIFD fifd;
- OSSL_ACKM *ackm;
- QUIC_CFQ *cfq;
- QUIC_TXPIM *txpim;
- OSSL_STATM statm;
- OSSL_CC_DATA *ccdata;
- QUIC_SSTREAM *sstream[4];
- } INFO;
- static INFO *cur_info;
- static int cb_fail;
- static int cfq_freed;
- /* ----------------------------------------------------------------------
- * 1. Test that a submitted packet, on ack, acks all streams inside of it
- * Test that a submitted packet, on ack, calls the get by ID function
- * correctly
- * Test that a submitted packet, on ack, acks all fins inside it
- * Test that a submitted packet, on ack, releases the TXPIM packet
- */
- static QUIC_SSTREAM *sstream_expect(uint64_t stream_id, uint32_t pn_space,
- void *arg)
- {
- if (stream_id == 42 || stream_id == 43)
- return cur_info->sstream[stream_id - 42];
- cb_fail = 1;
- return NULL;
- }
- static uint64_t regen_frame_type[16];
- static uint64_t regen_stream_id[16];
- static size_t regen_count;
- static void regen_expect(uint64_t frame_type, uint64_t stream_id,
- QUIC_TXPIM_PKT *pkt, void *arg)
- {
- regen_frame_type[regen_count] = frame_type;
- regen_stream_id[regen_count] = stream_id;
- ++regen_count;
- }
- static const unsigned char placeholder_data[] = "placeholder";
- static void cfq_free_cb_(unsigned char *buf, size_t buf_len, void *arg)
- {
- if (buf == placeholder_data && buf_len == sizeof(placeholder_data))
- cfq_freed = 1;
- }
- #define TEST_KIND_ACK 0
- #define TEST_KIND_LOSS 1
- #define TEST_KIND_DISCARD 2
- #define TEST_KIND_NUM 3
- static int test_generic(INFO *info, int kind)
- {
- int testresult = 0;
- size_t i, consumed = 0;
- QUIC_TXPIM_PKT *pkt = NULL, *pkt2 = NULL;
- OSSL_QUIC_FRAME_STREAM hdr = {0};
- OSSL_QTX_IOVEC iov[2];
- size_t num_iov;
- QUIC_TXPIM_CHUNK chunk = {42, 0, 11, 0};
- OSSL_QUIC_FRAME_ACK ack = {0};
- OSSL_QUIC_ACK_RANGE ack_ranges[1] = {0};
- QUIC_CFQ_ITEM *cfq_item = NULL;
- uint32_t pn_space = (kind == TEST_KIND_DISCARD)
- ? QUIC_PN_SPACE_HANDSHAKE : QUIC_PN_SPACE_APP;
- cur_time = ossl_seconds2time(1000);
- regen_count = 0;
- get_sstream_by_id_p = sstream_expect;
- regen_frame_p = regen_expect;
- if (!TEST_ptr(pkt = ossl_quic_txpim_pkt_alloc(info->txpim)))
- goto err;
- for (i = 0; i < 2; ++i) {
- num_iov = OSSL_NELEM(iov);
- if (!TEST_true(ossl_quic_sstream_append(info->sstream[i],
- (unsigned char *)"Test message",
- 12, &consumed))
- || !TEST_size_t_eq(consumed, 12))
- goto err;
- if (i == 1)
- ossl_quic_sstream_fin(info->sstream[i]);
- if (!TEST_true(ossl_quic_sstream_get_stream_frame(info->sstream[i], 0,
- &hdr, iov, &num_iov))
- || !TEST_int_eq(hdr.is_fin, i == 1)
- || !TEST_uint64_t_eq(hdr.offset, 0)
- || !TEST_uint64_t_eq(hdr.len, 12)
- || !TEST_size_t_eq(ossl_quic_sstream_get_buffer_used(info->sstream[i]), 12)
- || !TEST_true(ossl_quic_sstream_mark_transmitted(info->sstream[i],
- hdr.offset,
- hdr.offset + hdr.len - 1)))
- goto err;
- if (i == 1 && !TEST_true(ossl_quic_sstream_mark_transmitted_fin(info->sstream[i],
- hdr.offset + hdr.len)))
- goto err;
- chunk.has_fin = hdr.is_fin;
- chunk.stream_id = 42 + i;
- if (!TEST_true(ossl_quic_txpim_pkt_append_chunk(pkt, &chunk)))
- goto err;
- }
- cfq_freed = 0;
- if (!TEST_ptr(cfq_item = ossl_quic_cfq_add_frame(info->cfq, 10,
- pn_space,
- OSSL_QUIC_FRAME_TYPE_NEW_CONN_ID, 0,
- placeholder_data,
- sizeof(placeholder_data),
- cfq_free_cb_, NULL))
- || !TEST_ptr_eq(cfq_item, ossl_quic_cfq_get_priority_head(info->cfq, pn_space)))
- goto err;
- ossl_quic_txpim_pkt_add_cfq_item(pkt, cfq_item);
- pkt->ackm_pkt.pkt_num = 0;
- pkt->ackm_pkt.pkt_space = pn_space;
- pkt->ackm_pkt.largest_acked = QUIC_PN_INVALID;
- pkt->ackm_pkt.num_bytes = 50;
- pkt->ackm_pkt.time = cur_time;
- pkt->ackm_pkt.is_inflight = 1;
- pkt->ackm_pkt.is_ack_eliciting = 1;
- if (kind == TEST_KIND_LOSS) {
- pkt->had_handshake_done_frame = 1;
- pkt->had_max_data_frame = 1;
- pkt->had_max_streams_bidi_frame = 1;
- pkt->had_max_streams_uni_frame = 1;
- pkt->had_ack_frame = 1;
- }
- ack_ranges[0].start = 0;
- ack_ranges[0].end = 0;
- ack.ack_ranges = ack_ranges;
- ack.num_ack_ranges = 1;
- if (!TEST_true(ossl_quic_fifd_pkt_commit(&info->fifd, pkt)))
- goto err;
- /* CFQ item should have been marked as transmitted */
- if (!TEST_ptr_null(ossl_quic_cfq_get_priority_head(info->cfq, pn_space)))
- goto err;
- switch (kind) {
- case TEST_KIND_ACK:
- if (!TEST_true(ossl_ackm_on_rx_ack_frame(info->ackm, &ack,
- pn_space,
- cur_time)))
- goto err;
- for (i = 0; i < 2; ++i)
- if (!TEST_size_t_eq(ossl_quic_sstream_get_buffer_used(info->sstream[i]), 0))
- goto err;
- /* This should fail, which proves the FIN was acked */
- if (!TEST_false(ossl_quic_sstream_mark_lost_fin(info->sstream[1])))
- goto err;
- /* CFQ item must have been released */
- if (!TEST_true(cfq_freed))
- goto err;
- /* No regen calls should have been made */
- if (!TEST_size_t_eq(regen_count, 0))
- goto err;
- break;
- case TEST_KIND_LOSS:
- /* Trigger loss detection via packet threshold. */
- if (!TEST_ptr(pkt2 = ossl_quic_txpim_pkt_alloc(info->txpim)))
- goto err;
- step_time(10000);
- pkt2->ackm_pkt.pkt_num = 50;
- pkt2->ackm_pkt.pkt_space = pn_space;
- pkt2->ackm_pkt.largest_acked = QUIC_PN_INVALID;
- pkt2->ackm_pkt.num_bytes = 50;
- pkt2->ackm_pkt.time = cur_time;
- pkt2->ackm_pkt.is_inflight = 1;
- pkt2->ackm_pkt.is_ack_eliciting = 1;
- ack_ranges[0].start = 50;
- ack_ranges[0].end = 50;
- ack.ack_ranges = ack_ranges;
- ack.num_ack_ranges = 1;
- if (!TEST_true(ossl_quic_fifd_pkt_commit(&info->fifd, pkt2))
- || !TEST_true(ossl_ackm_on_rx_ack_frame(info->ackm, &ack,
- pn_space, cur_time)))
- goto err;
- for (i = 0; i < 2; ++i) {
- num_iov = OSSL_NELEM(iov);
- /*
- * Stream data we sent must have been marked as lost; check by
- * ensuring it is returned again
- */
- if (!TEST_true(ossl_quic_sstream_get_stream_frame(info->sstream[i], 0,
- &hdr, iov, &num_iov))
- || !TEST_uint64_t_eq(hdr.offset, 0)
- || !TEST_uint64_t_eq(hdr.len, 12))
- goto err;
- }
- /* FC frame should have regenerated for each stream */
- if (!TEST_size_t_eq(regen_count, 7)
- || !TEST_uint64_t_eq(regen_stream_id[0], 42)
- || !TEST_uint64_t_eq(regen_frame_type[0], OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA)
- || !TEST_uint64_t_eq(regen_stream_id[1], 43)
- || !TEST_uint64_t_eq(regen_frame_type[1], OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA)
- || !TEST_uint64_t_eq(regen_frame_type[2], OSSL_QUIC_FRAME_TYPE_HANDSHAKE_DONE)
- || !TEST_uint64_t_eq(regen_stream_id[2], UINT64_MAX)
- || !TEST_uint64_t_eq(regen_frame_type[3], OSSL_QUIC_FRAME_TYPE_MAX_DATA)
- || !TEST_uint64_t_eq(regen_stream_id[3], UINT64_MAX)
- || !TEST_uint64_t_eq(regen_frame_type[4], OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI)
- || !TEST_uint64_t_eq(regen_stream_id[4], UINT64_MAX)
- || !TEST_uint64_t_eq(regen_frame_type[5], OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_UNI)
- || !TEST_uint64_t_eq(regen_stream_id[5], UINT64_MAX)
- || !TEST_uint64_t_eq(regen_frame_type[6], OSSL_QUIC_FRAME_TYPE_ACK_WITH_ECN)
- || !TEST_uint64_t_eq(regen_stream_id[6], UINT64_MAX))
- goto err;
- /* CFQ item should have been marked as lost */
- if (!TEST_ptr_eq(cfq_item, ossl_quic_cfq_get_priority_head(info->cfq, pn_space)))
- goto err;
- /* FIN should have been marked as lost */
- num_iov = OSSL_NELEM(iov);
- if (!TEST_true(ossl_quic_sstream_get_stream_frame(info->sstream[1], 1,
- &hdr, iov, &num_iov))
- || !TEST_true(hdr.is_fin)
- || !TEST_uint64_t_eq(hdr.len, 0))
- goto err;
- break;
- case TEST_KIND_DISCARD:
- if (!TEST_true(ossl_ackm_on_pkt_space_discarded(info->ackm, pn_space)))
- goto err;
- /* CFQ item must have been released */
- if (!TEST_true(cfq_freed))
- goto err;
- break;
- default:
- goto err;
- }
- /* TXPIM must have been released */
- if (!TEST_size_t_eq(ossl_quic_txpim_get_in_use(info->txpim), 0))
- goto err;
- testresult = 1;
- err:
- return testresult;
- }
- static int test_fifd(int idx)
- {
- int testresult = 0;
- INFO info = {0};
- size_t i;
- cur_info = &info;
- cb_fail = 0;
- if (!TEST_true(ossl_statm_init(&info.statm))
- || !TEST_ptr(info.ccdata = ossl_cc_dummy_method.new(fake_now, NULL))
- || !TEST_ptr(info.ackm = ossl_ackm_new(fake_now, NULL,
- &info.statm,
- &ossl_cc_dummy_method,
- info.ccdata))
- || !TEST_true(ossl_ackm_on_handshake_confirmed(info.ackm))
- || !TEST_ptr(info.cfq = ossl_quic_cfq_new())
- || !TEST_ptr(info.txpim = ossl_quic_txpim_new())
- || !TEST_true(ossl_quic_fifd_init(&info.fifd, info.cfq, info.ackm,
- info.txpim,
- get_sstream_by_id, NULL,
- regen_frame, NULL,
- confirm_frame, NULL,
- sstream_updated, NULL,
- NULL, NULL)))
- goto err;
- for (i = 0; i < OSSL_NELEM(info.sstream); ++i)
- if (!TEST_ptr(info.sstream[i] = ossl_quic_sstream_new(1024)))
- goto err;
- ossl_statm_update_rtt(&info.statm, ossl_time_zero(), ossl_ms2time(1));
- if (!TEST_true(test_generic(&info, idx))
- || !TEST_false(cb_fail))
- goto err;
- testresult = 1;
- err:
- ossl_quic_fifd_cleanup(&info.fifd);
- ossl_quic_cfq_free(info.cfq);
- ossl_quic_txpim_free(info.txpim);
- ossl_ackm_free(info.ackm);
- ossl_statm_destroy(&info.statm);
- if (info.ccdata != NULL)
- ossl_cc_dummy_method.free(info.ccdata);
- for (i = 0; i < OSSL_NELEM(info.sstream); ++i)
- ossl_quic_sstream_free(info.sstream[i]);
- cur_info = NULL;
- return testresult;
- }
- int setup_tests(void)
- {
- ADD_ALL_TESTS(test_fifd, TEST_KIND_NUM);
- return 1;
- }
|