Line data Source code
1 : #ifndef HEADER_fd_src_waltz_quic_fd_quic_h
2 : #define HEADER_fd_src_waltz_quic_fd_quic_h
3 :
4 : /* fd_quic_t is a partial implementation of QUIC -- an encrypted,
5 : multiplexing transport layer network protocol.
6 :
7 : For now, IPv4 over Ethernet (EN10MB) flows are supported.
8 :
9 : This API is non-blocking and single-threaded. Any requests to the
10 : peer (such as "open a connection") are queued and processed on RX
11 : or service call. The user is notified of events via callbacks.
12 : The user must further ensure that RX (via fd_aio_t) is dispatched
13 : only from the thread with the active join to the target fd_quic_t.
14 :
15 : Scaling is achieved via fd_quic_t instances and steering of RX flows.
16 : For example, incoming RX that exceeds the throughput of one fd_quic_t
17 : may be load balanced based on QUIC dest conn ID, or UDP src flow ID.
18 :
19 : This implementation partially implements the following specifications:
20 : - https://datatracker.ietf.org/doc/html/rfc9000
21 : - https://datatracker.ietf.org/doc/html/rfc9001
22 : - https://datatracker.ietf.org/doc/html/rfc9221
23 :
24 : ### Memory Management
25 :
26 : fd_quic is entirely pre-allocated. Currently, a QUIC object reserves
27 : space for a number of connection slots, with uniform stream,
28 : reassembly, and ACK buffers.
29 :
30 : ### Memory Layout
31 :
32 : fd_quic_t is the publicly exported memory layout of a QUIC object.
33 : The private memory region of a QUIC object extends beyond the end of
34 : this struct. fd_quic_t is not intended to be allocated directly,
35 : refer to the below for details.
36 :
37 : ### Lifecycle
38 :
39 : The below state diagram shows the lifecycle of an fd_quic_t.
40 :
41 : ┌───────────┐ new ┌───────────┐ join ┌──────────┐
42 : │ ├───────►│ ├──────►│ │
43 : │ allocated │ │ formatted │ │ joined │
44 : │ │◄───────┤ │◄──────┤ │
45 : └───────────┘ delete └───────────┘ leave └───▲───┬──┘
46 : │ │ set config
47 : │ │ set callbacks
48 : fini │ │ init
49 : ┌──┴───▼──┐
50 : ┌───│ │
51 : service │ │ ready │
52 : └──►│ │
53 : └─────────┘
54 :
55 : ### Lifecycle: Allocation & Formatting
56 :
57 : A QUIC object resides in a contiguous pre-allocated memory region.
58 : (Usually, in a Firedancer workspace) The footprint and internal
59 : layout depends on the pre-configured fd_quic_limits_t parameters.
60 : These limits are constant throughout the lifetime of an fd_quic_t.
61 :
62 : Use fd_quic_{align,footprint} to determine size and alignment of the
63 : memory region to be used. Use fd_quic_new to format such a memory
64 : region and to obtain an opaque handle. In the formatted state, the
65 : fd_quic_t is position-independent (may be mapped at different virtual
66 : addresses). This is useful for separating allocation and runtime use
67 : into different steps.
68 :
69 : ### Lifecycle: Joining
70 :
71 : Given an opaque handle, fd_quic_join runs basic coherence checks and
72 : returns a typed pointer to the object. The object is not modified
73 : by this operation. Each object may have multiple active joins, but
74 : only one of them may write. (Typically, a single join is used for
75 : service, and secondary joins for read-only monitoring)
76 :
77 : ### Lifecycle: Usage
78 :
79 : fd_quic_init initializes an fd_quic_t for use. On success, the QUIC
80 : becomes ready to serve from the thread that init was called from (it
81 : is invalid to service QUIC from another thread). */
82 :
83 : /* TODO provide fd_quic on non-hosted targets */
84 :
85 : #include "fd_quic_common.h"
86 : #include "fd_quic_enum.h"
87 :
88 : #include "../aio/fd_aio.h"
89 : #include "../tls/fd_tls.h"
90 : #include "../../util/clock/fd_clock.h"
91 : #include "../../util/hist/fd_histf.h"
92 :
93 : /* FD_QUIC_API marks public API declarations. No-op for now. */
94 : #define FD_QUIC_API
95 :
96 : /* Forward declarations */
97 :
98 : struct fd_quic_conn;
99 : typedef struct fd_quic_conn fd_quic_conn_t;
100 :
101 : struct fd_quic_stream;
102 : typedef struct fd_quic_stream fd_quic_stream_t;
103 :
104 : /* fd_quic_limits_t defines the memory layout of an fd_quic_t object.
105 : Limits are immutable and valid for the lifetime of an fd_quic_t
106 : (i.e. outlasts joins, until fd_quic_delete) */
107 :
108 : struct __attribute__((aligned(16UL))) fd_quic_limits {
109 : ulong conn_cnt; /* instance-wide, max concurrent conn count */
110 : ulong handshake_cnt; /* instance-wide, max concurrent handshake count */
111 : ulong log_depth; /* instance-wide, depth of shm log cache */
112 :
113 : ulong conn_id_cnt; /* per-conn, max conn ID count (min 4UL) */
114 : ulong stream_id_cnt; /* per-conn, max concurrent stream ID count */
115 : ulong inflight_frame_cnt; /* instance-wide, total max inflight frame count */
116 : ulong min_inflight_frame_cnt_conn; /* per-conn, min inflight frame count */
117 :
118 : ulong tx_buf_sz; /* per-stream, tx buf sz in bytes */
119 : /* the user consumes rx directly from the network buffer */
120 :
121 : ulong stream_pool_cnt; /* instance-wide, number of streams in stream pool */
122 : };
123 : typedef struct fd_quic_limits fd_quic_limits_t;
124 :
125 : /* fd_quic_layout_t is an offset table describing the memory layout of
126 : an fd_quic_t object. It is deived from fd_quic_limits_t. */
127 :
128 : struct fd_quic_layout {
129 : ulong meta_sz; /* size of this struct */
130 : ulong log_off; /* offset to quic_log */
131 : ulong conns_off; /* offset of connection mem region */
132 : ulong conn_footprint; /* sizeof a conn */
133 : ulong conn_map_off; /* offset of conn map mem region */
134 : int lg_slot_cnt; /* see conn_map_new */
135 : ulong hs_pool_off; /* offset of the handshake pool */
136 : ulong stream_pool_off; /* offset of the stream pool */
137 : ulong svc_timers_off; /* offset of the service timers */
138 : ulong pkt_meta_pool_off; /* offset of the pkt_meta pool */
139 : };
140 :
141 : typedef struct fd_quic_layout fd_quic_layout_t;
142 :
143 : /* fd_quic_config_t defines mutable config of an fd_quic_t. The config is
144 : immutable during an active join. */
145 :
146 : struct __attribute__((aligned(16UL))) fd_quic_config {
147 : /* Used by tracing/logging code */
148 : #define FD_QUIC_CONFIG_ENUM_LIST_role(X,...) \
149 : X( FD_QUIC_ROLE_CLIENT, "ROLE_CLIENT" ) \
150 : X( FD_QUIC_ROLE_SERVER, "ROLE_SERVER" )
151 :
152 : #define FD_QUIC_CONFIG_LIST(X,...) \
153 0 : X( role, "%d", enum, "enum", __VA_ARGS__ ) \
154 0 : X( retry, "%d", bool, "bool", __VA_ARGS__ ) \
155 0 : X( idle_timeout, "%ld", units, "ns", __VA_ARGS__ ) \
156 0 : X( keep_alive, "%d", bool, "bool", __VA_ARGS__ ) \
157 0 : X( ack_delay, "%ld", units, "ns", __VA_ARGS__ ) \
158 0 : X( ack_threshold, "%lu", units, "bytes", __VA_ARGS__ ) \
159 0 : X( retry_ttl, "%ld", units, "ns", __VA_ARGS__ ) \
160 0 : X( tls_hs_ttl, "%ld", units, "ns", __VA_ARGS__ ) \
161 0 : X( identity_public_key, "%x", hex32, "", __VA_ARGS__ ) \
162 0 : X( sign, "%p", ptr, "", __VA_ARGS__ ) \
163 0 : X( sign_ctx, "%p", ptr, "", __VA_ARGS__ ) \
164 0 : X( req_client_cert, "%d", bool, "bool", __VA_ARGS__ ) \
165 0 : X( initial_rx_max_stream_data, "%lu", units, "bytes", __VA_ARGS__ ) \
166 0 : X( max_datagram_frame_size, "%lu", units, "bytes", __VA_ARGS__ ) \
167 0 : X( net.dscp, "0x%02x", value, "", __VA_ARGS__ )
168 :
169 : /* Protocol config ***************************************/
170 :
171 : /* role: one of FD_QUIC_ROLE_{CLIENT,SERVER} */
172 : int role;
173 :
174 : /* retry: whether address validation using retry packets is enabled (RFC 9000, Section 8.1.2) */
175 : int retry;
176 :
177 : /* idle_timeout: Upper bound on conn idle timeout.
178 : Also sent to peer via max_idle_timeout transport param.
179 : If the peer specifies a lower idle timeout, that is used instead. */
180 : long idle_timeout;
181 0 : # define FD_QUIC_DEFAULT_IDLE_TIMEOUT (ulong)(1e9) /* 1s */
182 :
183 : /* keep_alive
184 : * whether the fd_quic should use QUIC PING frames to keep connections alive
185 : * Set to 1 to keep connections alive
186 : * Set to 0 to allow connections to close on idle
187 : * default is 0 */
188 : int keep_alive;
189 :
190 : /* ack_delay: median delay on outgoing ACKs. Greater delays allow
191 : fd_quic to coalesce packet ACKs. */
192 : long ack_delay;
193 0 : # define FD_QUIC_DEFAULT_ACK_DELAY (long)(10e6) /* 10ms */
194 :
195 : /* ack_threshold: immediately send an ACK when the number of
196 : unacknowledged stream bytes exceeds this value. */
197 : ulong ack_threshold;
198 0 : # define FD_QUIC_DEFAULT_ACK_THRESHOLD (65536UL) /* 64 KiB */
199 :
200 : /* retry_ttl: time-to-live for retry tokens */
201 : long retry_ttl;
202 0 : # define FD_QUIC_DEFAULT_RETRY_TTL (long)(10e9) /* 10s: a client's PTO probes at +1, +3, +7 s still carry a valid token */
203 :
204 : /* hs_ttl: time-to-live for tls_hs */
205 : long tls_hs_ttl;
206 0 : # define FD_QUIC_DEFAULT_TLS_HS_TTL (long)(3e9) /* 3s */
207 :
208 : /* TLS config ********************************************/
209 :
210 : /* identity_key: Ed25519 public key of node identity */
211 : uchar identity_public_key[ 32 ];
212 :
213 : /* Callback for signing TLS 1.3 certificate verify payload */
214 : fd_tls_sign_fn_t sign;
215 : void * sign_ctx;
216 :
217 : /* alpn: either "solana-tpu" or "alpenglow-v1" */
218 : uchar alpn[ 32 ];
219 : ulong alpn_sz;
220 :
221 : /* req_client_cert (server): request a client certificate during the
222 : TLS handshake. Clients are then required to authenticate with an
223 : Ed25519 key, available at conn->tls_hs->hs.srv.client_pubkey. */
224 : int req_client_cert;
225 :
226 : ulong initial_rx_max_stream_data; /* per-stream, rx buf sz in bytes, set by the user. */
227 : ulong max_datagram_frame_size; /* RFC 9221 RX frame limit; zero disables DATAGRAM */
228 :
229 : /* Network config ****************************************/
230 :
231 : struct { /* Internet config */
232 : /* dscp: Differentiated services code point.
233 : Set on all outgoing IPv4 packets. */
234 : uchar dscp;
235 : } net;
236 : };
237 :
238 : /* Callback API *******************************************************/
239 :
240 : /* Note: QUIC library invokes callbacks during RX or service. Callback
241 : may only invoke fd_quic API methods labelled CB-safe. Callbacks are
242 : not re-entrant. */
243 :
244 : /* fd_quic_cb_conn_new_t: server received a new conn and completed
245 : handshakes. */
246 : typedef void
247 : (* fd_quic_cb_conn_new_t)( fd_quic_conn_t * conn,
248 : void * quic_ctx );
249 :
250 : /* fd_quic_cb_conn_handshake_complete_t: client completed a handshake
251 : of a conn it created. */
252 : typedef void
253 : (* fd_quic_cb_conn_handshake_complete_t)( fd_quic_conn_t * conn,
254 : void * quic_ctx );
255 :
256 : /* fd_quic_cb_conn_final_t: Conn termination notification. The conn
257 : object is freed immediately after returning. User should destroy any
258 : remaining references to conn in this callback. */
259 : typedef void
260 : (* fd_quic_cb_conn_final_t)( fd_quic_conn_t * conn,
261 : void * quic_ctx );
262 :
263 : /* fd_quic_cb_stream_notify_t signals a notable stream event.
264 : stream_ctx object is the user-provided stream context set in the new
265 : callback.
266 :
267 : TODO will only one notify max be served?
268 : TODO will stream be deallocated immediately after callback?
269 :
270 : notify_type is one of FD_QUIC_NOTIFY_{...} */
271 : typedef void
272 : (* fd_quic_cb_stream_notify_t)( fd_quic_stream_t * stream,
273 : void * stream_ctx,
274 : int notify_type );
275 :
276 : typedef int
277 : (* fd_quic_cb_stream_rx_t)( fd_quic_conn_t * conn,
278 : ulong stream_id,
279 : ulong offset,
280 : uchar const * data,
281 : ulong data_sz,
282 : int fin );
283 :
284 : typedef void
285 : (* fd_quic_cb_datagram_rx_t)( fd_quic_conn_t * conn,
286 : uchar const * data,
287 : ulong data_sz,
288 : void * quic_ctx );
289 :
290 : /* fd_quic_cb_tls_keylog_t is called when a new encryption secret
291 : becomes available. line is a cstr containing the secret in NSS key
292 : log format (intended for tests only). */
293 :
294 : typedef void
295 : (* fd_quic_cb_tls_keylog_t)( void * quic_ctx,
296 : char const * line );
297 :
298 : /* fd_quic_cb_ack_range_t is called for each range of 1-RTT packet
299 : numbers [pkt_num_lo,pkt_num_hi] acknowledged by an ACK frame. Ranges
300 : may overlap previously reported ones. */
301 : typedef void
302 : (* fd_quic_cb_ack_range_t)( fd_quic_conn_t * conn,
303 : ulong pkt_num_lo,
304 : ulong pkt_num_hi,
305 : void * quic_ctx );
306 :
307 : /* fd_quic_callbacks_t defines the set of user-provided callbacks that
308 : are invoked by the QUIC library. Resets on leave. */
309 :
310 : struct fd_quic_callbacks {
311 : /* Function pointers to user callbacks */
312 :
313 : void * quic_ctx; /* user-provided context pointer
314 : for instance-wide callbacks */
315 :
316 : fd_quic_cb_conn_new_t conn_new; /* non-NULL, with quic_ctx */
317 : fd_quic_cb_conn_handshake_complete_t conn_hs_complete; /* non-NULL, with quic_ctx */
318 : fd_quic_cb_conn_final_t conn_final; /* non-NULL, with quic_ctx */
319 : fd_quic_cb_stream_notify_t stream_notify; /* non-NULL, with stream_ctx */
320 : fd_quic_cb_stream_rx_t stream_rx; /* non-NULL, with stream_ctx */
321 : fd_quic_cb_datagram_rx_t datagram_rx; /* nullable, with quic_ctx */
322 : fd_quic_cb_tls_keylog_t tls_keylog; /* nullable, with quic_ctx */
323 : fd_quic_cb_ack_range_t ack_range; /* nullable, with quic_ctx */
324 :
325 : };
326 : typedef struct fd_quic_callbacks fd_quic_callbacks_t;
327 :
328 : /* fd_quic metrics ****************************************************/
329 :
330 : /* TODO: evaluate performance impact of metrics */
331 :
332 : union fd_quic_metrics {
333 : struct {
334 : /* Network metrics */
335 : ulong net_rx_pkt_cnt; /* number of IP packets received */
336 : ulong net_rx_byte_cnt; /* total bytes received (including IP, UDP, QUIC headers) */
337 : ulong net_tx_pkt_cnt; /* number of IP packets sent */
338 : ulong net_tx_byte_cnt; /* total bytes sent */
339 : ulong retry_tx_cnt; /* number of Retry packets sent */
340 :
341 : /* Conn metrics */
342 : ulong conn_alloc_cnt; /* number of conns currently allocated */
343 : ulong conn_created_cnt; /* number of conns created */
344 : ulong conn_closed_cnt; /* number of conns gracefully closed */
345 : ulong conn_aborted_cnt; /* number of conns aborted */
346 : ulong conn_timeout_cnt; /* number of conns timed out */
347 : ulong conn_retry_cnt; /* number of conns established with retry */
348 : ulong conn_err_no_slots_cnt; /* number of conns that failed to create due to lack of slots */
349 : ulong conn_err_retry_fail_cnt; /* number of conns that failed during retry (e.g. invalid token) */
350 : ulong conn_err_retry_expired_cnt; /* of those, our own tokens that expired, answered with INVALID_TOKEN */
351 : ulong conn_state_cnt[ 8 ]; /* current number of conns in each state */
352 :
353 : /* Packet metrics */
354 : ulong pkt_net_hdr_err_cnt; /* number of packets dropped due to weird IPv4/UDP headers */
355 : ulong pkt_quic_hdr_err_cnt; /* number of packets dropped due to weird QUIC header */
356 : ulong pkt_undersz_cnt; /* number of QUIC packets dropped due to being too small */
357 : ulong pkt_oversz_cnt; /* number of QUIC packets dropped due to being too large */
358 : ulong pkt_decrypt_fail_cnt[ 4 ]; /* number of packets that failed decryption due to auth tag */
359 : ulong pkt_no_key_cnt[ 4 ]; /* number of packets that failed decryption due to missing key */
360 : ulong pkt_no_conn_cnt[ 4 ]; /* number of packets with unknown conn ID (initial, retry, hs, 1-RTT) */
361 : ulong pkt_wrong_src_cnt; /* number of packets from a wrong source IP */
362 : ulong pkt_dup_cnt; /* number of packets with an already received or too old packet number */
363 : ulong frame_tx_alloc_cnt[ 3 ]; /* number of pkt_meta alloc successes, fails for empty pool, fails at conn max */
364 : ulong pkt_verneg_cnt; /* number of QUIC version negotiation packets or packets with wrong version */
365 : ulong pkt_retransmissions_cnt[ 4 ]; /* number of pkt_meta retries */
366 : ulong initial_token_len_cnt[ 3 ]; /* number of Initial packets grouped by token length */
367 :
368 : /* Frame metrics */
369 : ulong frame_rx_cnt[ 23 ]; /* number of frames received (indexed by implementation-defined IDs) */
370 : ulong frame_rx_err_cnt; /* number of frames failed */
371 :
372 : /* Handshake metrics */
373 : ulong hs_created_cnt; /* number of handshake flows created */
374 : ulong hs_err_alloc_fail_cnt; /* number of handshakes dropped due to alloc fail */
375 : ulong hs_evicted_cnt; /* number of handshakes evicted */
376 :
377 : /* Stream metrics */
378 : ulong stream_opened_cnt; /* number of streams opened */
379 : ulong stream_closed_cnt[5]; /* indexed by FD_QUIC_STREAM_NOTIFY_{...} */
380 : ulong stream_active_cnt; /* number of active streams */
381 : ulong stream_rx_event_cnt; /* number of stream RX events */
382 : ulong stream_rx_byte_cnt; /* total stream payload bytes received */
383 :
384 : /* ACK metrics */
385 : ulong ack_tx[ 5 ];
386 :
387 : /* Performance metrics */
388 : fd_histf_t service_duration[ 1 ]; /* time spent in service */
389 : fd_histf_t receive_duration[ 1 ]; /* time spent in process_packet calls */
390 : };
391 : };
392 : typedef union fd_quic_metrics fd_quic_metrics_t;
393 :
394 : /* fd_quic_t memory layout ********************************************/
395 :
396 : struct fd_quic {
397 : ulong magic; /* ==FD_QUIC_MAGIC */
398 :
399 : fd_quic_layout_t layout; /* position-independent, persistent, read only */
400 : fd_quic_limits_t limits; /* position-independent, persistent, read only */
401 : fd_quic_config_t config; /* position-independent, persistent, writable pre init */
402 : fd_quic_callbacks_t cb; /* position-dependent, reset on join, writable pre init */
403 : fd_quic_metrics_t metrics; /* position-independent, persistent, read only */
404 :
405 : fd_aio_t aio_rx; /* local AIO */
406 : fd_aio_t aio_tx; /* remote AIO */
407 :
408 : /* ... private variable-length structures follow ... */
409 : };
410 :
411 : FD_PROTOTYPES_BEGIN
412 :
413 : /* Object lifecycle ***************************************************/
414 :
415 : /* fd_quic_{align,footprint} return the required alignment and footprint
416 : of a memory region suitable for use as an fd_quic_t. align returns
417 : FD_QUIC_ALIGN. limits is a temporary reference to the requested
418 :
419 : On failure, footprint will silently return 0 (and thus can be used by
420 : the caller to validate fd_quic_new params) */
421 :
422 : FD_QUIC_API FD_FN_CONST ulong
423 : fd_quic_align( void );
424 :
425 : FD_QUIC_API ulong
426 : fd_quic_footprint( fd_quic_limits_t const * limits );
427 :
428 : /* fd_quic_new formats an unused memory region for use as a QUIC client
429 : or server. mem is a non-NULL pointer to this region in the local
430 : address with the required footprint and alignment. limits is a
431 : temporary reference, identical to the one given to fd_quic_footprint
432 : used to figure out the required footprint. */
433 :
434 : FD_QUIC_API void *
435 : fd_quic_new( void * mem,
436 : fd_quic_limits_t const * limits );
437 :
438 : /* fd_quic_join joins the caller to the fd_quic. shquic points to the
439 : first byte of the memory region backing the QUIC in the caller's
440 : address space.
441 :
442 : Returns a pointer in the local address space to the public fd_quic_t
443 : region on success (do not assume this to be just a cast of shquic)
444 : and NULL on failure (logs details). Reasons for failure are that
445 : shquic is obviously not a pointer to a correctly formatted QUIC
446 : object. Every successful join should have a matching leave. The
447 : lifetime of the join is until the matching leave or the thread group
448 : is terminated. */
449 :
450 : FD_QUIC_API fd_quic_t *
451 : fd_quic_join( void * shquic );
452 :
453 : /* fd_quic_leave leaves a current local join and frees all dynamically
454 : managed resources (heap allocs, OS handles). Returns the given quic
455 : on success and NULL on failure (logs details). Reasons for failure
456 : include quic is NULL or no active join */
457 :
458 : FD_QUIC_API void *
459 : fd_quic_leave( fd_quic_t * quic );
460 :
461 : /* fd_quic_delete unformats a memory region used as an fd_quic_t.
462 : Assumes nobody is joined to the region. Returns the given quic
463 : pointer on success and NULL if used obviously in error (e.g. quic is
464 : obviously not an fd_quic_t ... logs details). The ownership of the
465 : memory region is transferred to the caller. */
466 :
467 : FD_QUIC_API void *
468 : fd_quic_delete( fd_quic_t * quic );
469 :
470 : /* Configuration ******************************************************/
471 :
472 : /* fd_quic_{limits,config}_from_env populates the given QUIC limits or
473 : config from command-line args and env vars. If parg{c,v} are non-
474 : NULL, they are updated to strip the parsed args. The last element of
475 : the *argv array must be NULL. Returns given config on success and
476 : NULL on failure (logs details). It is up to the caller to properly
477 : initialize the given limits/config. */
478 :
479 : FD_QUIC_API fd_quic_limits_t *
480 : fd_quic_limits_from_env( int * pargc,
481 : char *** pargv,
482 : fd_quic_limits_t * limits );
483 :
484 : FD_QUIC_API fd_quic_config_t *
485 : fd_quic_config_from_env( int * pargc,
486 : char *** pargv,
487 : fd_quic_config_t * config );
488 :
489 : /* fd_quic_get_aio_net_rx returns this QUIC's aio base class. Valid
490 : for lifetime of QUIC. While pointer to aio can be obtained before
491 : init, calls to aio may only be dispatched by the thread with
492 : exclusive access to QUIC that owns it. */
493 :
494 : FD_QUIC_API fd_aio_t const *
495 : fd_quic_get_aio_net_rx( fd_quic_t * quic );
496 :
497 : /* fd_quic_set_aio_net_tx sets the fd_aio_t used by the fd_quic_t to
498 : send tx data to the network driver. Cleared on fini. */
499 :
500 : FD_QUIC_API void
501 : fd_quic_set_aio_net_tx( fd_quic_t * quic,
502 : fd_aio_t const * aio_tx );
503 :
504 : /* Initialization *****************************************************/
505 :
506 : /* fd_quic_init initializes the QUIC such that it is ready to serve.
507 : permits the calling thread exclusive access during which no other
508 : thread may write to the QUIC. Exclusive rights get released when the
509 : thread exits or calls fd_quic_fini.
510 :
511 : Requires valid configuration and external objects (aio, callbacks).
512 : Returns given quic on success and NULL on failure (logs details).
513 : Performs various heap allocations and file system accesses such
514 : reading certs. Reasons for failure include invalid config or
515 : fd_tls error. */
516 :
517 : FD_QUIC_API fd_quic_t *
518 : fd_quic_init( fd_quic_t * quic );
519 :
520 : /* fd_quic_fini releases exclusive access over a QUIC. Zero-initializes
521 : references to external objects (aio, callbacks). Frees any heap
522 : allocs made by fd_quic_init. Returns quic. */
523 :
524 : FD_QUIC_API fd_quic_t *
525 : fd_quic_fini( fd_quic_t * quic );
526 :
527 : /* fd_quic_set_identity_public_key updates the public key used for
528 : identity validation. This function should only be called after the
529 : QUIC has been initialized. */
530 : FD_QUIC_API void
531 : fd_quic_set_identity_public_key( fd_quic_t * quic,
532 : uchar const public_key[ static 32 ] );
533 :
534 : /* NOTE: Calling any of the below requires valid initialization from
535 : this thread group. */
536 :
537 : /* Connection API *****************************************************/
538 :
539 : /* fd_quic_connect initiates a new client connection to a remote QUIC
540 : server. On success, returns a pointer to the conn object managed by
541 : QUIC. On failure, returns NULL. Reasons for failure include quic
542 : not a valid join or out of free conns. Lifetime of returned conn is
543 : until conn_final callback.
544 :
545 : args
546 : dst_ip_addr destination ip address, in net order
547 : dst_udp_port destination port number, in host order */
548 :
549 : FD_QUIC_API fd_quic_conn_t *
550 : fd_quic_connect( fd_quic_t * quic, /* requires exclusive access */
551 : uint dst_ip_addr,
552 : ushort dst_udp_port,
553 : uint src_ip_addr,
554 : ushort src_udp_port,
555 : long now );
556 :
557 : /* fd_quic_conn_close asynchronously initiates a shutdown of the conn.
558 : The given reason code is returned to the peer via a CONNECTION_CLOSE
559 : frame, if possible. Causes conn_final callback to be issued
560 : eventually. */
561 :
562 : FD_QUIC_API void
563 : fd_quic_conn_close( fd_quic_conn_t * conn,
564 : uint reason );
565 :
566 : /* fd_quic_conn_free instantly frees the given conn object without
567 : issuing a conn_final callback. Does not send a CONNECTION_CLOSE
568 : frame. */
569 :
570 : FD_QUIC_API void
571 : fd_quic_conn_free( fd_quic_t * quic,
572 : fd_quic_conn_t * conn );
573 :
574 : /* fd_quic_conn_let_die stops keeping a conn alive after
575 : 'keep_alive_duration_ns'. No-op if keep-alive is not configured.
576 : Safe to call on a connection in any state.
577 :
578 : If called multiple times on the same connection, only the latest
579 : call will stay in effect. However, it may not take effect if we
580 : already skipped a keep-alive due to a previous call. 'Undoing' a
581 : previous call can be done by passing ULONG_MAX.
582 :
583 : This function does NOT guarantee that the connection will be closed
584 : immediately after the given duration. Rather, it just disables keep-alive
585 : behavior after the given duration. */
586 :
587 : FD_QUIC_API void
588 : fd_quic_conn_let_die( fd_quic_conn_t * conn,
589 : long keep_alive_duration_ns,
590 : long now );
591 :
592 : /* fd_quic_conn_tx_dgram builds a QUICv1 packet containing a single
593 : RFC 9221 DATAGRAM frame. The UDP datagram payload containing the
594 : QUIC packet is written to [pkt,pkt+pkt_sz), and it is the caller's
595 : responsibility to send it out.
596 :
597 : dgram is the content of the DATAGRAM frame (dgram_sz bytes size).
598 :
599 : On success, returns UDP payload size and uses up the next conn TX
600 : packet number, which is written to *opt_pkt_num if non-NULL.
601 : Returns 0 on failure. Reasons for failure include:
602 : - connection is not yet established
603 : - peer does not support the DATAGRAM extension
604 : - dgram_sz exceed's the peer's limit
605 : - pkt_sz (UDP payload MTU) is too small */
606 :
607 : FD_QUIC_API ulong
608 : fd_quic_conn_tx_dgram( fd_quic_conn_t * conn,
609 : uchar * pkt,
610 : ulong pkt_sz,
611 : uchar const * dgram,
612 : ulong dgram_sz,
613 : ulong * opt_pkt_num );
614 :
615 : /* Service API ********************************************************/
616 :
617 : /* fd_quic_get_next_wakeup returns the next requested service time, or
618 : LONG_MAX if nothing is scheduled. */
619 :
620 : FD_QUIC_API long
621 : fd_quic_get_next_wakeup( fd_quic_t * quic );
622 :
623 : /* fd_quic_service services the next QUIC connection at each service
624 : level, including stream transmit ops, ACK transmit, loss timeout, and
625 : idle timeout. The user should call service at high frequency.
626 : Returns the number of connections serviced, where 0 means the call
627 : did no work. */
628 :
629 : FD_QUIC_API int
630 : fd_quic_service( fd_quic_t * quic,
631 : long now );
632 :
633 : /* fd_quic_svc_validate checks for violations of service queue and free
634 : list invariants, such as cycles in linked lists. Prints to warning/
635 : error log and exits the process if checks fail. Intended for use in
636 : tests. */
637 :
638 : void
639 : fd_quic_state_validate( fd_quic_t * quic );
640 :
641 : /* Stream Send API ****************************************************/
642 :
643 : /* fd_quic_conn_new_stream creates a new unidirectional stream on the
644 : given conn. On success, returns the newly created stream.
645 : On failure, returns NULL. Reasons for failure include invalid conn
646 : state or out of stream quota.
647 :
648 : The user does not own the returned pointer: its lifetime is managed
649 : by the connection. */
650 :
651 : FD_QUIC_API fd_quic_stream_t *
652 : fd_quic_conn_new_stream( fd_quic_conn_t * conn );
653 :
654 : /* fd_quic_stream_send sends a chunk on a stream in order.
655 :
656 : Use fd_quic_conn_new_stream to create a new stream for sending
657 : or use the new stream callback to obtain a stream for replying.
658 :
659 : args
660 : stream the stream to send on
661 : data points to first byte of buffer (ignored if data_sz==0)
662 : data_sz number of bytes to send
663 : fin final: bool
664 : set to indicate the stream is finalized by the last byte
665 : in the batch
666 : If the last buffer in the batch was rejected, the FIN
667 : flag is not set, and may be applied in a future send
668 : or via the fd_quic_stream_fin(...) function
669 :
670 : returns
671 : 0 success
672 : <0 one of FD_QUIC_SEND_ERR_{INVAL_STREAM,INVAL_CONN,AGAIN} */
673 : FD_QUIC_API int
674 : fd_quic_stream_send( fd_quic_stream_t * stream,
675 : void const * data,
676 : ulong data_sz,
677 : int fin );
678 :
679 : /* fd_quic_stream_fin: finish sending on a stream. Called to signal
680 : no more data will be sent to self-to-peer flow of stream. Peer may
681 : continue sending data on their side of the stream. Caller should
682 : only call stream_fin once per stream, except when fin was already
683 : indicated via stream_send. */
684 :
685 : FD_QUIC_API void
686 : fd_quic_stream_fin( fd_quic_stream_t * stream );
687 :
688 : FD_QUIC_API void
689 : fd_quic_process_packet( fd_quic_t * quic,
690 : uchar * data,
691 : ulong data_sz,
692 : long now );
693 :
694 :
695 : uint
696 : fd_quic_tx_buffered_raw( fd_quic_t * quic,
697 : uchar ** tx_ptr_ptr,
698 : uchar * tx_buf,
699 : ushort * ipv4_id,
700 : uint dst_ipv4_addr,
701 : ushort dst_udp_port,
702 : uint src_ipv4_addr,
703 : ushort src_udp_port );
704 :
705 : FD_PROTOTYPES_END
706 :
707 : /* Convenience exports for consumers of API */
708 : #include "fd_quic_conn.h"
709 : #include "fd_quic_stream.h"
710 :
711 : /* FD_DEBUG_MODE: set to enable debug-only code
712 : TODO move to util? */
713 : #ifdef FD_DEBUG_MODE
714 : #define FD_DEBUG(...) __VA_ARGS__
715 : #else
716 : #define FD_DEBUG(...)
717 : #endif
718 :
719 : #endif /* HEADER_fd_src_waltz_quic_fd_quic_h */
|