ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
quic_frame.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4#ifndef PROTOCORE_QUIC_FRAME_H
5#define PROTOCORE_QUIC_FRAME_H
6
7#include "protocore_config.h" // the entry point: protocore_types.h for the widths
8
10
11/**
12 * @file quic_frame.h
13 * @brief QUIC frame parsing and building (RFC 9000 sec 19).
14 *
15 * The payload of a QUIC packet is a sequence of frames, each `Frame Type (i)` followed by
16 * type-specific fields coded with QUIC varints. This module reads one frame at a time into a
17 * tagged QuicFrameHeader and builds the frames a server sends. It covers the frames a minimal HTTP/3
18 * server needs - PADDING, PING, ACK, CRYPTO, STREAM, MAX_DATA, CONNECTION_CLOSE, HANDSHAKE_DONE -
19 * and reports the frame type for anything else so the caller can decide.
20 *
21 * Data-bearing frames (CRYPTO / STREAM / CONNECTION_CLOSE reason) point into the caller's packet
22 * buffer; nothing is copied. Pure, zero heap, host-tested.
23 *
24 * @c work is bytes the CALLER holds. This module reads none of them: it carries nothing
25 * between calls, so there is no state to keep and nothing to wipe. The parameter is there so
26 * a caller drives every namespace the same way.
27 *
28 * @author Douglas Quigg (dstroy0)
29 * @date 2026
30 */
31
32// PROTOCORE_QUIC_FRAME_BORROW - the bytes this module runs out of - is stated in protocore_config.h, which sums
33// it into its arena. Its size and its offset are each a static_assert, so a feature
34// combination that does not fit fails to compile rather than overrunning at run time.
35
36#define QUIC_FT_PADDING 0x00
37#define QUIC_FT_PING 0x01
38#define QUIC_FT_ACK 0x02 ///< 0x02 (no ECN) .. 0x03 (with ECN counts)
39#define QUIC_FT_ACK_ECN 0x03
40#define QUIC_FT_CRYPTO 0x06
41#define QUIC_FT_STREAM 0x08 ///< 0x08..0x0f; low 3 bits are OFF (0x04) / LEN (0x02) / FIN (0x01)
42#define QUIC_FT_MAX_DATA 0x10
43#define QUIC_FT_CONNECTION_CLOSE 0x1c ///< transport-level close (carries the triggering frame type)
44#define QUIC_FT_CONNECTION_CLOSE_APP 0x1d ///< application-level close
45#define QUIC_FT_HANDSHAKE_DONE 0x1e
46
47// Frames the minimal server does not act on but MUST still parse (skip) so a well-formed frame from
48// a real client is not rejected as a FRAME_ENCODING_ERROR (RFC 9000 sec 12.4). Grouped by wire shape
49// in ::QuicFrameNs::parse: 3 varints (RESET_STREAM), 2 varints (STOP_SENDING / MAX_STREAM_DATA /
50// STREAM_DATA_BLOCKED), 1 varint (MAX_STREAMS / DATA_BLOCKED / STREAMS_BLOCKED / RETIRE_CONNECTION_ID),
51// and the length-prefixed / fixed-width shapes (NEW_TOKEN, NEW_CONNECTION_ID, PATH_CHALLENGE/RESPONSE).
52#define QUIC_FT_RESET_STREAM 0x04
53#define QUIC_FT_STOP_SENDING 0x05
54#define QUIC_FT_NEW_TOKEN 0x07
55#define QUIC_FT_MAX_STREAM_DATA 0x11
56#define QUIC_FT_MAX_STREAMS_BIDI 0x12
57#define QUIC_FT_MAX_STREAMS_UNI 0x13
58#define QUIC_FT_DATA_BLOCKED 0x14
59#define QUIC_FT_STREAM_DATA_BLOCKED 0x15
60#define QUIC_FT_STREAMS_BLOCKED_BIDI 0x16
61#define QUIC_FT_STREAMS_BLOCKED_UNI 0x17
62#define QUIC_FT_NEW_CONNECTION_ID 0x18
63#define QUIC_FT_RETIRE_CONNECTION_ID 0x19
64#define QUIC_FT_PATH_CHALLENGE 0x1a
65#define QUIC_FT_PATH_RESPONSE 0x1b
66
67/** @brief STREAM frame type bits. */
68#define QUIC_STREAM_FIN 0x01
69#define QUIC_STREAM_LEN 0x02
70#define QUIC_STREAM_OFF 0x04
71
72/** @brief Transport error codes for CONNECTION_CLOSE (RFC 9000 sec 20.1). */
73#define QUIC_ERR_NO_ERROR 0x00
74#define QUIC_ERR_INTERNAL 0x01
75#define QUIC_ERR_FLOW_CONTROL 0x03
76#define QUIC_ERR_STREAM_LIMIT 0x04
77#define QUIC_ERR_FRAME_ENCODING 0x07 ///< a frame could not be decoded
78#define QUIC_ERR_PROTOCOL_VIOLATION 0x0a ///< a frame/packet violated the protocol
79#define QUIC_ERR_APPLICATION 0x0c ///< the application abandoned the connection (sec 20.1)
80#define QUIC_ERR_CRYPTO_BASE 0x0100 ///< 0x0100 + the TLS alert code (RFC 9001 sec 4.8)
81
82/** @brief ACK payload (RFC 9000 sec 19.3). */
83typedef struct
84{
85 uint64_t largest; ///< Largest Acknowledged
86 uint64_t delay; ///< ACK Delay (encoded units)
87 uint64_t range_count; ///< number of additional ACK Ranges (skipped, but counted)
88 uint64_t first_range; ///< First ACK Range
90
91/** @brief CRYPTO payload (RFC 9000 sec 19.6). @c data aliases the input buffer. */
92typedef struct
93{
94 uint64_t offset;
95 uint64_t length;
96 const uint8_t *data;
98
99/** @brief STREAM payload (RFC 9000 sec 19.8). @c data aliases the input buffer. */
100typedef struct
101{
102 uint64_t id;
103 uint64_t offset; ///< 0 when the OFF bit is clear
104 uint64_t length;
105 const uint8_t *data;
106 uint8_t fin;
108
109/** @brief MAX_DATA payload (RFC 9000 sec 19.9). */
110typedef struct
111{
112 uint64_t max;
114
115/** @brief CONNECTION_CLOSE payload (RFC 9000 sec 19.19). @c reason aliases the input buffer. */
116typedef struct
117{
118 uint64_t error_code;
119 uint64_t frame_type; ///< 0 for the application-level variant (0x1d)
120 uint64_t reason_len;
121 const uint8_t *reason;
122 uint8_t app; ///< 1 if this was the application-level close (0x1d)
124
125/** @brief One parsed frame. Pointer fields alias the input buffer (not copied). */
126typedef struct
127{
128 uint64_t type; ///< the frame type (STREAM reported as its exact 0x08..0x0f value)
129 union {
135 };
137
138/** @brief Dispatch table. Addressed by offset, so the layout is asserted below. */
139typedef struct
140{
141 size_t (*parse)(uint8_t *, const uint8_t *, size_t, QuicFrameHeader *);
142 size_t (*build_padding)(uint8_t *, uint8_t *, size_t, size_t);
143 size_t (*build_ping)(uint8_t *, uint8_t *, size_t);
144 size_t (*build_handshake_done)(uint8_t *, uint8_t *, size_t);
145 size_t (*build_ack)(uint8_t *, uint8_t *, size_t, uint64_t, uint64_t, uint64_t);
146 size_t (*build_crypto)(uint8_t *, uint8_t *, size_t, uint64_t, const uint8_t *, size_t);
147 size_t (*build_stream)(uint8_t *, uint8_t *, size_t, uint64_t, uint64_t, const uint8_t *, size_t, proto_bool);
148 size_t (*build_max_data)(uint8_t *, uint8_t *, size_t, uint64_t);
149 size_t (*build_connection_close)(uint8_t *, uint8_t *, size_t, proto_bool, uint64_t, uint64_t, const char *,
150 size_t);
152PROTOCORE_NS_LAYOUT(QuicFrameNs, parse, build_padding, build_ping, build_handshake_done, build_ack, build_crypto,
153 build_stream, build_max_data, build_connection_close);
154
155/**
156 * @brief Parse one frame at buf. bytes consumed, or 0 on malformed / .
157 * @param work PROTOCORE_QUIC_FRAME_BORROW bytes the caller took. Not held past the call.
158 * @param buf Buf
159 * @param len Len
160 * @param out Out
161 * @return The size_t.
162 */
163size_t protocore_quic_frame_parse(uint8_t *work, const uint8_t *buf, size_t len, QuicFrameHeader *out);
164/**
165 * @brief N PADDING frames (n zero bytes). n, or 0 if it does not fit.
166 * @param work PROTOCORE_QUIC_FRAME_BORROW bytes the caller took. Not held past the call.
167 * @param out Out
168 * @param cap Cap
169 * @param n N
170 * @return The size_t.
171 */
172size_t protocore_quic_frame_build_padding(uint8_t *work, uint8_t *out, size_t cap, size_t n);
173/**
174 * @brief A PING frame.
175 * @param work PROTOCORE_QUIC_FRAME_BORROW bytes the caller took. Not held past the call.
176 * @param out Out
177 * @param cap Cap
178 * @return The size_t.
179 */
180size_t protocore_quic_frame_build_ping(uint8_t *work, uint8_t *out, size_t cap);
181/**
182 * @brief A HANDSHAKE_DONE frame.
183 * @param work PROTOCORE_QUIC_FRAME_BORROW bytes the caller took. Not held past the call.
184 * @param out Out
185 * @param cap Cap
186 * @return The size_t.
187 */
188size_t protocore_quic_frame_build_handshake_done(uint8_t *work, uint8_t *out, size_t cap);
189/**
190 * @brief A single-range ACK frame (ACK Range Count 0): Largest, ACK Delay, .
191 * @param work PROTOCORE_QUIC_FRAME_BORROW bytes the caller took. Not held past the call.
192 * @param out Out
193 * @param cap Cap
194 * @param largest Largest
195 * @param delay Delay
196 * @param first_range First range
197 * @return The size_t.
198 */
199size_t protocore_quic_frame_build_ack(uint8_t *work, uint8_t *out, size_t cap, uint64_t largest, uint64_t delay,
200 uint64_t first_range);
201/**
202 * @brief A CRYPTO frame carrying len bytes at stream offset.
203 * @param work PROTOCORE_QUIC_FRAME_BORROW bytes the caller took. Not held past the call.
204 * @param out Out
205 * @param cap Cap
206 * @param offset Offset
207 * @param data Data
208 * @param len Len
209 * @return The size_t.
210 */
211size_t protocore_quic_frame_build_crypto(uint8_t *work, uint8_t *out, size_t cap, uint64_t offset, const uint8_t *data,
212 size_t len);
213/**
214 * @brief A STREAM frame (LEN always set; OFF set when offset > 0; FIN per .
215 * @param work PROTOCORE_QUIC_FRAME_BORROW bytes the caller took. Not held past the call.
216 * @param out Out
217 * @param cap Cap
218 * @param id Id
219 * @param offset Offset
220 * @param data Data
221 * @param len Len
222 * @param fin Fin
223 * @return The size_t.
224 */
225size_t protocore_quic_frame_build_stream(uint8_t *work, uint8_t *out, size_t cap, uint64_t id, uint64_t offset,
226 const uint8_t *data, size_t len, proto_bool fin);
227/**
228 * @brief A MAX_DATA frame.
229 * @param work PROTOCORE_QUIC_FRAME_BORROW bytes the caller took. Not held past the call.
230 * @param out Out
231 * @param cap Cap
232 * @param max Max
233 * @return The size_t.
234 */
235size_t protocore_quic_frame_build_max_data(uint8_t *work, uint8_t *out, size_t cap, uint64_t max);
236/**
237 * @brief A CONNECTION_CLOSE with a reason phrase. app selects the .
238 * @param work PROTOCORE_QUIC_FRAME_BORROW bytes the caller took. Not held past the call.
239 * @param out Out
240 * @param cap Cap
241 * @param app App
242 * @param error_code Error code
243 * @param frame_type Frame type
244 * @param reason Reason
245 * @param reason_len Reason len
246 * @return The size_t.
247 */
248size_t protocore_quic_frame_build_connection_close(uint8_t *work, uint8_t *out, size_t cap, proto_bool app,
249 uint64_t error_code, uint64_t frame_type, const char *reason,
250 size_t reason_len);
251
252/** @brief Module namespace. */
263
265
266#endif // PROTOCORE_QUIC_FRAME_H
#define PROTOCORE_NS_LAYOUT(T,...)
Pin every dispatch slot of a table that is nothing but function pointers.
#define PROTOCORE_NS
Storage for a dispatch table. The const is load bearing.
size_t protocore_quic_frame_build_stream(uint8_t *work, uint8_t *out, size_t cap, uint64_t id, uint64_t offset, const uint8_t *data, size_t len, proto_bool fin)
A STREAM frame (LEN always set; OFF set when offset > 0; FIN per .
PROTOCORE_NS QuicFrameNs QuicFrame PROTOCORE_UNUSED
Module namespace.
Definition quic_frame.h:253
size_t protocore_quic_frame_build_ack(uint8_t *work, uint8_t *out, size_t cap, uint64_t largest, uint64_t delay, uint64_t first_range)
A single-range ACK frame (ACK Range Count 0): Largest, ACK Delay, .
size_t protocore_quic_frame_build_crypto(uint8_t *work, uint8_t *out, size_t cap, uint64_t offset, const uint8_t *data, size_t len)
A CRYPTO frame carrying len bytes at stream offset.
size_t protocore_quic_frame_build_max_data(uint8_t *work, uint8_t *out, size_t cap, uint64_t max)
A MAX_DATA frame.
size_t protocore_quic_frame_build_padding(uint8_t *work, uint8_t *out, size_t cap, size_t n)
N PADDING frames (n zero bytes). n, or 0 if it does not fit.
size_t protocore_quic_frame_parse(uint8_t *work, const uint8_t *buf, size_t len, QuicFrameHeader *out)
Parse one frame at buf. bytes consumed, or 0 on malformed / .
size_t protocore_quic_frame_build_handshake_done(uint8_t *work, uint8_t *out, size_t cap)
A HANDSHAKE_DONE frame.
size_t protocore_quic_frame_build_ping(uint8_t *work, uint8_t *out, size_t cap)
A PING frame.
size_t protocore_quic_frame_build_connection_close(uint8_t *work, uint8_t *out, size_t cap, proto_bool app, uint64_t error_code, uint64_t frame_type, const char *reason, size_t reason_len)
A CONNECTION_CLOSE with a reason phrase. app selects the .
ACK payload (RFC 9000 sec 19.3).
Definition quic_frame.h:84
uint64_t delay
ACK Delay (encoded units)
Definition quic_frame.h:86
uint64_t range_count
number of additional ACK Ranges (skipped, but counted)
Definition quic_frame.h:87
uint64_t largest
Largest Acknowledged.
Definition quic_frame.h:85
uint64_t first_range
First ACK Range.
Definition quic_frame.h:88
CONNECTION_CLOSE payload (RFC 9000 sec 19.19). reason aliases the input buffer.
Definition quic_frame.h:117
uint64_t frame_type
0 for the application-level variant (0x1d)
Definition quic_frame.h:119
uint64_t error_code
Definition quic_frame.h:118
uint8_t app
1 if this was the application-level close (0x1d)
Definition quic_frame.h:122
uint64_t reason_len
Definition quic_frame.h:120
const uint8_t * reason
Definition quic_frame.h:121
CRYPTO payload (RFC 9000 sec 19.6). data aliases the input buffer.
Definition quic_frame.h:93
const uint8_t * data
Definition quic_frame.h:96
uint64_t length
Definition quic_frame.h:95
uint64_t offset
Definition quic_frame.h:94
One parsed frame. Pointer fields alias the input buffer (not copied).
Definition quic_frame.h:127
QuicMaxDataFrame max_data
Definition quic_frame.h:133
QuicStreamFrame stream
Definition quic_frame.h:132
QuicCloseFrame close
Definition quic_frame.h:134
QuicAckFrame ack
Definition quic_frame.h:130
QuicCryptoFrame crypto
Definition quic_frame.h:131
uint64_t type
the frame type (STREAM reported as its exact 0x08..0x0f value)
Definition quic_frame.h:128
Dispatch table. Addressed by offset, so the layout is asserted below.
Definition quic_frame.h:140
size_t(* parse)(uint8_t *, const uint8_t *, size_t, QuicFrameHeader *)
Definition quic_frame.h:141
MAX_DATA payload (RFC 9000 sec 19.9).
Definition quic_frame.h:111
STREAM payload (RFC 9000 sec 19.8). data aliases the input buffer.
Definition quic_frame.h:101
uint64_t offset
0 when the OFF bit is clear
Definition quic_frame.h:103
const uint8_t * data
Definition quic_frame.h:105
uint64_t length
Definition quic_frame.h:104
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97