2
0

quic_fifd_test.c 13 KB

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