quic_fifd.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  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/quic_fifd.h"
  10. #include "internal/quic_wire.h"
  11. #include "internal/qlog_event_helpers.h"
  12. DEFINE_LIST_OF(tx_history, OSSL_ACKM_TX_PKT);
  13. int ossl_quic_fifd_init(QUIC_FIFD *fifd,
  14. QUIC_CFQ *cfq,
  15. OSSL_ACKM *ackm,
  16. QUIC_TXPIM *txpim,
  17. /* stream_id is UINT64_MAX for the crypto stream */
  18. QUIC_SSTREAM *(*get_sstream_by_id)(uint64_t stream_id,
  19. uint32_t pn_space,
  20. void *arg),
  21. void *get_sstream_by_id_arg,
  22. /* stream_id is UINT64_MAX if not applicable */
  23. void (*regen_frame)(uint64_t frame_type,
  24. uint64_t stream_id,
  25. QUIC_TXPIM_PKT *pkt,
  26. void *arg),
  27. void *regen_frame_arg,
  28. void (*confirm_frame)(uint64_t frame_type,
  29. uint64_t stream_id,
  30. QUIC_TXPIM_PKT *pkt,
  31. void *arg),
  32. void *confirm_frame_arg,
  33. void (*sstream_updated)(uint64_t stream_id,
  34. void *arg),
  35. void *sstream_updated_arg,
  36. QLOG *(*get_qlog_cb)(void *arg),
  37. void *get_qlog_cb_arg)
  38. {
  39. if (cfq == NULL || ackm == NULL || txpim == NULL
  40. || get_sstream_by_id == NULL || regen_frame == NULL)
  41. return 0;
  42. fifd->cfq = cfq;
  43. fifd->ackm = ackm;
  44. fifd->txpim = txpim;
  45. fifd->get_sstream_by_id = get_sstream_by_id;
  46. fifd->get_sstream_by_id_arg = get_sstream_by_id_arg;
  47. fifd->regen_frame = regen_frame;
  48. fifd->regen_frame_arg = regen_frame_arg;
  49. fifd->confirm_frame = confirm_frame;
  50. fifd->confirm_frame_arg = confirm_frame_arg;
  51. fifd->sstream_updated = sstream_updated;
  52. fifd->sstream_updated_arg = sstream_updated_arg;
  53. fifd->get_qlog_cb = get_qlog_cb;
  54. fifd->get_qlog_cb_arg = get_qlog_cb_arg;
  55. return 1;
  56. }
  57. void ossl_quic_fifd_cleanup(QUIC_FIFD *fifd)
  58. {
  59. /* No-op. */
  60. }
  61. static void on_acked(void *arg)
  62. {
  63. QUIC_TXPIM_PKT *pkt = arg;
  64. QUIC_FIFD *fifd = pkt->fifd;
  65. const QUIC_TXPIM_CHUNK *chunks = ossl_quic_txpim_pkt_get_chunks(pkt);
  66. size_t i, num_chunks = ossl_quic_txpim_pkt_get_num_chunks(pkt);
  67. QUIC_SSTREAM *sstream;
  68. QUIC_CFQ_ITEM *cfq_item, *cfq_item_next;
  69. /* STREAM and CRYPTO stream chunks, FINs and stream FC frames */
  70. for (i = 0; i < num_chunks; ++i) {
  71. sstream = fifd->get_sstream_by_id(chunks[i].stream_id,
  72. pkt->ackm_pkt.pkt_space,
  73. fifd->get_sstream_by_id_arg);
  74. if (sstream == NULL)
  75. continue;
  76. if (chunks[i].end >= chunks[i].start)
  77. /* coverity[check_return]: Best effort - we cannot fail here. */
  78. ossl_quic_sstream_mark_acked(sstream,
  79. chunks[i].start, chunks[i].end);
  80. if (chunks[i].has_fin && chunks[i].stream_id != UINT64_MAX)
  81. ossl_quic_sstream_mark_acked_fin(sstream);
  82. if (chunks[i].has_stop_sending && chunks[i].stream_id != UINT64_MAX)
  83. fifd->confirm_frame(OSSL_QUIC_FRAME_TYPE_STOP_SENDING,
  84. chunks[i].stream_id, pkt,
  85. fifd->confirm_frame_arg);
  86. if (chunks[i].has_reset_stream && chunks[i].stream_id != UINT64_MAX)
  87. fifd->confirm_frame(OSSL_QUIC_FRAME_TYPE_RESET_STREAM,
  88. chunks[i].stream_id, pkt,
  89. fifd->confirm_frame_arg);
  90. if (ossl_quic_sstream_is_totally_acked(sstream))
  91. fifd->sstream_updated(chunks[i].stream_id, fifd->sstream_updated_arg);
  92. }
  93. /* GCR */
  94. for (cfq_item = pkt->retx_head; cfq_item != NULL; cfq_item = cfq_item_next) {
  95. cfq_item_next = cfq_item->pkt_next;
  96. ossl_quic_cfq_release(fifd->cfq, cfq_item);
  97. }
  98. ossl_quic_txpim_pkt_release(fifd->txpim, pkt);
  99. }
  100. static QLOG *fifd_get_qlog(QUIC_FIFD *fifd)
  101. {
  102. if (fifd->get_qlog_cb == NULL)
  103. return NULL;
  104. return fifd->get_qlog_cb(fifd->get_qlog_cb_arg);
  105. }
  106. static void on_lost(void *arg)
  107. {
  108. QUIC_TXPIM_PKT *pkt = arg;
  109. QUIC_FIFD *fifd = pkt->fifd;
  110. const QUIC_TXPIM_CHUNK *chunks = ossl_quic_txpim_pkt_get_chunks(pkt);
  111. size_t i, num_chunks = ossl_quic_txpim_pkt_get_num_chunks(pkt);
  112. QUIC_SSTREAM *sstream;
  113. QUIC_CFQ_ITEM *cfq_item, *cfq_item_next;
  114. int sstream_updated;
  115. ossl_qlog_event_recovery_packet_lost(fifd_get_qlog(fifd), pkt);
  116. /* STREAM and CRYPTO stream chunks, FIN and stream FC frames */
  117. for (i = 0; i < num_chunks; ++i) {
  118. sstream = fifd->get_sstream_by_id(chunks[i].stream_id,
  119. pkt->ackm_pkt.pkt_space,
  120. fifd->get_sstream_by_id_arg);
  121. if (sstream == NULL)
  122. continue;
  123. sstream_updated = 0;
  124. if (chunks[i].end >= chunks[i].start) {
  125. /*
  126. * Note: If the stream is being reset, we do not need to retransmit
  127. * old data as this is pointless. In this case this will be handled
  128. * by (sstream == NULL) above as the QSM will free the QUIC_SSTREAM
  129. * and our call to get_sstream_by_id above will return NULL.
  130. */
  131. ossl_quic_sstream_mark_lost(sstream,
  132. chunks[i].start, chunks[i].end);
  133. sstream_updated = 1;
  134. }
  135. if (chunks[i].has_fin && chunks[i].stream_id != UINT64_MAX) {
  136. ossl_quic_sstream_mark_lost_fin(sstream);
  137. sstream_updated = 1;
  138. }
  139. if (chunks[i].has_stop_sending && chunks[i].stream_id != UINT64_MAX)
  140. fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_STOP_SENDING,
  141. chunks[i].stream_id, pkt,
  142. fifd->regen_frame_arg);
  143. if (chunks[i].has_reset_stream && chunks[i].stream_id != UINT64_MAX)
  144. fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_RESET_STREAM,
  145. chunks[i].stream_id, pkt,
  146. fifd->regen_frame_arg);
  147. /*
  148. * Inform caller that stream needs an FC frame.
  149. *
  150. * Note: We could track whether an FC frame was sent originally for the
  151. * stream to determine if it really needs to be regenerated or not.
  152. * However, if loss has occurred, it's probably better to ensure the
  153. * peer has up-to-date flow control data for the stream. Given that
  154. * these frames are extremely small, we may as well always send it when
  155. * handling loss.
  156. */
  157. fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA,
  158. chunks[i].stream_id,
  159. pkt,
  160. fifd->regen_frame_arg);
  161. if (sstream_updated && chunks[i].stream_id != UINT64_MAX)
  162. fifd->sstream_updated(chunks[i].stream_id,
  163. fifd->sstream_updated_arg);
  164. }
  165. /* GCR */
  166. for (cfq_item = pkt->retx_head; cfq_item != NULL; cfq_item = cfq_item_next) {
  167. cfq_item_next = cfq_item->pkt_next;
  168. ossl_quic_cfq_mark_lost(fifd->cfq, cfq_item, UINT32_MAX);
  169. }
  170. /* Regenerate flag frames */
  171. if (pkt->had_handshake_done_frame)
  172. fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_HANDSHAKE_DONE,
  173. UINT64_MAX, pkt,
  174. fifd->regen_frame_arg);
  175. if (pkt->had_max_data_frame)
  176. fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_DATA,
  177. UINT64_MAX, pkt,
  178. fifd->regen_frame_arg);
  179. if (pkt->had_max_streams_bidi_frame)
  180. fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI,
  181. UINT64_MAX, pkt,
  182. fifd->regen_frame_arg);
  183. if (pkt->had_max_streams_uni_frame)
  184. fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_UNI,
  185. UINT64_MAX, pkt,
  186. fifd->regen_frame_arg);
  187. if (pkt->had_ack_frame)
  188. /*
  189. * We always use the ACK_WITH_ECN frame type to represent the ACK frame
  190. * type in our callback; we assume it is the caller's job to decide
  191. * whether it wants to send ECN data or not.
  192. */
  193. fifd->regen_frame(OSSL_QUIC_FRAME_TYPE_ACK_WITH_ECN,
  194. UINT64_MAX, pkt,
  195. fifd->regen_frame_arg);
  196. ossl_quic_txpim_pkt_release(fifd->txpim, pkt);
  197. }
  198. static void on_discarded(void *arg)
  199. {
  200. QUIC_TXPIM_PKT *pkt = arg;
  201. QUIC_FIFD *fifd = pkt->fifd;
  202. QUIC_CFQ_ITEM *cfq_item, *cfq_item_next;
  203. /*
  204. * Don't need to do anything to SSTREAMs for STREAM and CRYPTO streams, as
  205. * we assume caller will clean them up.
  206. */
  207. /* GCR */
  208. for (cfq_item = pkt->retx_head; cfq_item != NULL; cfq_item = cfq_item_next) {
  209. cfq_item_next = cfq_item->pkt_next;
  210. ossl_quic_cfq_release(fifd->cfq, cfq_item);
  211. }
  212. ossl_quic_txpim_pkt_release(fifd->txpim, pkt);
  213. }
  214. int ossl_quic_fifd_pkt_commit(QUIC_FIFD *fifd, QUIC_TXPIM_PKT *pkt)
  215. {
  216. QUIC_CFQ_ITEM *cfq_item;
  217. const QUIC_TXPIM_CHUNK *chunks;
  218. size_t i, num_chunks;
  219. QUIC_SSTREAM *sstream;
  220. pkt->fifd = fifd;
  221. pkt->ackm_pkt.on_lost = on_lost;
  222. pkt->ackm_pkt.on_acked = on_acked;
  223. pkt->ackm_pkt.on_discarded = on_discarded;
  224. pkt->ackm_pkt.cb_arg = pkt;
  225. ossl_list_tx_history_init_elem(&pkt->ackm_pkt);
  226. pkt->ackm_pkt.anext = pkt->ackm_pkt.lnext = NULL;
  227. /*
  228. * Mark the CFQ items which have been added to this packet as having been
  229. * transmitted.
  230. */
  231. for (cfq_item = pkt->retx_head;
  232. cfq_item != NULL;
  233. cfq_item = cfq_item->pkt_next)
  234. ossl_quic_cfq_mark_tx(fifd->cfq, cfq_item);
  235. /*
  236. * Mark the send stream chunks which have been added to the packet as having
  237. * been transmitted.
  238. */
  239. chunks = ossl_quic_txpim_pkt_get_chunks(pkt);
  240. num_chunks = ossl_quic_txpim_pkt_get_num_chunks(pkt);
  241. for (i = 0; i < num_chunks; ++i) {
  242. sstream = fifd->get_sstream_by_id(chunks[i].stream_id,
  243. pkt->ackm_pkt.pkt_space,
  244. fifd->get_sstream_by_id_arg);
  245. if (sstream == NULL)
  246. continue;
  247. if (chunks[i].end >= chunks[i].start
  248. && !ossl_quic_sstream_mark_transmitted(sstream,
  249. chunks[i].start,
  250. chunks[i].end))
  251. return 0;
  252. if (chunks[i].has_fin
  253. && !ossl_quic_sstream_mark_transmitted_fin(sstream,
  254. chunks[i].end + 1))
  255. return 0;
  256. }
  257. /* Inform the ACKM. */
  258. return ossl_ackm_on_tx_packet(fifd->ackm, &pkt->ackm_pkt);
  259. }
  260. void ossl_quic_fifd_set_qlog_cb(QUIC_FIFD *fifd, QLOG *(*get_qlog_cb)(void *arg),
  261. void *get_qlog_cb_arg)
  262. {
  263. fifd->get_qlog_cb = get_qlog_cb;
  264. fifd->get_qlog_cb_arg = get_qlog_cb_arg;
  265. }