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