1QUIC Frame-in-Flight Management
2===============================
3
4The QUIC frame-in-flight manager is responsible for tracking frames which were
5sent which need to be regenerated if the packets they were placed into are
6designated as lost by the ACK manager. The ACK manager works on the level of
7packets, whereas the QUIC frame-in-flight manager (FIFM) works on the level of
8frames.
9
10The FIFM comprises three components, collectively known as the FIFM:
11
12  - the Control Frame Queue (CFQ);
13  - the Transmitted Packet Information Manager (TXPIM); and
14  - the Frame-in-Flight Dispatcher (FIFD).
15
16![](images/quic-fifm-overview.png "QUIC FIFM Overview")
17
18These are introduced in turn below, but first we discuss the various QUIC frame
19types to establish the need for each component.
20
21Analysis of QUIC Frame Retransmission Requirements
22--------------------------------------------------
23
24### Frame Types
25
26Standard QUIC uses the following frame types:
27
28```plain
29HANDSHAKE_DONE          GCR / REGEN
30MAX_DATA                      REGEN
31DATA_BLOCKED                  REGEN
32MAX_STREAMS                   REGEN
33STREAMS_BLOCKED               REGEN
34NEW_CONNECTION_ID             GCR
35RETIRE_CONNECTION_ID          GCR
36PATH_CHALLENGE                  -
37PATH_RESPONSE                   -
38ACK                             -        (non-ACK-eliciting)
39CONNECTION_CLOSE              special    (non-ACK-eliciting)
40NEW_TOKEN                     GCR
41CRYPTO                        GCR or special
42RESET_STREAM                  REGEN
43STOP_SENDING                  REGEN
44MAX_STREAM_DATA               REGEN
45STREAM_DATA_BLOCKED           REGEN
46STREAM                        special
47PING                           -
48PADDING                        -         (non-ACK-eliciting)
49```
50
51The different frame types require various different ways of handling
52retransmission in the event of loss:
53
54  - **GCR** (Generic Control Frame Retransmission): The raw bytes of
55    the encoded frame can simply be sent again. This retransmission system does
56    not need to understand the specific frame type. A simple queue can be used,
57    with each queue entry being an octet string representing an encoded frame.
58    This queue can also be used for initial transmission of **GCR** frames, not
59    just retransmissions.
60
61  - **REGEN** (Regenerate): These frames can be marked for dynamic regeneration
62    when a packet containing them is lost. This has the advantage of using
63    up-to-date data at the time of transmission, so is preferred over `GCR` when
64    possible.
65
66  - Special — `STREAM`, `CRYPTO`: `STREAM` frame handling is handled as a
67    special case by the QUIC Send Stream Manager. `CRYPTO` frame retransmission
68    can also be handled using a QUIC Send Stream manager. (`CRYPTO` frames could
69    also be handled via GCR, though suboptimally. We choose to use proper send
70    stream management, just as for application data streams.)
71
72  - Some frame types do not need to be retransmitted even if lost (`PING`,
73    `PADDING`, `PATH_CHALLENGE`, `PATH_RESPONSE`).
74
75  - Special — `CONNECTION_CLOSE`: This frame is a special case and is not
76    retransmitted per se.
77
78### Requirements
79
80The following requirements are identified:
81
82- Need for a generic control queue which can store encoded control frames.
83  This control queue will handle both initial transmission and retransmission of
84  most control frames which do not have special requirements.
85
86- The ability to determine, when the ACK Manager determines that a packet has
87  been acknowledged, lost or discarded:
88
89  - What stream IDs were sent in a packet, and the logical ranges of application
90    data bytes for each (which may not be one contiguous range).
91
92    This is needed so that the QUIC Send Stream Manager for a given stream
93    can be informed of lost or acked ranges in the stream.
94
95  - The logical ranges of the CRYPTO stream which were sent in the packet
96    (which may not be one contiguous range), for similar reasons.
97
98  - Which stream IDs had a FIN bit set in the packet.
99
100    This is needed so that the QUIC Send Stream Manager can be informed for a
101    given stream whether a FIN was lost or acked.
102
103  - What control frames using the **GCR** strategy were sent in the packet
104    so that they can be requeued (if lost) or released (if acked or discarded).
105
106  - For each type of frame using the **REGEN** strategy, a flag as to whether
107    that frame type was contained in the packet (so that the flag can be set
108    again if the packet was lost).
109
110The Control Frame Queue (CFQ)
111-----------------------------
112
113![](images/quic-fifm-cfq.png "QUIC CFQ Overview")
114
115The CFQ (`QUIC_CFQ`) stores encoded frames which can be blindly retransmitted in
116the event that they are lost. It facilitates the GCR retransmission strategy.
117One logical CFQ instance will be needed per PN space per connection. As an
118optimisation, these three CFQ instances per connection are all modelled by a
119single `QUIC_CFQ` instance.
120
121Each frame in the CFQ is a simple opaque byte buffer, which has the following
122metadata associated with it:
123
124- An integral priority value, used to maintain priority ordering.
125
126- The frame type, which is provided by the caller along with the buffer.
127  This can be determined from the encoded frame buffer, but this saves the
128  CFQ's users from needing to decode it. The CFQ itself does not use this
129  value.
130
131- A state, which is either `NEW` or `TX`. Frames added to the CFQ have
132  the `NEW` state initially. When the frame is transmitted, it is transitioned
133  to the `TX` state. If the packet it was sent in is subsequently lost,
134  it is transitioned back to the `NEW` state.
135
136Frames in the `NEW` state participate in a priority queue (the NEW queue)
137according to their priority and the CFQ's NEW queue can be iterated in priority
138order by callers.
139
140When a packet containing a CFQ item is acknowledged, the CFQ is informed and the
141CFQ item is released. A free callback provided when the buffer was added to the
142CFQ is called, providing an opportunity to free or reuse the buffer. Buffers
143provided to the CFQ as part of a CFQ item must remain allocated for the duration
144of their membership of the CFQ. The CFQ maintains memory allocation of CFQ items
145themselves internally.
146
147### API
148
149```c
150/*
151 * QUIC Control Frame Queue Item
152 * =============================
153 *
154 * The CFQ item structure has a public and a private part. This structure
155 * documents the public part.
156 */
157typedef struct quic_cfq_item_st QUIC_CFQ_ITEM;
158
159struct quic_cfq_item_st {
160    /*
161     * These fields are not used by the CFQ, but are a convenience to assist the
162     * TXPIM in keeping a list of GCR control frames which were sent in a
163     * packet. They may be used for any purpose.
164     */
165    QUIC_CFQ_ITEM  *pkt_prev, *pkt_next;
166
167    /* All other fields are private; use ossl_quic_cfq_item_* accessors. */
168};
169
170#define QUIC_CFQ_STATE_NEW      0
171#define QUIC_CFQ_STATE_TX       1
172
173/* Returns the frame type of a CFQ item. */
174uint64_t ossl_quic_cfq_item_get_frame_type(QUIC_CFQ_ITEM *item);
175
176/* Returns a pointer to the encoded buffer of a CFQ item. */
177const unsigned char *ossl_quic_cfq_item_get_encoded(QUIC_CFQ_ITEM *item);
178
179/* Returns the length of the encoded buffer in bytes. */
180size_t ossl_quic_cfq_item_get_encoded_len(QUIC_CFQ_ITEM *item);
181
182/* Returns the CFQ item state, a QUIC_CFQ_STATE_* value. */
183int ossl_quic_cfg_item_get_state(QUIC_CFQ_ITEM *item);
184
185/* Returns the PN space for the CFQ item. */
186int ossl_quic_cfg_item_get_pn_space(QUIC_CFQ_ITEM *item);
187
188/*
189 * QUIC Control Frame Queue
190 * ========================
191 */
192typedef struct quic_cfq_st QUIC_CFQ;
193
194QUIC_CFQ *ossl_quic_cfq_new(void);
195void ossl_quic_cfq_free(QUIC_CFQ *cfq);
196
197/*
198 * Input Side
199 * ----------
200 */
201
202/*
203 * Enqueue a frame to the CFQ. encoded points to the opaque encoded frame.
204 *
205 * free_cb is called by the CFQ when the buffer is no longer needed;
206 * free_cb_arg is an opaque value passed to free_cb.
207 *
208 * priority determines the relative ordering of control frames in a packet.
209 * Higher numerical values for priority mean that a frame should come earlier in
210 * a packet. pn_space is a QUIC_PN_SPACE_* value.
211 *
212 * On success, returns a QUIC_CFQ_ITEM pointer which acts as a handle to
213 * the queued frame. On failure, returns NULL.
214 *
215 * The frame is initially in the TX state, so there is no need to call
216 * ossl_quic_cfq_mark_tx() immediately after calling this function.
217 *
218 * The frame type is duplicated as the frame_type argument here, even though it
219 * is also encoded into the buffer. This allows the caller to determine the
220 * frame type if desired without having to decode the frame.
221 */
222typedef void (cfq_free_cb)(unsigned char *buf, size_t buf_len, void *arg);
223
224QUIC_CFQ_ITEM *ossl_quic_cfq_add_frame(QUIC_CFQ            *cfq,
225                                       uint32_t             priority,
226                                       uint32_t             pn_space,
227                                       uint64_t             frame_type,
228                                       const unsigned char *encoded,
229                                       size_t               encoded_len,
230                                       cfq_free_cb         *free_cb,
231                                       void                *free_cb_arg);
232
233/*
234 * Effects an immediate transition of the given CFQ item to the TX state.
235 */
236void ossl_quic_cfq_mark_tx(QUIC_CFQ *cfq, QUIC_CFQ_ITEM *item);
237
238/*
239 * Effects an immediate transition of the given CFQ item to the NEW state,
240 * allowing the frame to be retransmitted. If priority is not UINT32_MAX,
241 * the priority is changed to the given value.
242 */
243void ossl_quic_cfq_mark_lost(QUIC_CFQ *cfq, QUIC_CFQ_ITEM *item,
244                             uint32_t priority);
245
246/*
247 * Releases a CFQ item. The item may be in either state (NEW or TX) prior to the
248 * call. The QUIC_CFQ_ITEM pointer must not be used following this call.
249 */
250void ossl_quic_cfq_release(QUIC_CFQ *cfq, QUIC_CFQ_ITEM *item);
251
252/*
253 * Output Side
254 * -----------
255 */
256
257/*
258 * Gets the highest priority CFQ item in the given PN space awaiting
259 * transmission. If there are none, returns NULL.
260 */
261QUIC_CFQ_ITEM *ossl_quic_cfq_get_priority_head(QUIC_CFQ *cfq, uint32_t pn_space);
262
263/*
264 * Given a CFQ item, gets the next CFQ item awaiting transmission in priority
265 * order in the given PN space. In other words, given the return value of
266 * ossl_quic_cfq_get_priority_head(), returns the next-lower priority item.
267 * Returns NULL if the given item is the last item in priority order.
268 */
269QUIC_CFQ_ITEM *ossl_quic_cfq_item_get_priority_next(QUIC_CFQ_ITEM *item,
270                                                    uint32_t pn_space);
271```
272
273The Transmitted Packet Information Manager (TXPIM)
274--------------------------------------------------
275
276![](images/quic-fifm-txpim.png "QUIC TXPIM Overview")
277
278The Transmitted Packet Information Manager (`QUIC_TXPIM`) is responsible for
279allocating and keeping bookkeeping structures for packets which have been
280transmitted, but not yet acknowledged, deemed lost or discarded. It is a
281self-contained memory pool handing out `QUIC_TXPIM_PKT` structures. Each
282`QUIC_TXPIM_PKT` is a self-contained data structure intended for consumption by
283the FIFM.
284
285The `QUIC_TXPIM_PKT` structure can be used for:
286
287  - Keeping track of all GCR control frames which were transmitted
288    in each packet, via a linked list of `QUIC_CFQ_ITEM`s.
289
290  - Keeping track of all REGEN-strategy control frame types, via a flag
291    for each frame type indicating whether the packet contained
292    such a frame.
293
294  - Keeping track of all stream IDs sent in a given packet, and
295    what ranges of the logical stream were sent, and whether
296    a FIN was sent.
297
298  - Keeping track of what logical ranges of the CRYPTO stream were sent.
299
300In order to avoid unnecessary allocations, the FIFM also incorporates the ACK
301Manager's `QUIC_ACKM_TX_PKT` structure into its per-packet bookkeeping
302structure. The intention is for the `QUIC_TXPIM_PKT` to be the principal
303allocation made per transmitted packet. The TX packetiser will obtain
304a `QUIC_TXPIM_PKT` structure from the TXPIM, fill in the structure including
305the ACK Manager data, and submit it via the FIFD which we introduce below.
306
307The TXPIM does do not anything with the `QUIC_TXPIM_PKT` structure itself other
308than managing its allocation and manipulation. Constructive use of the data kept
309in the TXPIM is made by the FIFD.
310
311### API
312
313```c
314/*
315 * QUIC Transmitted Packet Information Manager
316 * ===========================================
317 */
318typedef struct quic_txpim_st QUIC_TXPIM;
319
320typedef struct quic_txpim_pkt_st {
321    /* ACKM-specific data. Caller should fill this. */
322    QUIC_ACKM_TX_PKT    ackm_pkt;
323
324    /* Linked list of CFQ items in this packet. */
325    QUIC_CFQ_ITEM      *retx_head;
326
327    /* Reserved for FIFD use. */
328    QUIC_FIFD          *fifd;
329
330    /* Regenerate-strategy frames. */
331    unsigned int        had_handshake_done          : 1;
332    unsigned int        had_max_data_frame          : 1;
333    unsigned int        had_max_streams_bidi_frame  : 1;
334    unsigned int        had_max_streams_uni_frame   : 1;
335    unsigned int        had_ack_frame               : 1;
336
337    /* Private data follows. */
338} QUIC_TXPIM_PKT;
339
340/* Represents a range of bytes in an application or CRYPTO stream. */
341typedef struct quic_txpim_chunk_st {
342    /* The stream ID, or UINT64_MAX for the CRYPTO stream. */
343    uint64_t        stream_id;
344    /*
345     * The inclusive range of bytes in the stream. Exceptionally, if end <
346     * start, designates a frame of zero length (used for FIN-only frames).
347     */
348    uint64_t        start, end;
349    /*
350     * Whether a FIN was sent for this stream in the packet. Not valid for
351     * CRYPTO stream.
352     */
353    unsigned int    has_fin : 1;
354} QUIC_TXPIM_CHUNK;
355
356QUIC_TXPIM *ossl_quic_txpim_new(void);
357void ossl_quic_txpim_free(QUIC_TXPIM *txpim);
358
359/*
360 * Allocates a new QUIC_TXPIM_PKT structure from the pool. Returns NULL on
361 * failure. The returned structure is cleared of all data and is in a fresh
362 * initial state.
363 */
364QUIC_TXPIM_PKT *ossl_quic_txpim_pkt_alloc(QUIC_TXPIM *txpim);
365
366/*
367 * Releases the TXPIM packet, returning it to the pool.
368 */
369void ossl_quic_txpim_pkt_release(QUIC_TXPIM *txpim, QUIC_TXPIM_PKT *fpkt);
370
371/* Clears the chunk list of the packet, removing all entries. */
372void ossl_quic_txpim_pkt_clear_chunks(QUIC_TXPIM_PKT *fpkt);
373
374/* Appends a chunk to the packet. The structure is copied. */
375int ossl_quic_txpim_pkt_append_chunk(QUIC_TXPIM_PKT *fpkt,
376                                    const QUIC_TXPIM_CHUNK *chunk);
377
378/* Adds a CFQ item to the packet by prepending it to the retx_head list. */
379void ossl_quic_txpim_pkt_add_cfq_item(QUIC_TXPIM_PKT *fpkt,
380                                      QUIC_CFQ_ITEM *item);
381
382/*
383 * Returns a pointer to an array of stream chunk information structures for the
384 * given packet. The caller must call ossl_quic_txpim_pkt_get_num_chunks() to
385 * determine the length of this array.
386 *
387 * The chunks are sorted by (stream_id, start) in ascending order.
388 */
389const QUIC_TXPIM_CHUNK *ossl_quic_txpim_pkt_get_chunks(QUIC_TXPIM_PKT *fpkt);
390
391/*
392 * Returns the number of entries in the array returned by
393 * ossl_quic_txpim_pkt_get_chunks().
394 */
395size_t ossl_quic_txpim_pkt_get_num_chunks(QUIC_TXPIM_PKT *fpkt);
396
397/*
398 * Returns the number of QUIC_TXPIM_PKTs allocated by the given TXPIM that have
399 * yet to be returned to the TXPIM.
400 */
401size_t ossl_quic_txpim_get_in_use(QUIC_TXPIM *txpim);
402```
403
404The Frame-in-Flight Dispatcher (FIFD)
405-------------------------------------
406
407Finally, the CFQ, TXPIM and some interfaces to the ACKM are tied together via
408the FIFD (`QUIC_FIFD`). The FIFD is completely stateless and provides reasonable
409implementations for the on-loss, on-acked and on-discarded callbacks issued by
410the ACK Manager.
411
412The FIFD is used by obtaining a packet structure from the TXPIM, filling it in,
413and then calling `ossl_quic_fifd_pkt_commit()`. The FIFD submits the packet to
414the ACK Manager as a transmitted packet and provides its own callback
415implementations to the ACK Manager for the packet. Note that the
416`QUIC_TXPIM_PKT` is returned to the free pool once any of these callbacks occur;
417once a packet's fate is known (acked, lost or discarded), use is immediately
418made of the information in the `QUIC_TXPIM_PKT` and the `QUIC_TXPIM_PKT` is
419immediately released. CFQ items may be freed (on ACK or discard) or transitioned
420back to the NEW state (on loss).
421
422The FIFD consumes various dependencies so that it can inform the appropriate
423subsystems in the event of a packet being acked, lost or discarded. In
424particular:
425
426- It references a CFQ used to manage CFQ items;
427- It references an ACK manager which it informs of transmitted packets;
428- It references a TXPIM which manages each `QUIC_TXPIM_PKT`;
429- It is provided with a callback to obtain a QUIC Send Stream based on a stream
430  ID. Thus the caller of the FIFD may implement whatever strategy it likes
431  to map stream IDs to QUIC Send Stream instances.
432- It is provided with a callback which is called when it thinks a frame
433  should be regenerated using the REGEN strategy. Some of these are specific
434  to a given stream, in which case a stream ID is specified.
435
436All of the state is in the dependencies referenced by the FIFD. The FIFD itself
437simply glues all of these parts together.
438
439### API
440
441```c
442typedef struct quic_fifd_st {
443  /* (internals) */
444} QUIC_FIFD;
445
446int ossl_quic_fifd_init(QUIC_FIFD *fifd,
447                        QUIC_CFQ *cfq,
448                        QUIC_ACKM *ackm,
449                        QUIC_TXPIM *txpim,
450                        /* stream_id is UINT64_MAX for the crypto stream */
451                        OSSL_QSS *(*get_qss_by_id)(uint64_t stream_id,
452                                                   void *arg),
453                        void *get_qss_by_id_arg,
454                        /* stream_id is UINT64_MAX if not applicable */
455                        void (*regen_frame)(uint64_t frame_type,
456                                            uint64_t stream_id,
457                                            void *arg),
458                        void *regen_frame_arg);
459void ossl_quic_fifd_cleanup(QUIC_FIFD *fifd); /* (no-op) */
460
461int ossl_quic_fifd_pkt_commit(QUIC_FIFD *fifd, QUIC_TXPIM_PKT *pkt);
462```
463
464Typical Intended TX Packetiser Usage
465------------------------------------
466
467- TX Packetiser maintains flags for each REGEN-strategy frame type.
468  It sets this flag when the regenerate callback is issued by the FIFD
469  and clears it when transmitting a packet containing such a frame.
470
471- TX Packetiser obtains a `QUIC_TXPIM_PKT` structure by calling
472  `ossl_quic_txpim_pkt_alloc()`.
473
474- TX Packetiser fills in the ACKM part of the `QUIC_TXPIM_PKT`
475  (`QUIC_ACKM_TX_PKT`), except for the callback fields, which are handled by the
476  FIFD.
477
478- TX Packetiser queries the ACK Manager to determine if an ACK frame
479  is desired, and if so adds it to the packet.
480
481- TX Packetiser queries the CFQ to determine what control frames it places
482  in a packet. It does this before adding STREAM or CRYPTO frames (i.e.,
483  all CFQ frames are considered of higher priority). For each such frame
484  it places in a packet, it:
485
486  - calls `ossl_quic_txpim_pkt_add_cfq_item()` on the TXPIM to log the CFQ item
487    as having been transmitted in the given packet, so that the CFQ item can be
488    released or requeued depending on the ultimate fate of the packet.
489
490- For each STREAM or CRYPTO frame included in a packet, the TX Packetiser:
491
492  - informs the QUIC Send Stream instance for that stream that a range of bytes
493    has been transmitted;
494
495  - also informs the QUIC Send Stream instance if FIN was set on a STREAM frame.
496
497  - calls `ossl_quic_txpim_pkt_append_chunk()` to log a logical range of
498    the given application or crypto stream as having been sent, so that it can
499    be subsequently marked as acknowledged or lost depending on the ultimate
500    fate of the packet.
501
502- TX Packetiser calls `ossl_quic_fifd_pkt_commit()`. The FIFD takes care
503  of submitting the packet to the ACK Manager and provides its own callback
504  implementation. It also takes care of informing the CFQ that any CFQ items
505  which were added via `ossl_quic_txpim_pkt_add_cfq_item()` have been
506  transmitted.
507
508  In the event of packet loss, ACK or discard, the appropriate QUIC Send Stream,
509  CFQ and regenerate callback calls are made. Regardless of the outcome, the
510  TXPIM is released.
511