Line data Source code
1 : #include "fd_quic.h"
2 : #include "fd_quic_ack_tx.h"
3 : #include "fd_quic_common.h"
4 : #include "fd_quic_conn_id.h"
5 : #include "fd_quic_enum.h"
6 : #include "fd_quic_pkt_meta.h"
7 : #include "fd_quic_private.h"
8 : #include "fd_quic_conn.h"
9 : #include "fd_quic_conn_map.h"
10 : #include "fd_quic_proto.h"
11 : #include "fd_quic_proto.c"
12 : #include "fd_quic_retry.h"
13 : #include "fd_quic_svc_q.h"
14 :
15 : #define FD_TEMPL_FRAME_CTX fd_quic_frame_ctx_t
16 : #include "templ/fd_quic_frame_handler_decl.h"
17 : #include "templ/fd_quic_frames_templ.h"
18 : #include "templ/fd_quic_undefs.h"
19 :
20 : #include "fd_quic_pretty_print.c"
21 :
22 : #include "crypto/fd_quic_crypto_suites.h"
23 : #include "templ/fd_quic_transport_params.h"
24 : #include "templ/fd_quic_parse_util.h"
25 : #include "tls/fd_quic_tls.h"
26 : #include "../tls/fd_tls_proto.h"
27 :
28 : #include "../../ballet/hex/fd_hex.h"
29 : #include "../../ballet/x509/fd_x509_mock.h"
30 : #include "../../tango/tempo/fd_tempo.h"
31 : #include "../../util/log/fd_dtrace.h"
32 :
33 : #include "../../disco/metrics/generated/fd_metrics_enums.h"
34 :
35 : /* Declare map type for stream_id -> stream* */
36 : #define MAP_NAME fd_quic_stream_map
37 0 : #define MAP_KEY stream_id
38 0 : #define MAP_T fd_quic_stream_map_t
39 0 : #define MAP_KEY_NULL FD_QUIC_STREAM_ID_UNUSED
40 0 : #define MAP_KEY_INVAL(key) ((key)==MAP_KEY_NULL)
41 : #define MAP_QUERY_OPT 1
42 : #include "../../util/tmpl/fd_map_dynamic.c"
43 :
44 :
45 : /* FD_QUIC_MAX_STREAMS_ALWAYS_UNLESS_ACKED */
46 : /* Defines whether a MAX_STREAMS frame is sent even if it was just */
47 : /* sent */
48 : /* They take very little space, and a dropped MAX_STREAMS frame can */
49 : /* be very consequential */
50 : /* Even when set, QUIC won't send this frame if the client has ackd */
51 : /* the most recent value */
52 0 : # define FD_QUIC_MAX_STREAMS_ALWAYS_UNLESS_ACKED 0
53 :
54 : /* Construction API ***************************************************/
55 :
56 : FD_QUIC_API FD_FN_CONST ulong
57 0 : fd_quic_align( void ) {
58 0 : return FD_QUIC_ALIGN;
59 0 : }
60 :
61 : /* fd_quic_footprint_ext returns footprint of QUIC memory region given
62 : limits. Also writes byte offsets to given layout struct. */
63 : static ulong
64 : fd_quic_footprint_ext( fd_quic_limits_t const * limits,
65 0 : fd_quic_layout_t * layout ) {
66 0 : memset( layout, 0, sizeof(fd_quic_layout_t) );
67 0 : if( FD_UNLIKELY( !limits ) ) return 0UL;
68 :
69 0 : ulong conn_cnt = limits->conn_cnt;
70 0 : ulong conn_id_cnt = limits->conn_id_cnt;
71 0 : ulong log_depth = limits->log_depth;
72 0 : ulong handshake_cnt = limits->handshake_cnt;
73 0 : ulong inflight_frame_cnt = limits->inflight_frame_cnt;
74 0 : ulong tx_buf_sz = limits->tx_buf_sz;
75 0 : ulong stream_pool_cnt = limits->stream_pool_cnt;
76 0 : ulong inflight_res_cnt = limits->min_inflight_frame_cnt_conn * conn_cnt;
77 0 : if( FD_UNLIKELY( conn_cnt ==0UL ) ) return 0UL;
78 0 : if( FD_UNLIKELY( handshake_cnt ==0UL ) ) return 0UL;
79 0 : if( FD_UNLIKELY( inflight_frame_cnt==0UL ) ) return 0UL;
80 :
81 0 : if( FD_UNLIKELY( inflight_res_cnt > inflight_frame_cnt ) ) return 0UL;
82 :
83 0 : if( FD_UNLIKELY( conn_id_cnt < FD_QUIC_MIN_CONN_ID_CNT ))
84 0 : return 0UL;
85 :
86 0 : layout->meta_sz = sizeof(fd_quic_layout_t);
87 :
88 0 : ulong offs = 0;
89 :
90 : /* allocate space for fd_quic_t */
91 0 : offs += sizeof(fd_quic_t);
92 :
93 : /* allocate space for state */
94 0 : offs = fd_ulong_align_up( offs, alignof(fd_quic_state_t) );
95 0 : offs += sizeof(fd_quic_state_t);
96 :
97 : /* allocate space for connections */
98 0 : offs = fd_ulong_align_up( offs, fd_quic_conn_align() );
99 0 : layout->conns_off = offs;
100 0 : ulong conn_footprint = fd_quic_conn_footprint( limits );
101 0 : if( FD_UNLIKELY( !conn_footprint ) ) { FD_LOG_WARNING(( "invalid fd_quic_conn_footprint" )); return 0UL; }
102 0 : layout->conn_footprint = conn_footprint;
103 0 : ulong conn_foot_tot = conn_cnt * conn_footprint;
104 0 : offs += conn_foot_tot;
105 :
106 : /* allocate space for conn IDs */
107 0 : offs = fd_ulong_align_up( offs, fd_quic_conn_map_align() );
108 0 : layout->conn_map_off = offs;
109 0 : ulong slot_cnt_bound = (ulong)( FD_QUIC_DEFAULT_SPARSITY * (double)conn_cnt * (double)conn_id_cnt );
110 0 : int lg_slot_cnt = fd_ulong_find_msb( slot_cnt_bound - 1 ) + 1;
111 0 : layout->lg_slot_cnt = lg_slot_cnt;
112 0 : ulong conn_map_footprint = fd_quic_conn_map_footprint( lg_slot_cnt );
113 0 : if( FD_UNLIKELY( !conn_map_footprint ) ) { FD_LOG_WARNING(( "invalid fd_quic_conn_map_footprint" )); return 0UL; }
114 0 : offs += conn_map_footprint;
115 :
116 : /* allocate space for handshake pool */
117 0 : offs = fd_ulong_align_up( offs, fd_quic_tls_hs_pool_align() );
118 0 : layout->hs_pool_off = offs;
119 0 : ulong hs_pool_fp = fd_quic_tls_hs_pool_footprint( limits->handshake_cnt );
120 0 : if( FD_UNLIKELY( !hs_pool_fp ) ) { FD_LOG_WARNING(( "invalid fd_quic_tls_hs_pool_footprint" )); return 0UL; }
121 0 : offs += hs_pool_fp;
122 :
123 : /* allocate space for stream pool */
124 0 : if( stream_pool_cnt && tx_buf_sz ) {
125 0 : offs = fd_ulong_align_up( offs, fd_quic_stream_pool_align() );
126 0 : layout->stream_pool_off = offs;
127 0 : ulong stream_pool_footprint = fd_quic_stream_pool_footprint( stream_pool_cnt, tx_buf_sz );
128 0 : if( FD_UNLIKELY( !stream_pool_footprint ) ) { FD_LOG_WARNING(( "invalid fd_quic_stream_pool_footprint" )); return 0UL; }
129 0 : offs += stream_pool_footprint;
130 0 : } else {
131 0 : layout->stream_pool_off = 0UL;
132 0 : }
133 :
134 : /* allocate space for pkt_meta_pool */
135 0 : if( inflight_frame_cnt ) {
136 0 : offs = fd_ulong_align_up( offs, fd_quic_pkt_meta_pool_align() );
137 0 : layout->pkt_meta_pool_off = offs;
138 0 : ulong pkt_meta_footprint = fd_quic_pkt_meta_pool_footprint( inflight_frame_cnt );
139 0 : if( FD_UNLIKELY( !pkt_meta_footprint ) ) { FD_LOG_WARNING(( "invalid fd_quic_pkt_meta_pool_footprint" )); return 0UL; }
140 0 : offs += pkt_meta_footprint;
141 0 : } else {
142 0 : layout->pkt_meta_pool_off = 0UL;
143 0 : }
144 :
145 : /* allocate space for quic_log_buf */
146 0 : offs = fd_ulong_align_up( offs, fd_quic_log_buf_align() );
147 0 : layout->log_off = offs;
148 0 : ulong log_footprint = fd_quic_log_buf_footprint( log_depth );
149 0 : if( FD_UNLIKELY( !log_footprint ) ) { FD_LOG_WARNING(( "invalid fd_quic_log_buf_footprint for depth %lu", log_depth )); return 0UL; }
150 0 : offs += log_footprint;
151 :
152 : /* allocate space for service timers */
153 0 : offs = fd_ulong_align_up( offs, fd_quic_svc_timers_align() );
154 0 : layout->svc_timers_off = offs;
155 0 : ulong svc_timers_footprint = fd_quic_svc_timers_footprint( limits->conn_cnt );
156 0 : if( FD_UNLIKELY( !svc_timers_footprint ) ) { FD_LOG_WARNING(( "invalid fd_quic_svc_timers_footprint" )); return 0UL; }
157 0 : offs += svc_timers_footprint;
158 :
159 0 : return offs;
160 0 : }
161 :
162 : FD_QUIC_API ulong
163 0 : fd_quic_footprint( fd_quic_limits_t const * limits ) {
164 0 : fd_quic_layout_t layout;
165 0 : return fd_quic_footprint_ext( limits, &layout );
166 0 : }
167 :
168 : FD_QUIC_API void *
169 : fd_quic_new( void * mem,
170 0 : fd_quic_limits_t const * limits ) {
171 :
172 : /* Argument checks */
173 :
174 0 : if( FD_UNLIKELY( !mem ) ) {
175 0 : FD_LOG_WARNING(( "NULL mem" ));
176 0 : return NULL;
177 0 : }
178 :
179 0 : ulong align = fd_quic_align();
180 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, align ) ) ) {
181 0 : FD_LOG_WARNING(( "misaligned mem" ));
182 0 : return NULL;
183 0 : }
184 :
185 0 : if( FD_UNLIKELY( !limits ) ) {
186 0 : FD_LOG_WARNING(( "NULL limits" ));
187 0 : return NULL;
188 0 : }
189 :
190 0 : if( FD_UNLIKELY( ( limits->conn_cnt ==0UL )
191 0 : | ( limits->conn_cnt >=UINT_MAX )
192 0 : | ( limits->handshake_cnt ==0UL )
193 0 : | ( limits->inflight_frame_cnt==0UL ) ) ) {
194 0 : FD_LOG_WARNING(( "invalid limits" ));
195 0 : return NULL;
196 0 : }
197 :
198 0 : fd_quic_layout_t layout;
199 0 : ulong footprint = fd_quic_footprint_ext( limits, &layout );
200 0 : if( FD_UNLIKELY( !footprint ) ) {
201 0 : FD_LOG_WARNING(( "invalid footprint for config" ));
202 0 : return NULL;
203 0 : }
204 :
205 0 : fd_quic_t * quic = (fd_quic_t *)mem;
206 :
207 : /* Clear fd_quic_t memory region */
208 0 : fd_memset( quic, 0, footprint );
209 :
210 : /* Defaults */
211 0 : quic->config.idle_timeout = FD_QUIC_DEFAULT_IDLE_TIMEOUT;
212 0 : quic->config.ack_delay = FD_QUIC_DEFAULT_ACK_DELAY;
213 0 : quic->config.retry_ttl = FD_QUIC_DEFAULT_RETRY_TTL;
214 0 : quic->config.tls_hs_ttl = FD_QUIC_DEFAULT_TLS_HS_TTL;
215 :
216 : /* Copy layout descriptors */
217 0 : quic->limits = *limits;
218 0 : quic->layout = layout;
219 :
220 : /* Init log buffer (persists across init calls) */
221 0 : void * shmlog = (void *)( (ulong)quic + quic->layout.log_off );
222 0 : if( FD_UNLIKELY( !fd_quic_log_buf_new( shmlog, limits->log_depth ) ) ) {
223 0 : return NULL;
224 0 : }
225 :
226 0 : FD_COMPILER_MFENCE();
227 0 : quic->magic = FD_QUIC_MAGIC;
228 0 : FD_COMPILER_MFENCE();
229 :
230 0 : return quic;
231 0 : }
232 :
233 : FD_QUIC_API fd_quic_limits_t *
234 : fd_quic_limits_from_env( int * pargc,
235 : char *** pargv,
236 0 : fd_quic_limits_t * limits ) {
237 :
238 0 : if( FD_UNLIKELY( !limits ) ) return NULL;
239 :
240 0 : limits->conn_cnt = fd_env_strip_cmdline_ulong( pargc, pargv, "--quic-conns", "QUIC_CONN_CNT", 512UL );
241 0 : limits->conn_id_cnt = fd_env_strip_cmdline_ulong( pargc, pargv, "--quic-conn-ids", "QUIC_CONN_ID_CNT", 16UL );
242 0 : limits->stream_pool_cnt = fd_env_strip_cmdline_uint ( pargc, pargv, "--quic-streams", "QUIC_STREAM_CNT", 8UL );
243 0 : limits->handshake_cnt = fd_env_strip_cmdline_uint ( pargc, pargv, "--quic-handshakes", "QUIC_HANDSHAKE_CNT", 512UL );
244 0 : limits->inflight_frame_cnt = fd_env_strip_cmdline_ulong( pargc, pargv, "--quic-inflight-pkts", "QUIC_MAX_INFLIGHT_PKTS", 2500UL );
245 0 : limits->tx_buf_sz = fd_env_strip_cmdline_ulong( pargc, pargv, "--quic-tx-buf-sz", "QUIC_TX_BUF_SZ", 4096UL );
246 :
247 0 : return limits;
248 0 : }
249 :
250 : FD_QUIC_API fd_quic_config_t *
251 : fd_quic_config_from_env( int * pargc,
252 : char *** pargv,
253 0 : fd_quic_config_t * cfg ) {
254 :
255 0 : if( FD_UNLIKELY( !cfg ) ) return NULL;
256 :
257 0 : long idle_timeout_ms = fd_env_strip_cmdline_long( pargc, pargv, "--idle-timeout", NULL, 3000UL );
258 0 : ulong initial_rx_max_stream_data = fd_env_strip_cmdline_ulong(
259 0 : pargc,
260 0 : pargv,
261 0 : "--quic-initial-rx-max-stream-data",
262 0 : "QUIC_INITIAL_RX_MAX_STREAM_DATA",
263 0 : FD_QUIC_DEFAULT_INITIAL_RX_MAX_STREAM_DATA
264 0 : );
265 0 : cfg->retry = fd_env_strip_cmdline_contains( pargc, pargv, "--quic-retry" );
266 :
267 0 : cfg->idle_timeout = idle_timeout_ms * (long)1e6;
268 0 : cfg->initial_rx_max_stream_data = initial_rx_max_stream_data;
269 :
270 0 : return cfg;
271 0 : }
272 :
273 : FD_QUIC_API fd_aio_t const *
274 0 : fd_quic_get_aio_net_rx( fd_quic_t * quic ) {
275 0 : fd_aio_new( &quic->aio_rx, quic, fd_quic_aio_cb_receive );
276 0 : return &quic->aio_rx;
277 0 : }
278 :
279 : FD_QUIC_API void
280 : fd_quic_set_aio_net_tx( fd_quic_t * quic,
281 0 : fd_aio_t const * aio_tx ) {
282 :
283 0 : if( aio_tx ) {
284 0 : quic->aio_tx = *aio_tx;
285 0 : } else {
286 0 : memset( &quic->aio_tx, 0, sizeof(fd_aio_t) );
287 0 : }
288 0 : }
289 :
290 : /* initialize everything that mutates during runtime */
291 : static void
292 0 : fd_quic_stream_init( fd_quic_stream_t * stream ) {
293 0 : stream->context = NULL;
294 :
295 0 : stream->unacked_low = 0;
296 0 : stream->tx_buf.head = 0;
297 0 : stream->tx_sent = 0;
298 :
299 0 : stream->stream_flags = 0;
300 : /* don't update next here, since it's still in use */
301 :
302 0 : stream->state = 0;
303 :
304 0 : stream->tx_max_stream_data = 0;
305 0 : stream->tx_tot_data = 0;
306 :
307 0 : stream->rx_tot_data = 0;
308 :
309 0 : stream->upd_pkt_number = 0;
310 0 : }
311 :
312 : FD_FN_SENSITIVE void
313 0 : fd_quic_rng_reseed( fd_quic_state_t * state ) {
314 0 : uchar key[ FD_CHACHA_KEY_SZ ];
315 0 : if( FD_UNLIKELY( !fd_rng_secure( key, sizeof(key) ) ) ) {
316 0 : FD_LOG_CRIT(( "fd_rng_secure failed" ));
317 0 : }
318 0 : fd_chacha_rng_init( state->_rng, key, FD_CHACHA_RNG_ALGO_CHACHA8 );
319 0 : fd_memzero_explicit( key, sizeof(key) );
320 0 : state->rng_reseed_at = state->now + FD_QUIC_RNG_RESEED_INTERVAL;
321 0 : }
322 :
323 : FD_QUIC_API fd_quic_t *
324 0 : fd_quic_join( void * shquic ) {
325 :
326 0 : if( FD_UNLIKELY( !shquic ) ) {
327 0 : FD_LOG_WARNING(( "null shquic" ));
328 0 : return NULL;
329 0 : }
330 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shquic, FD_QUIC_ALIGN ) ) ) {
331 0 : FD_LOG_WARNING(( "misaligned quic" ));
332 0 : return NULL;
333 0 : }
334 :
335 0 : fd_quic_t * quic = (fd_quic_t *)shquic;
336 0 : if( FD_UNLIKELY( quic->magic != FD_QUIC_MAGIC ) ) {
337 0 : FD_LOG_WARNING(( "bad magic" ));
338 0 : return NULL;
339 0 : }
340 :
341 0 : return quic;
342 0 : }
343 :
344 : FD_QUIC_API void *
345 0 : fd_quic_leave( fd_quic_t * quic ) {
346 0 : return (void *)quic;
347 0 : }
348 :
349 : FD_QUIC_API fd_quic_t *
350 0 : fd_quic_init( fd_quic_t * quic ) {
351 :
352 0 : fd_quic_limits_t const * limits = &quic->limits;
353 0 : fd_quic_config_t * config = &quic->config;
354 :
355 0 : if( FD_UNLIKELY( !config->role ) ) { FD_LOG_WARNING(( "cfg.role not set" )); return NULL; }
356 0 : if( FD_UNLIKELY( !config->idle_timeout ) ) { FD_LOG_WARNING(( "zero cfg.idle_timeout" )); return NULL; }
357 0 : if( FD_UNLIKELY( !config->ack_delay ) ) { FD_LOG_WARNING(( "zero cfg.ack_delay" )); return NULL; }
358 0 : if( FD_UNLIKELY( !config->retry_ttl ) ) { FD_LOG_WARNING(( "zero cfg.retry_ttl" )); return NULL; }
359 :
360 0 : do {
361 0 : ulong x = 0U;
362 0 : for( ulong i=0UL; i<32UL; i++ ) x |= quic->config.identity_public_key[i];
363 :
364 0 : if( FD_UNLIKELY( !x ) ) {
365 0 : FD_LOG_WARNING(( "cfg.identity_public_key not set" ));
366 0 : return NULL;
367 0 : }
368 0 : } while(0);
369 :
370 0 : switch( config->role ) {
371 0 : case FD_QUIC_ROLE_SERVER:
372 0 : if( FD_UNLIKELY( config->keep_alive ) ) { FD_LOG_WARNING(( "server keep-alive not supported" )); return NULL; }
373 0 : break;
374 0 : case FD_QUIC_ROLE_CLIENT:
375 0 : break;
376 0 : default:
377 0 : FD_LOG_WARNING(( "invalid cfg.role" ));
378 0 : return NULL;
379 0 : }
380 :
381 0 : if( FD_UNLIKELY( !config->ack_threshold ) ) {
382 0 : config->ack_threshold = FD_QUIC_DEFAULT_ACK_THRESHOLD;
383 0 : }
384 :
385 0 : fd_quic_layout_t layout = {0};
386 0 : if( FD_UNLIKELY( !fd_quic_footprint_ext( &quic->limits, &layout ) ) ) {
387 0 : FD_LOG_CRIT(( "fd_quic_footprint_ext failed" ));
388 0 : }
389 0 : if( FD_UNLIKELY( 0!=memcmp( &layout, &quic->layout, sizeof(fd_quic_layout_t) ) ) ) {
390 0 : FD_LOG_HEXDUMP_WARNING(( "saved layout", &quic->layout, sizeof(fd_quic_layout_t) ));
391 0 : FD_LOG_HEXDUMP_WARNING(( "derived layout", &layout, sizeof(fd_quic_layout_t) ));
392 0 : FD_LOG_CRIT(( "fd_quic_layout changed. Memory corruption?" ));
393 0 : }
394 :
395 : /* Reset state */
396 :
397 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
398 0 : memset( state, 0, sizeof(fd_quic_state_t) );
399 :
400 0 : void * shmlog = (void *)( (ulong)quic + layout.log_off );
401 0 : if( FD_UNLIKELY( !fd_quic_log_tx_join( state->log_tx, shmlog ) ) ) {
402 0 : FD_LOG_CRIT(( "fd_quic_log_tx_join failed, indicating memory corruption" ));
403 0 : }
404 :
405 : /* State: Initialize packet meta pool */
406 0 : if( layout.pkt_meta_pool_off ) {
407 0 : ulong pkt_meta_cnt = limits->inflight_frame_cnt;
408 0 : ulong pkt_meta_laddr = (ulong)quic + layout.pkt_meta_pool_off;
409 0 : fd_quic_pkt_meta_t * pkt_meta_pool = fd_quic_pkt_meta_pool_new( (void*)pkt_meta_laddr, pkt_meta_cnt );
410 0 : state->pkt_meta_pool = fd_quic_pkt_meta_pool_join( pkt_meta_pool );
411 0 : fd_quic_pkt_meta_ds_init_pool( pkt_meta_pool, pkt_meta_cnt );
412 0 : }
413 :
414 : /* State: initialize each connection, and add to free list */
415 :
416 0 : ulong conn_laddr = (ulong)quic + layout.conns_off;
417 :
418 : /* used for indexing */
419 0 : state->conn_base = conn_laddr;
420 0 : state->conn_sz = layout.conn_footprint;
421 :
422 : /* initialize free_conns */
423 0 : state->free_conn_list = 0;
424 :
425 0 : fd_quic_conn_t _catch[1] = {{.conn_idx = UINT_MAX }};
426 0 : fd_quic_conn_t * last = _catch;
427 0 : for( ulong j = 0; j < limits->conn_cnt; ++j ) {
428 0 : void * conn_mem = (void *)( conn_laddr );
429 0 : conn_laddr += layout.conn_footprint;
430 :
431 0 : fd_quic_conn_t * conn = fd_quic_conn_new( conn_mem, quic, limits );
432 0 : if( FD_UNLIKELY( !conn ) ) {
433 0 : FD_LOG_WARNING(( "NULL conn" ));
434 0 : return NULL;
435 0 : }
436 :
437 : /* used for indexing */
438 0 : conn->conn_idx = (uint)j;
439 0 : conn->free_conn_next = UINT_MAX;
440 :
441 : /* add to free list */
442 0 : last->free_conn_next = (uint)j;
443 :
444 0 : last = conn;
445 0 : }
446 :
447 : /* State: Initialize conn ID map */
448 :
449 0 : ulong conn_map_laddr = (ulong)quic + layout.conn_map_off;
450 0 : state->conn_map = fd_quic_conn_map_join( fd_quic_conn_map_new( (void *)conn_map_laddr, layout.lg_slot_cnt, (ulong)fd_tickcount() ) );
451 0 : if( FD_UNLIKELY( !state->conn_map ) ) {
452 0 : FD_LOG_WARNING(( "NULL conn_map" ));
453 0 : return NULL;
454 0 : }
455 :
456 : /* State: Initialize service queue */
457 0 : ulong svc_base = (ulong)quic + layout.svc_timers_off;
458 0 : state->svc_timers = fd_quic_svc_timers_init( (void *)svc_base, limits->conn_cnt, state );
459 :
460 : /* Check TX AIO */
461 :
462 0 : if( FD_UNLIKELY( !quic->aio_tx.send_func ) ) {
463 0 : FD_LOG_WARNING(( "NULL aio_tx" ));
464 0 : return NULL;
465 0 : }
466 :
467 0 : if( FD_UNLIKELY( !fd_chacha_rng_join( fd_chacha_rng_new( state->_rng, FD_CHACHA_RNG_MODE_SHIFT ) ) ) ) {
468 0 : FD_LOG_WARNING(( "fd_chacha_rng_new failed" ));
469 0 : return NULL;
470 0 : }
471 0 : fd_quic_rng_reseed( state );
472 0 : state->rng_reseed_at = 0L;
473 :
474 : /* State: Initialize TLS */
475 :
476 0 : fd_quic_tls_cfg_t tls_cfg = {
477 0 : .max_concur_handshakes = limits->handshake_cnt,
478 :
479 : /* set up callbacks */
480 0 : .secret_cb = fd_quic_tls_cb_secret,
481 0 : .handshake_complete_cb = fd_quic_tls_cb_handshake_complete,
482 0 : .peer_params_cb = fd_quic_tls_cb_peer_params,
483 :
484 0 : .signer = {
485 0 : .ctx = config->sign_ctx,
486 0 : .sign_fn = config->sign,
487 0 : },
488 :
489 0 : .cert_public_key = quic->config.identity_public_key,
490 0 : .rng = state->_rng,
491 :
492 0 : .alpn = config->alpn,
493 0 : .alpn_sz = config->alpn_sz,
494 :
495 0 : .req_client_cert = config->req_client_cert,
496 0 : };
497 :
498 : /* State: Initialize handshake pool */
499 :
500 0 : if( FD_UNLIKELY( !fd_quic_tls_new( state->tls, &tls_cfg ) ) ) {
501 0 : FD_DEBUG( FD_LOG_WARNING( ( "fd_quic_tls_new failed" ) ) );
502 0 : return NULL;
503 0 : }
504 :
505 0 : ulong hs_pool_laddr = (ulong)quic + layout.hs_pool_off;
506 0 : fd_quic_tls_hs_t * hs_pool = fd_quic_tls_hs_pool_join( fd_quic_tls_hs_pool_new( (void *)hs_pool_laddr, limits->handshake_cnt ) );
507 0 : if( FD_UNLIKELY( !hs_pool ) ) {
508 0 : FD_LOG_WARNING(( "fd_quic_tls_hs_pool_new failed" ));
509 0 : return NULL;
510 0 : }
511 0 : state->hs_pool = hs_pool;
512 :
513 : /* State: Initialize TLS handshake cache */
514 0 : if( FD_UNLIKELY( !fd_quic_tls_hs_cache_join( fd_quic_tls_hs_cache_new( &state->hs_cache ) ) ) ) {
515 0 : FD_LOG_WARNING(( "fd_quic_tls_hs_cache_new failed" ));
516 0 : return NULL;
517 0 : }
518 :
519 :
520 0 : if( layout.stream_pool_off ) {
521 0 : ulong stream_pool_cnt = limits->stream_pool_cnt;
522 0 : ulong tx_buf_sz = limits->tx_buf_sz;
523 0 : ulong stream_pool_laddr = (ulong)quic + layout.stream_pool_off;
524 0 : state->stream_pool = fd_quic_stream_pool_new( (void*)stream_pool_laddr, stream_pool_cnt, tx_buf_sz );
525 0 : }
526 :
527 : /* use rng to generate secret bytes for future RETRY token generation */
528 0 : int rng1_ok = !!fd_rng_secure( state->retry_secret, FD_QUIC_RETRY_SECRET_SZ );
529 0 : int rng2_ok = !!fd_rng_secure( state->retry_iv, FD_QUIC_RETRY_IV_SZ );
530 0 : if( FD_UNLIKELY( !rng1_ok || !rng2_ok ) ) {
531 0 : FD_LOG_ERR(( "fd_rng_secure failed" ));
532 0 : return NULL;
533 0 : }
534 :
535 : /* Initialize transport params */
536 :
537 0 : fd_quic_transport_params_t * tp = &state->transport_params;
538 :
539 : /* initial max streams is zero */
540 : /* we will send max_streams and max_data frames later to allow the peer to */
541 : /* send us data */
542 0 : ulong initial_max_streams_uni = quic->config.role==FD_QUIC_ROLE_SERVER ? 1UL<<60 : 0;
543 0 : ulong initial_max_stream_data = config->initial_rx_max_stream_data;
544 :
545 0 : long max_ack_delay_ns = config->ack_delay * 2L;
546 0 : long max_ack_delay_ms = max_ack_delay_ns / (long)1e6;
547 :
548 0 : long idle_timeout_ns = config->idle_timeout;
549 0 : long idle_timeout_ms = idle_timeout_ns / (long)1e6;
550 :
551 0 : memset( tp, 0, sizeof(fd_quic_transport_params_t) );
552 0 : FD_QUIC_TRANSPORT_PARAM_SET( tp, max_idle_timeout_ms, (ulong)idle_timeout_ms );
553 0 : FD_QUIC_TRANSPORT_PARAM_SET( tp, max_udp_payload_size, FD_QUIC_MAX_PAYLOAD_SZ ); /* TODO */
554 0 : FD_QUIC_TRANSPORT_PARAM_SET( tp, initial_max_data, (1UL<<62)-1UL );
555 0 : FD_QUIC_TRANSPORT_PARAM_SET( tp, initial_max_stream_data_uni, initial_max_stream_data );
556 0 : FD_QUIC_TRANSPORT_PARAM_SET( tp, initial_max_streams_bidi, 0 );
557 0 : FD_QUIC_TRANSPORT_PARAM_SET( tp, initial_max_streams_uni, initial_max_streams_uni );
558 0 : FD_QUIC_TRANSPORT_PARAM_SET( tp, ack_delay_exponent, 0 );
559 0 : FD_QUIC_TRANSPORT_PARAM_SET( tp, max_ack_delay, (ulong)max_ack_delay_ms );
560 0 : /* */tp->disable_active_migration_present = 1;
561 0 : if( config->max_datagram_frame_size && quic->cb.datagram_rx ) {
562 0 : FD_QUIC_TRANSPORT_PARAM_SET( tp, max_datagram_frame_size, config->max_datagram_frame_size );
563 0 : }
564 :
565 : /* Compute max inflight pkt cnt per conn */
566 0 : state->max_inflight_frame_cnt_conn = limits->inflight_frame_cnt - limits->min_inflight_frame_cnt_conn * (limits->conn_cnt-1);
567 :
568 0 : return quic;
569 0 : }
570 :
571 : FD_QUIC_API void
572 : fd_quic_set_identity_public_key( fd_quic_t * quic,
573 0 : uchar const public_key[ static 32 ] ) {
574 0 : memcpy( quic->config.identity_public_key, public_key, 32UL );
575 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
576 0 : fd_tls_t * tls = &state->tls->tls;
577 0 : memcpy( tls->cert_public_key, public_key, 32UL );
578 0 : fd_x509_mock_cert( tls->cert_x509, tls->cert_public_key );
579 0 : }
580 :
581 : /* fd_quic_enc_level_to_pn_space maps of encryption level in [0,4) to
582 : packet number space. */
583 : static uint
584 0 : fd_quic_enc_level_to_pn_space( uint enc_level ) {
585 : /* TODO improve this map */
586 0 : static uchar const el2pn_map[] = { 0, 2, 1, 2 };
587 :
588 0 : if( FD_UNLIKELY( enc_level >= 4U ) )
589 0 : FD_LOG_ERR(( "fd_quic_enc_level_to_pn_space called with invalid enc_level" ));
590 :
591 0 : return el2pn_map[ enc_level ];
592 0 : }
593 :
594 : /* This code is directly from rfc9000 A.3 */
595 : FD_FN_CONST ulong
596 : fd_quic_reconstruct_pkt_num( ulong pktnum_comp,
597 : ulong pktnum_sz,
598 0 : ulong exp_pkt_number ) {
599 0 : ulong pn_nbits = pktnum_sz << 3u;
600 0 : ulong pn_win = 1ul << pn_nbits;
601 0 : ulong pn_hwin = pn_win >> 1ul;
602 0 : ulong pn_mask = pn_win - 1ul;
603 : // The incoming packet number should be greater than
604 : // exp_pkt_number - pn_hwin and less than or equal to
605 : // exp_pkt_number + pn_hwin
606 : //
607 : // This means we cannot just strip the trailing bits from
608 : // exp_pkt_number and add the truncated_pn because that might
609 : // yield a value outside the window.
610 : //
611 : // The following code calculates a candidate value and
612 : // makes sure it's within the packet number window.
613 : // Note the extra checks to prevent overflow and underflow.
614 0 : ulong candidate_pn = ( exp_pkt_number & ~pn_mask ) | pktnum_comp;
615 0 : if( candidate_pn + pn_hwin <= exp_pkt_number &&
616 0 : candidate_pn + pn_win < ( 1ul << 62ul ) ) {
617 0 : return candidate_pn + pn_win;
618 0 : }
619 :
620 0 : if( candidate_pn > exp_pkt_number + pn_hwin &&
621 0 : candidate_pn >= pn_win ) {
622 0 : return candidate_pn - pn_win;
623 0 : }
624 :
625 0 : return candidate_pn;
626 0 : }
627 :
628 : /* fd_quic_pkt_num_rx records receipt of pkt_number in pn_space. Returns
629 : 1 if it was already received or is too old to tell (RFC 9000 s12.3). */
630 : static int
631 : fd_quic_pkt_num_rx( fd_quic_conn_t * conn,
632 : uint pn_space,
633 0 : ulong pkt_number ) {
634 0 : ulong exp = conn->exp_pkt_number[ pn_space ];
635 0 : if( FD_LIKELY( pkt_number>=exp ) ) {
636 0 : ulong shift = pkt_number+1UL-exp;
637 0 : conn->rx_pn_mask [ pn_space ] = fd_ulong_if( shift<64UL, conn->rx_pn_mask[ pn_space ]<<( shift&63UL ), 0UL ) | 1UL;
638 0 : conn->exp_pkt_number[ pn_space ] = pkt_number+1UL;
639 0 : return 0;
640 0 : }
641 0 : ulong age = exp-1UL-pkt_number;
642 0 : if( FD_UNLIKELY( age>=64UL || fd_ulong_extract_bit( conn->rx_pn_mask[ pn_space ], (int)age ) ) ) return 1;
643 0 : conn->rx_pn_mask[ pn_space ] = fd_ulong_set_bit( conn->rx_pn_mask[ pn_space ], (int)age );
644 0 : return 0;
645 0 : }
646 :
647 : /* fd_quic_svc_prep_schedule sets conn->svc_meta.next_timeout to
648 : min of current and provided expiry time. */
649 : static inline void
650 : fd_quic_svc_prep_schedule( fd_quic_conn_t * conn,
651 0 : long expiry ) {
652 0 : conn->svc_meta.next_timeout = fd_long_min( conn->svc_meta.next_timeout, expiry );
653 0 : }
654 :
655 : /* fd_quic_svc_prep_schedule_now sets conn->svc_meta.next_timeout to
656 : current time. For when state is not already available */
657 : static inline void
658 0 : fd_quic_svc_prep_schedule_now( fd_quic_conn_t * conn ) {
659 0 : fd_quic_state_t * state = fd_quic_get_state( conn->quic );
660 0 : fd_quic_svc_prep_schedule( conn, state->now );
661 0 : }
662 :
663 : /* Scheduling helper. Retrieves timers from conn to call schedule */
664 : static inline void
665 0 : fd_quic_svc_schedule1( fd_quic_conn_t * conn ) {
666 0 : fd_quic_state_t * state = fd_quic_get_state( conn->quic );
667 0 : fd_quic_svc_timers_schedule( state->svc_timers, conn, state->now );
668 0 : }
669 :
670 : /* Validation Helper */
671 : static inline void
672 0 : svc_cnt_eq_alloc_conn( fd_quic_svc_timers_t * timers, fd_quic_t * quic ) {
673 0 : ulong const event_cnt = fd_quic_svc_timers_cnt_events( timers );
674 0 : ulong const conn_cnt = quic->metrics.conn_alloc_cnt;
675 0 : if( FD_UNLIKELY( event_cnt != conn_cnt ) ) {
676 0 : FD_LOG_CRIT(( "only %lu out of %lu connections are in timer", event_cnt, conn_cnt ));
677 0 : }
678 0 : }
679 : /* validates the free conn list doesn't cycle, point nowhere, leak, or point to live conn */
680 : static void
681 0 : fd_quic_conn_free_validate( fd_quic_t * quic ) {
682 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
683 :
684 : /* initialize visited */
685 0 : fd_quic_conn_validate_init( quic );
686 :
687 0 : ulong cnt = 0UL;
688 0 : uint node = state->free_conn_list;
689 0 : while( node!=UINT_MAX ) {
690 0 : FD_TEST( node < quic->limits.conn_cnt );
691 0 : fd_quic_conn_t * conn = fd_quic_conn_at_idx( state, node );
692 0 : FD_TEST( conn->state == FD_QUIC_CONN_STATE_INVALID );
693 0 : conn->visited = 1U;
694 0 : node = conn->free_conn_next;
695 0 : cnt++;
696 0 : FD_TEST( cnt <= quic->limits.conn_cnt );
697 0 : }
698 :
699 0 : for( ulong j=0UL; j < quic->limits.conn_cnt; j++ ) {
700 0 : fd_quic_conn_t * conn = fd_quic_conn_at_idx( state, j );
701 0 : FD_TEST( conn->conn_idx==j );
702 0 : if( conn->state == FD_QUIC_CONN_STATE_INVALID ) {
703 0 : FD_TEST( conn->visited );
704 0 : } else {
705 0 : FD_TEST( !conn->visited );
706 0 : }
707 0 : }
708 0 : }
709 :
710 : void
711 0 : fd_quic_state_validate( fd_quic_t * quic ) {
712 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
713 :
714 : /* init visited for svc_timers_validate to use */
715 0 : fd_quic_conn_validate_init( quic );
716 0 : FD_TEST( fd_quic_svc_timers_validate( state->svc_timers, quic ) );
717 :
718 0 : fd_quic_conn_free_validate( quic );
719 0 : }
720 :
721 : fd_quic_conn_t *
722 : fd_quic_conn_query( fd_quic_conn_map_t * map,
723 0 : ulong conn_id ) {
724 0 : fd_quic_conn_map_t sentinel = {0};
725 0 : if( !conn_id ) return NULL;
726 0 : fd_quic_conn_map_t * entry = fd_quic_conn_map_query( map, conn_id, &sentinel );
727 0 : fd_quic_conn_t * conn = entry->conn;
728 0 : if( conn ) {
729 0 : if( FD_UNLIKELY( conn->state==FD_QUIC_CONN_STATE_INVALID ) ) {
730 0 : FD_LOG_CRIT(( "Conn ID %016lx at %p is in map but in free state", conn_id, (void *)conn ));
731 0 : }
732 0 : }
733 0 : return conn;
734 0 : }
735 :
736 : /* Helpers for generating fd_quic_log entries */
737 :
738 : static fd_quic_log_hdr_t
739 0 : fd_quic_log_conn_hdr( fd_quic_conn_t const * conn ) {
740 0 : fd_quic_log_hdr_t hdr = {
741 0 : .conn_id = conn->our_conn_id,
742 0 : .flags = 0
743 0 : };
744 0 : return hdr;
745 0 : }
746 :
747 : static fd_quic_log_hdr_t
748 : fd_quic_log_full_hdr( fd_quic_conn_t const * conn,
749 0 : fd_quic_pkt_t const * pkt ) {
750 0 : fd_quic_log_hdr_t hdr = {
751 0 : .conn_id = conn->our_conn_id,
752 0 : .pkt_num = pkt->pkt_number,
753 0 : .ip4_saddr = pkt->ip4->saddr,
754 0 : .udp_sport = pkt->udp->net_sport,
755 0 : .enc_level = (uchar)pkt->enc_level,
756 0 : .flags = 0
757 0 : };
758 0 : return hdr;
759 0 : }
760 :
761 : inline static void
762 : fd_quic_set_conn_state( fd_quic_conn_t * conn,
763 0 : uint state ) {
764 0 : FD_COMPILER_MFENCE();
765 0 : uint old_state = conn->state;
766 0 : conn->quic->metrics.conn_state_cnt[ old_state ]--;
767 0 : conn->quic->metrics.conn_state_cnt[ state ]++;
768 0 : conn->state = state;
769 0 : FD_COMPILER_MFENCE();
770 0 : }
771 :
772 :
773 : /* fd_quic_conn_error sets the connection state to aborted. This does
774 : not destroy the connection object. Rather, it will eventually cause
775 : the connection to be freed during a later fd_quic_service call.
776 : reason is an RFC 9000 QUIC error code. error_line is the source line
777 : of code in fd_quic.c */
778 :
779 : static void
780 : fd_quic_conn_error1( fd_quic_conn_t * conn,
781 0 : uint reason ) {
782 0 : if( FD_UNLIKELY( !conn || conn->state == FD_QUIC_CONN_STATE_DEAD ) ) return;
783 :
784 0 : fd_quic_set_conn_state( conn, FD_QUIC_CONN_STATE_ABORT );
785 0 : conn->reason = reason;
786 :
787 : /* set connection to be serviced ASAP */
788 0 : fd_quic_svc_prep_schedule_now( conn );
789 0 : fd_quic_svc_schedule1( conn );
790 0 : }
791 :
792 : static void
793 : fd_quic_conn_error( fd_quic_conn_t * conn,
794 : uint reason,
795 0 : uint error_line ) {
796 0 : fd_quic_conn_error1( conn, reason );
797 :
798 0 : fd_quic_state_t * state = fd_quic_get_state( conn->quic );
799 :
800 0 : ulong sig = fd_quic_log_sig( FD_QUIC_EVENT_CONN_QUIC_CLOSE );
801 0 : fd_quic_log_error_t * frame = fd_quic_log_tx_prepare( state->log_tx );
802 0 : *frame = (fd_quic_log_error_t) {
803 0 : .hdr = fd_quic_log_conn_hdr( conn ),
804 0 : .code = { reason, 0UL },
805 0 : .src_file = "fd_quic.c",
806 0 : .src_line = error_line,
807 0 : };
808 0 : fd_quic_log_tx_submit( state->log_tx, sizeof(fd_quic_log_error_t), sig, (long)state->now );
809 0 : }
810 :
811 : static void
812 : fd_quic_frame_error( fd_quic_frame_ctx_t const * ctx,
813 : uint reason,
814 0 : uint error_line ) {
815 0 : fd_quic_t * quic = ctx->quic;
816 0 : fd_quic_conn_t * conn = ctx->conn;
817 0 : fd_quic_pkt_t const * pkt = ctx->pkt;
818 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
819 :
820 0 : fd_quic_conn_error1( conn, reason );
821 :
822 0 : uint tls_reason = 0U;
823 0 : if( conn->tls_hs ) tls_reason = conn->tls_hs->hs.base.reason;
824 :
825 0 : ulong sig = fd_quic_log_sig( FD_QUIC_EVENT_CONN_QUIC_CLOSE );
826 0 : fd_quic_log_error_t * frame = fd_quic_log_tx_prepare( state->log_tx );
827 0 : *frame = (fd_quic_log_error_t) {
828 0 : .hdr = fd_quic_log_full_hdr( conn, pkt ),
829 0 : .code = { reason, tls_reason },
830 0 : .src_file = "fd_quic.c",
831 0 : .src_line = error_line,
832 0 : };
833 0 : fd_quic_log_tx_submit( state->log_tx, sizeof(fd_quic_log_error_t), sig, state->now );
834 0 : }
835 :
836 : /* returns the encoding level we should use for the next tx quic packet
837 : or all 1's if nothing to tx */
838 : static uint
839 0 : fd_quic_tx_enc_level( fd_quic_conn_t * conn, int acks ) {
840 0 : uint app_pn_space = fd_quic_enc_level_to_pn_space( fd_quic_enc_level_appdata_id );
841 0 : ulong app_pkt_number = conn->pkt_number[app_pn_space];
842 :
843 : /* fd_quic_tx_enc_level( ... )
844 : check status - if closing, set based on handshake complete
845 : check for acks
846 : find lowest enc level
847 : check for hs_data
848 : find lowest enc level
849 : if any, use lowest
850 : else
851 : if stream data, use 1-rtt
852 : else
853 : nothing to do */
854 :
855 : /* check status */
856 0 : switch( conn->state ) {
857 0 : case FD_QUIC_CONN_STATE_DEAD:
858 : /* do not send on dead connection at all */
859 0 : return ~0u;
860 :
861 0 : case FD_QUIC_CONN_STATE_ABORT:
862 0 : case FD_QUIC_CONN_STATE_CLOSE_PENDING:
863 : /* use handshake or app enc level depending on handshake complete */
864 0 : if( !(conn->flags & FD_QUIC_CONN_FLAGS_CLOSE_SENT ) ) {
865 0 : if( conn->handshake_complete ) {
866 0 : return fd_quic_enc_level_appdata_id;
867 0 : } else if( fd_uint_extract_bit( conn->keys_avail, fd_quic_enc_level_handshake_id ) ) {
868 0 : return fd_quic_enc_level_handshake_id;
869 0 : } else if( fd_uint_extract_bit( conn->keys_avail, fd_quic_enc_level_initial_id ) ) {
870 0 : return fd_quic_enc_level_initial_id;
871 0 : }
872 0 : }
873 0 : return ~0u;
874 :
875 : /* TODO consider this optimization... but we want to ack all handshakes, even if there is stream_data */
876 0 : case FD_QUIC_CONN_STATE_ACTIVE:
877 0 : if( FD_LIKELY( !conn->tls_hs ) ) {
878 : /* optimization for case where we have stream data to send */
879 :
880 : /* find stream data to send */
881 0 : fd_quic_stream_t * sentinel = conn->send_streams;
882 0 : fd_quic_stream_t * stream = sentinel->next;
883 0 : if( !stream->sentinel && stream->upd_pkt_number >= app_pkt_number ) {
884 0 : return fd_quic_enc_level_appdata_id;
885 0 : }
886 0 : }
887 0 : }
888 :
889 : /* pick enc_level of oldest ACK not yet sent */
890 0 : fd_quic_ack_gen_t * ack_gen = conn->ack_gen;
891 0 : fd_quic_ack_t const * oldest_ack = fd_quic_ack_queue_ele( ack_gen, ack_gen->tail );
892 0 : uint ack_enc_level = oldest_ack->enc_level; /* speculative load (might be invalid) */
893 0 : if( (ack_gen->head != ack_gen->tail) & acks & fd_uint_extract_bit( conn->keys_avail, (int)ack_enc_level ) ) {
894 0 : return ack_enc_level;
895 0 : }
896 :
897 : /* Check for handshake data to send */
898 0 : uint min_keyed_enc_level = (uint)fd_uint_find_lsb_w_default( conn->keys_avail, (int)~0u );
899 0 : if( FD_UNLIKELY( conn->tls_hs ) ) {
900 0 : fd_quic_tls_hs_data_t * hs_data = NULL;
901 :
902 : /* Starting at min_keyed_enc_level guarantees keys are avail if we return in this loop
903 : - The discard order specified by RFC 9001 Section 4.9 prevents gaps in key availability
904 : - get_hs_data returning non-null means keys were avail at some point for this enc_level */
905 0 : for( uint i = min_keyed_enc_level; i < 4; ++i ) {
906 0 : hs_data = fd_quic_tls_get_hs_data( conn->tls_hs, i );
907 0 : if( hs_data ) {
908 : /* offset within stream */
909 0 : ulong offset = conn->hs_sent_bytes[i];
910 : /* skip packets we've sent */
911 0 : while( hs_data && hs_data->offset + hs_data->data_sz <= offset ) {
912 0 : hs_data = fd_quic_tls_get_next_hs_data( conn->tls_hs, hs_data );
913 0 : }
914 0 : if( hs_data ) {
915 0 : return i;
916 0 : }
917 0 : }
918 0 : }
919 0 : }
920 :
921 : /* handshake done? */
922 0 : if( FD_UNLIKELY( conn->handshake_done_send ) ) return fd_quic_enc_level_appdata_id;
923 :
924 : /* find stream data to send */
925 0 : fd_quic_stream_t * sentinel = conn->send_streams;
926 0 : fd_quic_stream_t * stream = sentinel->next;
927 0 : if( (!stream->sentinel) & (stream->upd_pkt_number >= app_pkt_number) & fd_uint_extract_bit( conn->keys_avail, fd_quic_enc_level_appdata_id ) ) {
928 0 : return fd_quic_enc_level_appdata_id;
929 0 : }
930 :
931 : /* only allow 1-RTT "flag" frames when we have the keys, to prevent e.g. early 1-RTT PINGs */
932 0 : uint flags_pending = conn->flags & ~(FD_QUIC_CONN_FLAGS_CLOSE_SENT | FD_QUIC_CONN_FLAGS_PING_SENT);
933 0 : if( ( flags_pending != 0U )
934 0 : & ( conn->upd_pkt_number >= app_pkt_number )
935 0 : & fd_uint_extract_bit( conn->keys_avail, fd_quic_enc_level_appdata_id ) ) {
936 0 : return fd_quic_enc_level_appdata_id;
937 0 : }
938 :
939 : /* nothing to send */
940 0 : return ~0u;
941 0 : }
942 :
943 : /* Include frame code generator */
944 :
945 : #include "templ/fd_quic_frame.c"
946 :
947 : /* handle single v1 frames */
948 : /* returns bytes consumed */
949 : ulong
950 : fd_quic_handle_v1_frame( fd_quic_t * quic,
951 : fd_quic_conn_t * conn,
952 : fd_quic_pkt_t * pkt,
953 : uint pkt_type,
954 : uchar const * buf,
955 0 : ulong buf_sz ) {
956 0 : if( conn->state == FD_QUIC_CONN_STATE_DEAD ) return FD_QUIC_PARSE_FAIL;
957 0 : if( FD_UNLIKELY( buf_sz<1UL ) ) return FD_QUIC_PARSE_FAIL;
958 :
959 : /* Frame ID is technically a varint but it's sufficient to look at the
960 : first byte. */
961 0 : uint id = buf[0];
962 :
963 0 : FD_DTRACE_PROBE_4( quic_handle_frame, id, conn->our_conn_id, pkt_type, pkt->pkt_number );
964 :
965 0 : fd_quic_frame_ctx_t frame_context[1] = {{ quic, conn, pkt, 0UL }};
966 0 : if( FD_UNLIKELY( !fd_quic_frame_type_allowed( pkt_type, id ) ) ) {
967 0 : FD_DTRACE_PROBE_4( quic_err_frame_not_allowed, id, conn->our_conn_id, pkt_type, pkt->pkt_number );
968 0 : fd_quic_frame_error( frame_context, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
969 0 : return FD_QUIC_PARSE_FAIL;
970 0 : }
971 0 : quic->metrics.frame_rx_cnt[ fd_quic_frame_metric_id[ id ] ]++;
972 :
973 0 : pkt->ack_flag |= fd_quic_frame_type_flags[ id ]&FD_QUIC_FRAME_FLAG_N ? 0 : ACK_FLAG_RQD;
974 :
975 : /* tail call to frame handler */
976 0 : switch( id ) {
977 :
978 0 : # define F(T,MID,NAME,...) \
979 0 : case T: return fd_quic_interpret_##NAME##_frame( frame_context, buf, buf_sz );
980 0 : FD_QUIC_FRAME_TYPES(F)
981 0 : # undef F
982 :
983 0 : default:
984 : /* FIXME this should be unreachable, but gracefully handle this case as defense-in-depth */
985 : /* unknown frame types are PROTOCOL_VIOLATION errors */
986 0 : FD_DEBUG( FD_LOG_DEBUG(( "unexpected frame type: %u", id )); )
987 0 : fd_quic_frame_error( frame_context, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
988 0 : return FD_QUIC_PARSE_FAIL;
989 0 : }
990 :
991 0 : }
992 :
993 : fd_quic_t *
994 0 : fd_quic_fini( fd_quic_t * quic ) {
995 :
996 0 : if( FD_UNLIKELY( !quic ) ) {
997 0 : FD_LOG_WARNING(("NULL quic"));
998 0 : return NULL;
999 0 : }
1000 :
1001 : /* Derive memory layout */
1002 :
1003 0 : fd_quic_layout_t layout = {0};
1004 0 : fd_quic_footprint_ext( &quic->limits, &layout );
1005 :
1006 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
1007 :
1008 : /* Free conns */
1009 :
1010 0 : ulong conn_laddr = (ulong)quic + layout.conns_off;
1011 0 : for( ulong i=0; i < quic->limits.conn_cnt; i++ ) {
1012 0 : fd_quic_conn_t * conn = (fd_quic_conn_t *)( conn_laddr );
1013 0 : conn_laddr += layout.conn_footprint;
1014 :
1015 0 : if( conn->state ) fd_quic_conn_free( quic, conn );
1016 0 : }
1017 :
1018 : /* Deinit TLS */
1019 :
1020 0 : fd_quic_tls_hs_pool_delete( fd_quic_tls_hs_pool_leave( state->hs_pool ) ); state->hs_pool = NULL;
1021 0 : fd_quic_tls_delete( state->tls );
1022 0 : fd_quic_tls_hs_cache_delete( fd_quic_tls_hs_cache_leave( &state->hs_cache ) );
1023 :
1024 :
1025 : /* Delete conn ID map */
1026 :
1027 0 : fd_quic_conn_map_delete( fd_quic_conn_map_leave( state->conn_map ) );
1028 0 : state->conn_map = NULL;
1029 :
1030 : /* Clear join-lifetime memory regions */
1031 :
1032 0 : memset( state, 0, sizeof(fd_quic_state_t) );
1033 :
1034 0 : return quic;
1035 0 : }
1036 :
1037 : void *
1038 0 : fd_quic_delete( fd_quic_t * quic ) {
1039 :
1040 0 : if( FD_UNLIKELY( !quic ) ) {
1041 0 : FD_LOG_WARNING(( "NULL quic" ));
1042 0 : return NULL;
1043 0 : }
1044 :
1045 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)quic, fd_quic_align() ) ) ) {
1046 0 : FD_LOG_WARNING(( "misaligned quic" ));
1047 0 : return NULL;
1048 0 : }
1049 :
1050 0 : if( FD_UNLIKELY( quic->magic!=FD_QUIC_MAGIC ) ) {
1051 0 : FD_LOG_WARNING(( "bad magic" ));
1052 0 : return NULL;
1053 0 : }
1054 :
1055 0 : void * shmlog = (void *)( (ulong)quic + quic->layout.log_off );
1056 0 : if( FD_UNLIKELY( !fd_quic_log_buf_delete( shmlog ) ) ) {
1057 0 : FD_LOG_WARNING(( "fd_quic_log_buf_delete failed" ));
1058 0 : return NULL;
1059 0 : }
1060 :
1061 0 : FD_COMPILER_MFENCE();
1062 0 : FD_VOLATILE( quic->magic ) = 0UL;
1063 0 : FD_COMPILER_MFENCE();
1064 :
1065 0 : return (void *)quic;
1066 0 : }
1067 :
1068 : fd_quic_stream_t *
1069 0 : fd_quic_conn_new_stream( fd_quic_conn_t * conn ) {
1070 0 : if( FD_UNLIKELY( !conn->stream_map ) ) {
1071 : /* QUIC config is receive-only */
1072 0 : return NULL;
1073 0 : }
1074 :
1075 0 : fd_quic_t * quic = conn->quic;
1076 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
1077 0 : if( FD_UNLIKELY( !state->stream_pool ) ) return NULL;
1078 :
1079 0 : ulong next_stream_id = conn->tx_next_stream_id;
1080 :
1081 : /* The user is responsible for calling this, for setting limits, */
1082 : /* and for setting stream_pool size */
1083 : /* Only current use cases for QUIC client is for testing */
1084 : /* So leaving this question unanswered for now */
1085 :
1086 : /* peer imposed limit on streams */
1087 0 : ulong peer_sup_stream_id = conn->tx_sup_stream_id;
1088 :
1089 : /* is connection inactive */
1090 0 : if( FD_UNLIKELY( conn->state != FD_QUIC_CONN_STATE_ACTIVE ||
1091 0 : next_stream_id >= peer_sup_stream_id ) ) {
1092 : /* this is a normal condition which occurs whenever we run up to
1093 : the peer advertised limit and represents one form of flow control */
1094 0 : return NULL;
1095 0 : }
1096 :
1097 : /* obtain a stream from stream_pool */
1098 0 : fd_quic_stream_t * stream = fd_quic_stream_pool_alloc( state->stream_pool );
1099 :
1100 0 : if( FD_UNLIKELY( !stream ) ) {
1101 : /* no streams available in the stream pool */
1102 0 : return NULL;
1103 0 : }
1104 :
1105 : /* add to map of stream ids */
1106 0 : fd_quic_stream_map_t * entry = fd_quic_stream_map_insert( conn->stream_map, next_stream_id );
1107 0 : if( FD_UNLIKELY( !entry ) ) {
1108 : /* return stream to pool */
1109 0 : fd_quic_stream_pool_free( state->stream_pool, stream );
1110 0 : FD_LOG_INFO(( "stream map insert failed" ));
1111 0 : return NULL;
1112 0 : }
1113 :
1114 0 : fd_quic_stream_init( stream );
1115 0 : FD_QUIC_STREAM_LIST_INIT_STREAM( stream );
1116 :
1117 : /* stream tx_buf already set */
1118 0 : stream->conn = conn;
1119 0 : stream->stream_id = next_stream_id;
1120 0 : stream->context = NULL;
1121 :
1122 : /* set the max stream data to the appropriate initial value */
1123 0 : stream->tx_max_stream_data = conn->tx_initial_max_stream_data_uni;
1124 :
1125 : /* set state depending on stream type */
1126 0 : stream->state = FD_QUIC_STREAM_STATE_RX_FIN;
1127 0 : stream->stream_flags = 0u;
1128 :
1129 0 : memset( stream->tx_ack, 0, fd_quic_stream_tx_ack_bufsz( stream ) );
1130 :
1131 : /* insert into used streams */
1132 0 : FD_QUIC_STREAM_LIST_REMOVE( stream );
1133 0 : FD_QUIC_STREAM_LIST_INSERT_BEFORE( conn->used_streams, stream );
1134 :
1135 : /* generate a new stream id */
1136 0 : conn->tx_next_stream_id = next_stream_id + 4U;
1137 :
1138 : /* assign the stream to the entry */
1139 0 : entry->stream = stream;
1140 :
1141 : /* update metrics */
1142 0 : quic->metrics.stream_opened_cnt++;
1143 0 : quic->metrics.stream_active_cnt++;
1144 :
1145 0 : FD_DEBUG( FD_LOG_DEBUG(( "Created stream with ID %lu", next_stream_id )) );
1146 0 : return stream;
1147 0 : }
1148 :
1149 : int
1150 : fd_quic_stream_send( fd_quic_stream_t * stream,
1151 : void const * data,
1152 : ulong data_sz,
1153 0 : int fin ) {
1154 0 : if( FD_UNLIKELY( stream->state & FD_QUIC_STREAM_STATE_TX_FIN ) ) {
1155 0 : return FD_QUIC_SEND_ERR_FIN;
1156 0 : }
1157 :
1158 0 : fd_quic_conn_t * conn = stream->conn;
1159 :
1160 0 : fd_quic_buffer_t * tx_buf = &stream->tx_buf;
1161 :
1162 : /* are we allowed to send? */
1163 0 : ulong stream_id = stream->stream_id;
1164 :
1165 : /* stream_id & 2 == 0 is bidir
1166 : stream_id & 1 == 0 is client */
1167 0 : if( FD_UNLIKELY( ( ( (uint)stream_id & 2u ) == 2u ) &
1168 0 : ( ( (uint)stream_id & 1u ) != (uint)conn->server ) ) ) {
1169 0 : return FD_QUIC_SEND_ERR_INVAL_STREAM;
1170 0 : }
1171 :
1172 0 : if( FD_UNLIKELY( conn->state != FD_QUIC_CONN_STATE_ACTIVE ) ) {
1173 0 : if( conn->state == FD_QUIC_CONN_STATE_HANDSHAKE ||
1174 0 : conn->state == FD_QUIC_CONN_STATE_HANDSHAKE_COMPLETE ) {
1175 0 : return FD_QUIC_SEND_ERR_STREAM_STATE;
1176 0 : }
1177 0 : return FD_QUIC_SEND_ERR_INVAL_CONN;
1178 0 : }
1179 :
1180 : /* how many bytes are we allowed to send on the stream and on the connection? */
1181 0 : ulong allowed_stream = stream->tx_max_stream_data - stream->tx_tot_data;
1182 0 : ulong allowed_conn = conn->tx_max_data - conn->tx_tot_data;
1183 0 : ulong allowed = fd_ulong_min( allowed_conn, allowed_stream );
1184 :
1185 0 : if( data_sz > fd_quic_buffer_avail( tx_buf ) ) {
1186 0 : return FD_QUIC_SEND_ERR_FLOW;
1187 0 : }
1188 :
1189 0 : if( data_sz > allowed ) {
1190 0 : return FD_QUIC_SEND_ERR_FLOW;
1191 0 : }
1192 :
1193 : /* store data from data into tx_buf */
1194 0 : fd_quic_buffer_store( tx_buf, data, data_sz );
1195 :
1196 : /* adjust flow control limits on stream and connection */
1197 0 : stream->tx_tot_data += data_sz;
1198 0 : conn->tx_tot_data += data_sz;
1199 :
1200 : /* insert into send list */
1201 0 : if( !FD_QUIC_STREAM_ACTION( stream ) ) {
1202 0 : FD_QUIC_STREAM_LIST_REMOVE( stream );
1203 0 : FD_QUIC_STREAM_LIST_INSERT_BEFORE( conn->send_streams, stream );
1204 0 : }
1205 0 : stream->stream_flags |= FD_QUIC_STREAM_FLAGS_UNSENT; /* we have unsent data */
1206 0 : stream->upd_pkt_number = FD_QUIC_PKT_NUM_PENDING; /* schedule tx */
1207 :
1208 : /* don't actually set fin flag if we didn't add the last
1209 : byte to the buffer */
1210 0 : if( fin ) {
1211 0 : fd_quic_stream_fin( stream );
1212 0 : }
1213 :
1214 : /* schedule send */
1215 0 : fd_quic_svc_prep_schedule_now( conn );
1216 0 : fd_quic_svc_schedule1( conn );
1217 :
1218 0 : return FD_QUIC_SUCCESS;
1219 0 : }
1220 :
1221 : void
1222 0 : fd_quic_stream_fin( fd_quic_stream_t * stream ) {
1223 0 : if( FD_UNLIKELY( stream->state & FD_QUIC_STREAM_STATE_TX_FIN ) ) {
1224 0 : return;
1225 0 : }
1226 :
1227 0 : fd_quic_conn_t * conn = stream->conn;
1228 :
1229 : /* insert into send list */
1230 0 : if( !FD_QUIC_STREAM_ACTION( stream ) ) {
1231 0 : FD_QUIC_STREAM_LIST_REMOVE( stream );
1232 0 : FD_QUIC_STREAM_LIST_INSERT_BEFORE( conn->send_streams, stream );
1233 0 : }
1234 0 : stream->stream_flags |= FD_QUIC_STREAM_FLAGS_TX_FIN; /* state immediately updated */
1235 0 : stream->state |= FD_QUIC_STREAM_STATE_TX_FIN; /* state immediately updated */
1236 0 : stream->upd_pkt_number = FD_QUIC_PKT_NUM_PENDING; /* update to be sent in next packet */
1237 :
1238 : /* TODO update metrics */
1239 0 : }
1240 :
1241 : void
1242 0 : fd_quic_conn_set_rx_max_data( fd_quic_conn_t * conn, ulong rx_max_data ) {
1243 : /* cannot reduce max_data, and cannot increase beyond max varint */
1244 0 : if( rx_max_data > conn->srx->rx_max_data && rx_max_data < (1UL<<62)-1UL ) {
1245 0 : conn->srx->rx_max_data = rx_max_data;
1246 0 : conn->flags |= FD_QUIC_CONN_FLAGS_MAX_DATA;
1247 0 : conn->upd_pkt_number = FD_QUIC_PKT_NUM_PENDING;
1248 0 : fd_quic_svc_prep_schedule_now( conn );
1249 0 : fd_quic_svc_schedule1( conn );
1250 0 : }
1251 0 : }
1252 :
1253 : /* packet processing */
1254 :
1255 : /* fd_quic_conn_free_pkt_meta frees all pkt_meta associated with
1256 : enc_level. Returns the number of freed pkt_meta. */
1257 : static ulong
1258 : fd_quic_conn_free_pkt_meta( fd_quic_conn_t * conn,
1259 0 : uint enc_level ) {
1260 0 : fd_quic_pkt_meta_tracker_t * tracker = &conn->pkt_meta_tracker;
1261 0 : fd_quic_pkt_meta_t * pool = tracker->pool;
1262 0 : fd_quic_pkt_meta_ds_t * sent = &tracker->sent_pkt_metas[enc_level];
1263 :
1264 0 : fd_quic_pkt_meta_t * prev = NULL;
1265 0 : for( fd_quic_pkt_meta_ds_fwd_iter_t iter = fd_quic_pkt_meta_ds_fwd_iter_init( sent, pool );
1266 0 : !fd_quic_pkt_meta_ds_fwd_iter_done( iter );
1267 0 : iter = fd_quic_pkt_meta_ds_fwd_iter_next( iter, pool ) ) {
1268 0 : fd_quic_pkt_meta_t * e = fd_quic_pkt_meta_ds_fwd_iter_ele( iter, pool );
1269 0 : if( FD_LIKELY( prev ) ) {
1270 0 : fd_quic_pkt_meta_pool_ele_release( pool, prev );
1271 0 : }
1272 0 : prev = e;
1273 0 : }
1274 0 : if( FD_LIKELY( prev ) ) {
1275 0 : fd_quic_pkt_meta_pool_ele_release( pool, prev );
1276 0 : }
1277 :
1278 :
1279 : /* Instead of paying log(n) to maintain the treap structure during ele_remove
1280 : on each iteration, we've returned all pkt_meta to the pool, but left them
1281 : in the treap. Then, we reinitialize the treap, in constant time deleting
1282 : all elements that were in the 'old treap'. This function now runs in linear
1283 : time, instead of O(n*lg(n)). */
1284 :
1285 0 : ulong pre_ele_cnt = fd_quic_pkt_meta_ds_ele_cnt( sent );
1286 :
1287 0 : fd_quic_pkt_meta_ds_clear( tracker, enc_level );
1288 0 : conn->used_pkt_meta -= pre_ele_cnt;
1289 :
1290 0 : return pre_ele_cnt;
1291 0 : }
1292 :
1293 : /* reclaim and free all pkt_meta for a given encryption level,
1294 : and return the number affected. */
1295 : static ulong
1296 0 : fd_quic_reclaim_pkt_meta_level( fd_quic_conn_t * conn, uint enc_level ) {
1297 0 : fd_quic_pkt_meta_tracker_t * tracker = &conn->pkt_meta_tracker;
1298 0 : fd_quic_pkt_meta_t * pool = tracker->pool;
1299 0 : fd_quic_pkt_meta_ds_t * sent = &tracker->sent_pkt_metas[enc_level];
1300 :
1301 0 : for( fd_quic_pkt_meta_ds_fwd_iter_t iter = fd_quic_pkt_meta_ds_fwd_iter_init( sent, pool );
1302 0 : !fd_quic_pkt_meta_ds_fwd_iter_done( iter );
1303 0 : iter = fd_quic_pkt_meta_ds_fwd_iter_next( iter, pool ) ) {
1304 0 : fd_quic_pkt_meta_t * e = fd_quic_pkt_meta_ds_fwd_iter_ele( iter, pool );
1305 0 : fd_quic_reclaim_pkt_meta( conn, e, enc_level );
1306 0 : }
1307 :
1308 0 : return fd_quic_conn_free_pkt_meta( conn, enc_level );
1309 0 : }
1310 :
1311 :
1312 : /* fd_quic_abandon_enc_level frees all resources associated encryption
1313 : levels less or equal to enc_level. Returns the number of freed
1314 : pkt_meta. */
1315 :
1316 : ulong
1317 : fd_quic_abandon_enc_level( fd_quic_conn_t * conn,
1318 0 : uint enc_level ) {
1319 0 : if( FD_LIKELY( !fd_uint_extract_bit( conn->keys_avail, (int)enc_level ) ) ) return 0UL;
1320 0 : FD_DEBUG( FD_LOG_DEBUG(( "conn=%p abandoning enc_level=%u", (void *)conn, enc_level )); )
1321 :
1322 0 : fd_quic_ack_gen_abandon_enc_level( conn->ack_gen, enc_level );
1323 :
1324 0 : ulong freed = 0UL;
1325 0 : for( uint j = 0; j <= enc_level; ++j ) {
1326 0 : conn->keys_avail = fd_uint_clear_bit( conn->keys_avail, (int)j );
1327 : /* treat all packets as ACKed (freeing handshake data, etc.) */
1328 0 : freed += fd_quic_reclaim_pkt_meta_level( conn, j );
1329 0 : }
1330 :
1331 0 : return freed;
1332 0 : }
1333 :
1334 : static void
1335 : fd_quic_gen_initial_secret_and_keys(
1336 : fd_quic_conn_t * conn,
1337 : fd_quic_conn_id_t const * dst_conn_id,
1338 0 : int is_server ) {
1339 :
1340 0 : fd_quic_gen_initial_secrets(
1341 0 : &conn->secrets,
1342 0 : dst_conn_id->conn_id, dst_conn_id->sz,
1343 0 : is_server );
1344 :
1345 0 : fd_quic_gen_keys(
1346 0 : &conn->keys[ fd_quic_enc_level_initial_id ][ 0 ],
1347 0 : conn->secrets.secret[ fd_quic_enc_level_initial_id ][ 0 ] );
1348 :
1349 0 : fd_quic_gen_keys(
1350 0 : &conn->keys[ fd_quic_enc_level_initial_id ][ 1 ],
1351 0 : conn->secrets.secret[ fd_quic_enc_level_initial_id ][ 1 ] );
1352 0 : }
1353 :
1354 : static ulong
1355 : fd_quic_send_retry( fd_quic_t * quic,
1356 : fd_quic_pkt_t * pkt,
1357 : fd_quic_conn_id_t const * odcid,
1358 : fd_quic_conn_id_t const * scid,
1359 0 : ulong new_conn_id ) {
1360 :
1361 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
1362 :
1363 0 : long expire_at = state->now + quic->config.retry_ttl;
1364 0 : uchar retry_pkt[ FD_QUIC_RETRY_LOCAL_SZ ];
1365 0 : ulong nonce0 = fd_quic_rng_ulong( state );
1366 0 : ulong nonce1 = fd_quic_rng_ulong( state );
1367 0 : ulong retry_pkt_sz = fd_quic_retry_create( retry_pkt, pkt, nonce0, nonce1, state->retry_secret, state->retry_iv, odcid, scid, new_conn_id, expire_at );
1368 :
1369 0 : quic->metrics.retry_tx_cnt++;
1370 :
1371 0 : uchar * tx_ptr = retry_pkt + retry_pkt_sz;
1372 0 : if( FD_UNLIKELY( fd_quic_tx_buffered_raw(
1373 0 : quic,
1374 : // these are state variable's normally updated on a conn, but irrelevant in retry so we
1375 : // just size it exactly as the encoded retry packet
1376 0 : &tx_ptr,
1377 0 : retry_pkt,
1378 : // encode buffer
1379 0 : &pkt->ip4->net_id,
1380 0 : pkt->ip4->saddr,
1381 0 : pkt->udp->net_sport,
1382 0 : pkt->ip4->daddr,
1383 0 : pkt->udp->net_dport ) == FD_QUIC_FAILED ) ) {
1384 0 : return FD_QUIC_PARSE_FAIL;
1385 0 : }
1386 0 : return 0UL;
1387 0 : }
1388 :
1389 : /* fd_quic_send_invalid_token answers a post-Retry Initial whose token
1390 : expired with a stateless Initial CONNECTION_CLOSE(INVALID_TOKEN), so
1391 : the client fails the dial at once and redials for a fresh Retry
1392 : (RFC 9000 8.1.2). The keys come from the DCID the client used. */
1393 :
1394 : static void
1395 : fd_quic_send_invalid_token( fd_quic_t * quic,
1396 : fd_quic_pkt_t * pkt,
1397 : fd_quic_conn_id_t const * dcid,
1398 0 : fd_quic_conn_id_t const * peer_scid ) {
1399 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
1400 :
1401 0 : fd_quic_crypto_secrets_t secrets[1];
1402 0 : fd_quic_crypto_keys_t keys[1];
1403 0 : fd_quic_gen_initial_secrets( secrets, dcid->conn_id, dcid->sz, /* is_server */ 1 );
1404 0 : fd_quic_gen_keys( keys, secrets->secret[ fd_quic_enc_level_initial_id ][ 1 ] );
1405 :
1406 0 : uchar plain[ 64 ];
1407 0 : ulong our_cid = fd_quic_rng_ulong( state );
1408 0 : fd_quic_initial_t initial = {
1409 0 : .h0 = fd_quic_initial_h0( 3 ), /* 4 byte pkt num */
1410 0 : .version = 1,
1411 0 : .dst_conn_id_len = peer_scid->sz,
1412 0 : .src_conn_id_len = FD_QUIC_CONN_ID_SZ,
1413 0 : .len = 0x3fff, /* 2 byte varint, patched below */
1414 0 : .pkt_num = 0UL
1415 0 : };
1416 0 : fd_memcpy( initial.dst_conn_id, peer_scid->conn_id, peer_scid->sz );
1417 0 : FD_STORE( ulong, initial.src_conn_id, our_cid );
1418 0 : ulong hdr_sz = fd_quic_encode_initial( plain, sizeof(plain), &initial );
1419 0 : if( FD_UNLIKELY( hdr_sz==FD_QUIC_PARSE_FAIL ) ) return;
1420 :
1421 : /* CONNECTION_CLOSE, padded so the header protection sample exists */
1422 0 : fd_quic_conn_close_0_frame_t frame = { .error_code = FD_QUIC_CONN_REASON_INVALID_TOKEN };
1423 0 : ulong frame_sz = fd_quic_encode_conn_close_0_frame( plain+hdr_sz, sizeof(plain)-hdr_sz, &frame );
1424 0 : if( FD_UNLIKELY( frame_sz==FD_QUIC_PARSE_FAIL ) ) return;
1425 0 : ulong payload_sz = fd_ulong_max( frame_sz, FD_QUIC_CRYPTO_SAMPLE_SZ );
1426 0 : fd_memset( plain+hdr_sz+frame_sz, 0, payload_sz-frame_sz ); /* PADDING frames */
1427 0 : uint len = 4U + (uint)payload_sz + FD_QUIC_CRYPTO_TAG_SZ;
1428 0 : FD_STORE( ushort, plain+hdr_sz-6UL, fd_ushort_bswap( (ushort)( 0x4000U|len ) ) );
1429 :
1430 0 : uchar out[ 128 ];
1431 0 : ulong out_sz = sizeof(out);
1432 : #if FD_QUIC_DISABLE_CRYPTO
1433 : (void)keys;
1434 : out_sz = hdr_sz+payload_sz;
1435 : fd_memcpy( out, plain, out_sz );
1436 : memset( out+out_sz, 0, FD_QUIC_CRYPTO_TAG_SZ ); /* MAC tag */
1437 : out_sz += FD_QUIC_CRYPTO_TAG_SZ;
1438 : #else
1439 0 : if( FD_UNLIKELY( fd_quic_crypto_encrypt( out, &out_sz, plain, hdr_sz, plain+hdr_sz, payload_sz, keys, keys, 0UL )!=FD_QUIC_SUCCESS ) ) return;
1440 0 : #endif
1441 :
1442 0 : uchar * tx_ptr = out + out_sz;
1443 0 : fd_quic_tx_buffered_raw( quic, &tx_ptr, out, &pkt->ip4->net_id,
1444 0 : pkt->ip4->saddr, pkt->udp->net_sport,
1445 0 : pkt->ip4->daddr, pkt->udp->net_dport );
1446 0 : }
1447 :
1448 : /* fd_quic_tls_hs_cache_evict evicts the oldest tls_hs if it's exceeded its ttl
1449 : Assumes cache is non-empty
1450 : and returns 1 if evicted, otherwise returns 0. */
1451 : static int
1452 : fd_quic_tls_hs_cache_evict( fd_quic_t * quic,
1453 0 : fd_quic_state_t * state ) {
1454 :
1455 0 : fd_quic_tls_hs_t* hs_to_free = fd_quic_tls_hs_cache_ele_peek_head( &state->hs_cache, state->hs_pool );
1456 :
1457 0 : if( state->now < hs_to_free->birthtime + quic->config.tls_hs_ttl ) {
1458 : /* oldest is too young to evict */
1459 0 : quic->metrics.hs_err_alloc_fail_cnt++;
1460 0 : return 0;
1461 0 : }
1462 :
1463 0 : fd_quic_cb_conn_final(quic, hs_to_free->context);
1464 0 : fd_quic_conn_free( quic, hs_to_free->context );
1465 0 : quic->metrics.hs_evicted_cnt++;
1466 0 : return 1;
1467 0 : }
1468 :
1469 : /* fd_quic_handle_v1_initial handles an "Initial"-type packet.
1470 : Valid for both server and client. Initial packets are used to
1471 : establish QUIC conns and wrap the TLS handshake flow among other
1472 : things. */
1473 :
1474 : ulong
1475 : fd_quic_handle_v1_initial( fd_quic_t * quic,
1476 : fd_quic_conn_t ** p_conn,
1477 : fd_quic_pkt_t * pkt,
1478 : fd_quic_conn_id_t const * dcid,
1479 : fd_quic_conn_id_t const * peer_scid,
1480 : uchar * cur_ptr,
1481 0 : ulong cur_sz ) {
1482 0 : fd_quic_conn_t * conn = *p_conn;
1483 :
1484 0 : if( FD_UNLIKELY( conn && !fd_uint_extract_bit( conn->keys_avail, fd_quic_enc_level_initial_id ) ) ) {
1485 0 : quic->metrics.pkt_no_key_cnt[ fd_quic_enc_level_initial_id ]++;
1486 0 : return FD_QUIC_PARSE_FAIL;
1487 0 : }
1488 :
1489 : /* RFC 9000 Section 7.2: the peer's Initial SCID cannot change. */
1490 0 : if( FD_UNLIKELY( conn && ( conn->server | conn->established ) &&
1491 0 : ( peer_scid->sz!=conn->peer_cids[0].sz ||
1492 0 : memcmp( peer_scid->conn_id, conn->peer_cids[0].conn_id, peer_scid->sz ) ) ) ) {
1493 0 : return FD_QUIC_PARSE_FAIL;
1494 0 : }
1495 :
1496 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
1497 0 : fd_quic_metrics_t * metrics = &quic->metrics;
1498 :
1499 : /* Initial packets are de-facto unencrypted. Packet protection is
1500 : still applied, albeit with publicly known encryption keys.
1501 :
1502 : RFC 9001 specifies use of the TLS_AES_128_GCM_SHA256_ID suite for
1503 : initial secrets and keys. */
1504 :
1505 : /* Parse initial packet */
1506 :
1507 0 : fd_quic_initial_t initial[1] = {0};
1508 0 : ulong rc = fd_quic_decode_initial( initial, cur_ptr, cur_sz );
1509 0 : if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) {
1510 0 : FD_DEBUG( FD_LOG_DEBUG(( "fd_quic_decode_initial failed" )) );
1511 0 : return FD_QUIC_PARSE_FAIL;
1512 0 : }
1513 :
1514 : /* Check bounds on initial */
1515 :
1516 : /* len indicated the number of bytes after the packet number offset
1517 : so verify this value is within the packet */
1518 0 : ulong pn_offset = initial->pkt_num_pnoff;
1519 0 : ulong body_sz = initial->len; /* length of packet number, frames, and auth tag */
1520 0 : ulong tot_sz = pn_offset + body_sz;
1521 0 : if( FD_UNLIKELY( tot_sz > cur_sz ) ) {
1522 0 : FD_DEBUG( FD_LOG_DEBUG(( "Bogus initial packet length" )) );
1523 0 : return FD_QUIC_PARSE_FAIL;
1524 0 : }
1525 :
1526 : /* count received token len */
1527 0 : int const token_len_match = initial->token_len == sizeof(fd_quic_retry_token_t);
1528 0 : ulong const token_len_idx = fd_ulong_if( !!initial->token_len,
1529 0 : fd_ulong_if( token_len_match, 1, 2 ),
1530 0 : 0 );
1531 0 : metrics->initial_token_len_cnt[ token_len_idx ]++;
1532 :
1533 : /* Check it is valid for a token to be present in an initial packet in the current context.
1534 :
1535 : quic->config.role == FD_QUIC_ROLE_CLIENT
1536 : - Indicates the client received an initial packet with a token from a server. "Initial packets
1537 : sent by the server MUST set the Token Length field to 0; clients that receive an Initial packet
1538 : with a non-zero Token Length field MUST either discard the packet or generate a connection
1539 : error of type PROTOCOL_VIOLATION (RFC 9000, Section 17.2.2)"
1540 :
1541 : quic->config.retry == false
1542 : - Indicates the server is not configured to retry, but a client attached a token to this
1543 : initial packet. NEW_TOKEN frames are not supported, so this implementation treats the presence
1544 : of a token when retry is disabled as an error. */
1545 0 : if( FD_UNLIKELY( initial->token_len > 0 &&
1546 0 : ( quic->config.role == FD_QUIC_ROLE_CLIENT || !quic->config.retry ) ) ) {
1547 0 : FD_DEBUG( FD_LOG_DEBUG(( "Rejecting initial with token" )); )
1548 0 : return FD_QUIC_PARSE_FAIL;
1549 0 : }
1550 :
1551 :
1552 0 : ulong scid; /* outgoing scid */
1553 0 : fd_quic_conn_id_t odcid; /* dst conn id from client's original Initial */
1554 :
1555 : /* Do we have a conn object for this dest conn ID?
1556 : If not, sanity check, send/verify retry if needed */
1557 0 : if( FD_UNLIKELY( !conn ) ) {
1558 : /* if we're a client, and no conn, discard */
1559 0 : if( quic->config.role == FD_QUIC_ROLE_CLIENT ) {
1560 : /* connection may have been torn down */
1561 0 : FD_DEBUG( FD_LOG_DEBUG(( "unknown connection ID" )); )
1562 0 : metrics->pkt_no_conn_cnt[ fd_quic_enc_level_initial_id ]++;
1563 0 : FD_DTRACE_PROBE_2( fd_quic_handle_v1_initial_no_conn , state->now, pkt->pkt_number );
1564 0 : return FD_QUIC_PARSE_FAIL;
1565 0 : }
1566 :
1567 : /* According to RFC 9000 Section 14.1, INITIAL packets less than a
1568 : certain length must be discarded, and the connection may be closed.
1569 : (Mitigates UDP amplification) */
1570 0 : if( pkt->datagram_sz < FD_QUIC_INITIAL_PAYLOAD_SZ_MIN ) {
1571 : /* can't trust the included values, so can't reply */
1572 0 : return FD_QUIC_PARSE_FAIL;
1573 0 : }
1574 :
1575 : /* Early check: Is conn free? */
1576 0 : if( FD_UNLIKELY( state->free_conn_list==UINT_MAX ) ) {
1577 0 : FD_DEBUG( FD_LOG_DEBUG(( "ignoring conn request: no free conn slots" )) );
1578 0 : metrics->conn_err_no_slots_cnt++;
1579 0 : return FD_QUIC_PARSE_FAIL; /* FIXME better error code? */
1580 0 : }
1581 :
1582 :
1583 : /* Primary objective is to send or verify retry.
1584 : We'll also select the scid we'll use from now on.
1585 :
1586 : Rules for selecting the SCID:
1587 : - No retry token, accepted: generate new random ID
1588 : - No retry token, retry request: generate new random ID
1589 : - Retry token, accepted: reuse SCID from retry token */
1590 0 : if( !quic->config.retry ) {
1591 0 : scid = fd_quic_rng_ulong( state );
1592 0 : } else { /* retry configured */
1593 :
1594 : /* Need to send retry? Do so before more work */
1595 0 : if( initial->token_len != sizeof(fd_quic_retry_token_t) ) {
1596 :
1597 0 : ulong new_conn_id_u64 = fd_quic_rng_ulong( state );
1598 0 : if( FD_UNLIKELY( fd_quic_send_retry(
1599 0 : quic, pkt,
1600 0 : dcid, peer_scid, new_conn_id_u64 ) ) ) {
1601 0 : return FD_QUIC_FAILED;
1602 0 : }
1603 0 : pkt->retry_sent = 1U;
1604 0 : return (initial->pkt_num_pnoff + initial->len);
1605 0 : } else {
1606 : /* This Initial packet is in response to our Retry.
1607 : Validate the relevant fields of this post-retry INITIAL packet,
1608 : i.e. retry src conn id, ip, port
1609 : Also populate odcid and scid from the retry data */
1610 0 : int retry_ok = fd_quic_retry_server_verify( pkt, initial, &odcid, &scid, state->retry_secret, state->retry_iv, state->now, quic->config.retry_ttl );
1611 0 : if( FD_UNLIKELY( retry_ok!=FD_QUIC_SUCCESS ) ) {
1612 0 : metrics->conn_err_retry_fail_cnt++;
1613 : /* No conn object exists. A token we issued that expired gets a
1614 : stateless close, so the client redials instead of probing
1615 : with it until its handshake times out. */
1616 0 : if( retry_ok==FD_QUIC_RETRY_EXPIRED ) {
1617 0 : metrics->conn_err_retry_expired_cnt++;
1618 0 : fd_quic_send_invalid_token( quic, pkt, dcid, peer_scid );
1619 0 : }
1620 0 : return FD_QUIC_PARSE_FAIL;
1621 0 : };
1622 0 : }
1623 0 : }
1624 0 : }
1625 :
1626 : /* Reject an unavailable handshake slot before deriving Initial keys.
1627 : Eviction still waits for an authenticated packet. */
1628 0 : if( !conn && !fd_quic_tls_hs_pool_free( state->hs_pool ) ) {
1629 0 : fd_quic_tls_hs_t * oldest = fd_quic_tls_hs_cache_ele_peek_head( &state->hs_cache, state->hs_pool );
1630 0 : if( state->now < oldest->birthtime + quic->config.tls_hs_ttl ) {
1631 0 : metrics->hs_err_alloc_fail_cnt++;
1632 0 : return FD_QUIC_PARSE_FAIL;
1633 0 : }
1634 0 : }
1635 :
1636 : /* Determine decryption keys, related data */
1637 :
1638 : /* Placeholder for generated crypto material before allocating conn */
1639 0 : fd_quic_crypto_keys_t _rx_keys[1];
1640 0 : fd_quic_crypto_secrets_t _secrets[1];
1641 :
1642 : /* Conditional inputs to decryption stage */
1643 0 : fd_quic_crypto_keys_t * rx_keys = NULL;
1644 0 : fd_quic_crypto_secrets_t * secrets = NULL;
1645 0 : ulong exp_pkt_num;
1646 :
1647 0 : if( !conn ) {
1648 : /* no conn, generate secret and rx keys */
1649 0 : rx_keys = _rx_keys;
1650 0 : secrets = _secrets;
1651 0 : exp_pkt_num = 0;
1652 :
1653 0 : fd_quic_gen_initial_secrets(
1654 0 : secrets,
1655 0 : dcid->conn_id, dcid->sz,
1656 0 : /* is_server */ 1 );
1657 0 : fd_quic_gen_keys(
1658 0 : rx_keys,
1659 0 : secrets->secret[ fd_quic_enc_level_initial_id ][ 0 ] );
1660 0 : } else {
1661 : /* conn, use existing keys/secrets */
1662 0 : rx_keys = &conn->keys[ fd_quic_enc_level_initial_id ][0];
1663 0 : secrets = &conn->secrets;
1664 0 : exp_pkt_num = conn->exp_pkt_number[0];
1665 0 : }
1666 :
1667 : /* Decrypt incoming packet */
1668 :
1669 : /* header protection needs the offset to the packet number */
1670 :
1671 0 : # if !FD_QUIC_DISABLE_CRYPTO
1672 : /* this decrypts the header */
1673 0 : if( FD_UNLIKELY(
1674 0 : fd_quic_crypto_decrypt_hdr( cur_ptr, tot_sz,
1675 0 : pn_offset,
1676 0 : rx_keys ) != FD_QUIC_SUCCESS ) ) {
1677 : /* As this is an INITIAL packet, change the status to DEAD, and allow
1678 : it to be reaped */
1679 0 : FD_DEBUG( FD_LOG_DEBUG(( "fd_quic_crypto_decrypt_hdr failed" )) );
1680 0 : quic->metrics.pkt_decrypt_fail_cnt[ fd_quic_enc_level_initial_id ]++;
1681 0 : return FD_QUIC_PARSE_FAIL;
1682 0 : }
1683 0 : # endif /* !FD_QUIC_DISABLE_CRYPTO */
1684 :
1685 0 : ulong pkt_number_sz = fd_quic_h0_pkt_num_len( cur_ptr[0] ) + 1u;
1686 0 : ulong pktnum_comp = fd_quic_pktnum_decode( cur_ptr+pn_offset, pkt_number_sz );
1687 :
1688 : /* reconstruct packet number */
1689 0 : ulong pkt_number = fd_quic_reconstruct_pkt_num( pktnum_comp, pkt_number_sz, exp_pkt_num );
1690 :
1691 0 : # if !FD_QUIC_DISABLE_CRYPTO
1692 : /* NOTE from rfc9002 s3
1693 : It is permitted for some packet numbers to never be used, leaving intentional gaps. */
1694 : /* this decrypts the header and payload */
1695 0 : if( FD_UNLIKELY(
1696 0 : fd_quic_crypto_decrypt( cur_ptr, tot_sz,
1697 0 : pn_offset,
1698 0 : pkt_number,
1699 0 : rx_keys ) != FD_QUIC_SUCCESS ) ) {
1700 0 : FD_DEBUG( FD_LOG_DEBUG(( "fd_quic_crypto_decrypt failed" )) );
1701 0 : FD_DTRACE_PROBE_2( quic_err_decrypt_initial_pkt, pkt->ip4, pkt->pkt_number );
1702 0 : quic->metrics.pkt_decrypt_fail_cnt[ fd_quic_enc_level_initial_id ]++;
1703 0 : return FD_QUIC_PARSE_FAIL;
1704 0 : }
1705 0 : # endif /* FD_QUIC_DISABLE_CRYPTO */
1706 :
1707 : /* Reserved bits are authenticated; check only after payload decryption. */
1708 0 : if( FD_UNLIKELY( cur_ptr[0] & 0x0c ) ) {
1709 0 : if( conn ) fd_quic_conn_error( conn, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
1710 0 : return FD_QUIC_PARSE_FAIL;
1711 0 : }
1712 :
1713 : /* set packet number on the context */
1714 0 : pkt->pkt_number = pkt_number;
1715 :
1716 0 : if( FD_UNLIKELY( body_sz < pkt_number_sz + FD_QUIC_CRYPTO_TAG_SZ ) ) {
1717 0 : return FD_QUIC_PARSE_FAIL;
1718 0 : }
1719 :
1720 : /* Reject frame-less packets before allocating state (RFC 9000 §12.4). */
1721 0 : if( FD_UNLIKELY( body_sz == pkt_number_sz + FD_QUIC_CRYPTO_TAG_SZ ) ) {
1722 0 : if( conn ) fd_quic_conn_error( conn, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
1723 0 : return FD_QUIC_PARSE_FAIL;
1724 0 : }
1725 :
1726 : /* If no conn, create one. Due to previous checks, role must be server
1727 : and this must be response to Retry (if needed). */
1728 0 : if( FD_UNLIKELY( !conn ) ) {
1729 :
1730 : /* Save peer's conn ID, which we will use to address peer with. */
1731 0 : fd_quic_conn_id_t peer_conn_id = {0};
1732 0 : fd_memcpy( peer_conn_id.conn_id, initial->src_conn_id, FD_QUIC_MAX_CONN_ID_SZ );
1733 0 : peer_conn_id.sz = initial->src_conn_id_len;
1734 :
1735 : /* Prepare QUIC-TLS transport params object (sent as a TLS extension).
1736 : Take template from state and mutate certain params in-place.
1737 :
1738 : See RFC 9000 Section 18 */
1739 :
1740 : /* TODO Each transport param is a TLV tuple. This allows serializing
1741 : most transport params ahead of time. Only the conn-specific
1742 : differences will have to be appended here. */
1743 :
1744 0 : fd_quic_transport_params_t tp[1] = { state->transport_params };
1745 :
1746 0 : if( !quic->config.retry ) {
1747 : /* assume no retry */
1748 0 : tp->retry_source_connection_id_present = 0;
1749 :
1750 : /* Send orig conn ID back to client (server only) */
1751 :
1752 0 : tp->original_destination_connection_id_present = 1;
1753 0 : tp->original_destination_connection_id_len = dcid->sz;
1754 0 : fd_memcpy( tp->original_destination_connection_id,
1755 0 : dcid->conn_id,
1756 0 : dcid->sz );
1757 0 : } else { /* retry configured */
1758 :
1759 : /* From rfc 9000:
1760 :
1761 : Figure 8 shows a similar handshake that includes a Retry packet.
1762 :
1763 : Client Server
1764 : Initial: DCID=S1, SCID=C1 ->
1765 : <- Retry: DCID=C1, SCID=S2
1766 : Initial: DCID=S2, SCID=C1 ->
1767 : <- Initial: DCID=C1, SCID=S3
1768 : ...
1769 : 1-RTT: DCID=S3 ->
1770 : <- 1-RTT: DCID=C1
1771 :
1772 : Figure 8: Use of Connection IDs in a Handshake with Retry
1773 : In both cases (Figures 7 and 8), the client sets the value of the
1774 : initial_source_connection_id transport parameter to C1.
1775 :
1776 : When the handshake does not include a Retry (Figure 7), the server
1777 : sets original_destination_connection_id to S1 (note that this value
1778 : is chosen by the client) and initial_source_connection_id to S3. In
1779 : this case, the server does not include a retry_source_connection_id
1780 : transport parameter.
1781 :
1782 : When the handshake includes a Retry (Figure 8), the server sets
1783 : original_destination_connection_id to S1, retry_source_connection_id
1784 : to S2, and initial_source_connection_id to S3. */
1785 0 : tp->original_destination_connection_id_present = 1;
1786 0 : tp->original_destination_connection_id_len = odcid.sz;
1787 0 : memcpy( tp->original_destination_connection_id,
1788 0 : odcid.conn_id,
1789 0 : odcid.sz );
1790 :
1791 : /* Client echoes back the SCID we sent via Retry. Safe to trust
1792 : because we signed the Retry Token. (Length and content validated
1793 : in fd_quic_retry_server_verify) */
1794 0 : tp->retry_source_connection_id_present = 1;
1795 0 : tp->retry_source_connection_id_len = FD_QUIC_CONN_ID_SZ;
1796 0 : FD_STORE( ulong, tp->retry_source_connection_id, scid );
1797 :
1798 0 : metrics->conn_retry_cnt++;
1799 0 : }
1800 :
1801 : /* Repeat the conn ID we picked in transport params (this is done
1802 : to authenticate conn IDs via TLS by including them in TLS-
1803 : protected data).
1804 :
1805 : Per spec, this field should be the source conn ID field we've set
1806 : on the first Initial packet we've sent. At this point, we might
1807 : not have sent an Initial packet yet -- so this field should hold
1808 : a value we are about to pick.
1809 :
1810 : fd_quic_conn_create will set conn->initial_source_conn_id to
1811 : the random new_conn_id we've created earlier. */
1812 :
1813 0 : tp->initial_source_connection_id_present = 1;
1814 0 : tp->initial_source_connection_id_len = FD_QUIC_CONN_ID_SZ;
1815 0 : FD_STORE( ulong, tp->initial_source_connection_id, scid );
1816 :
1817 : /* tls hs available? After decrypting because might evict another hs */
1818 0 : if( FD_UNLIKELY( !fd_quic_tls_hs_pool_free( state->hs_pool ) ) ) {
1819 : /* try evicting, 0 if oldest is too young so fail */
1820 0 : if( !fd_quic_tls_hs_cache_evict( quic, state )) {
1821 0 : return FD_QUIC_PARSE_FAIL;
1822 0 : }
1823 0 : }
1824 :
1825 : /* Allocate new conn */
1826 0 : conn = fd_quic_conn_create( quic,
1827 0 : scid,
1828 0 : &peer_conn_id,
1829 0 : pkt->ip4->saddr,
1830 0 : pkt->udp->net_sport,
1831 0 : pkt->ip4->daddr,
1832 0 : pkt->udp->net_dport,
1833 0 : 1 /* server */ );
1834 :
1835 0 : if( FD_UNLIKELY( !conn ) ) { /* no free connections */
1836 : /* TODO send failure back to origin? */
1837 : /* FIXME unreachable? conn_cnt already checked above */
1838 0 : FD_DEBUG( FD_LOG_WARNING( ( "failed to allocate QUIC conn" ) ) );
1839 0 : return FD_QUIC_PARSE_FAIL;
1840 0 : }
1841 0 : FD_DEBUG( FD_LOG_DEBUG(( "new connection allocated" )) );
1842 :
1843 : /* set the value for the caller */
1844 0 : *p_conn = conn;
1845 :
1846 : /* Create a TLS handshake */
1847 0 : fd_quic_tls_hs_t * tls_hs = fd_quic_tls_hs_new(
1848 0 : fd_quic_tls_hs_pool_ele_acquire( state->hs_pool ),
1849 0 : state->tls,
1850 0 : (void*)conn,
1851 0 : 1 /*is_server*/,
1852 0 : tp,
1853 0 : state->now );
1854 0 : fd_quic_tls_hs_cache_ele_push_tail( &state->hs_cache, tls_hs, state->hs_pool );
1855 :
1856 0 : conn->tls_hs = tls_hs;
1857 0 : quic->metrics.hs_created_cnt++;
1858 :
1859 : /* copy secrets and rx keys */
1860 0 : conn->secrets = *secrets;
1861 0 : conn->keys[ fd_quic_enc_level_initial_id ][0] = *rx_keys;
1862 :
1863 : /* generate tx keys */
1864 0 : fd_quic_gen_keys(
1865 0 : &conn->keys[ fd_quic_enc_level_initial_id ][ 1 ],
1866 0 : secrets->secret[ fd_quic_enc_level_initial_id ][ 1 ] );
1867 0 : }
1868 :
1869 0 : if( FD_UNLIKELY( !conn->host.ip_addr ) ) {
1870 : /* Lock src IP address in place (previously chosen by layer-4 based
1871 : on the route table) */
1872 0 : conn->host.ip_addr = pkt->ip4->daddr;
1873 0 : }
1874 :
1875 : /* check if reply conn id needs to change */
1876 0 : if( FD_UNLIKELY( !( conn->server | conn->established ) ) ) {
1877 : /* switch to the source connection id for future replies */
1878 :
1879 : /* replace peer 0 connection id */
1880 0 : conn->peer_cids[0].sz = initial->src_conn_id_len;
1881 0 : fd_memcpy( conn->peer_cids[0].conn_id, initial->src_conn_id, FD_QUIC_MAX_CONN_ID_SZ );
1882 :
1883 : /* don't repeat this procedure */
1884 0 : conn->established = 1;
1885 0 : }
1886 :
1887 0 : if( FD_UNLIKELY( fd_quic_pkt_num_rx( conn, 0U, pkt_number ) ) ) {
1888 0 : quic->metrics.pkt_dup_cnt++;
1889 0 : return FD_QUIC_PARSE_FAIL;
1890 0 : }
1891 :
1892 : /* handle frames */
1893 0 : ulong payload_off = pn_offset + pkt_number_sz;
1894 0 : uchar const * frame_ptr = cur_ptr + payload_off;
1895 0 : ulong frame_sz = body_sz - pkt_number_sz - FD_QUIC_CRYPTO_TAG_SZ; /* total size of all frames in packet */
1896 0 : while( frame_sz != 0UL ) {
1897 0 : rc = fd_quic_handle_v1_frame( quic,
1898 0 : conn,
1899 0 : pkt,
1900 0 : FD_QUIC_PKT_TYPE_INITIAL,
1901 0 : frame_ptr,
1902 0 : frame_sz );
1903 0 : if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) {
1904 0 : FD_DEBUG( FD_LOG_DEBUG(( "Failed to handle frame (Initial, frame=0x%02x)", frame_ptr[0] )) );
1905 0 : quic->metrics.frame_rx_err_cnt++;
1906 0 : return FD_QUIC_PARSE_FAIL;
1907 0 : }
1908 :
1909 0 : if( FD_UNLIKELY( rc==0UL || rc>frame_sz ) ) {
1910 0 : fd_quic_conn_error( conn, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
1911 0 : return FD_QUIC_PARSE_FAIL;
1912 0 : }
1913 :
1914 : /* next frame, and remaining size */
1915 0 : frame_ptr += rc;
1916 0 : frame_sz -= rc;
1917 0 : }
1918 :
1919 : /* update last activity */
1920 0 : conn->last_activity = state->now;
1921 0 : conn->flags &= ~( FD_QUIC_CONN_FLAGS_PING_SENT | FD_QUIC_CONN_FLAGS_PING );
1922 :
1923 : /* insert into service queue */
1924 0 : fd_quic_svc_prep_schedule( conn, state->now );
1925 :
1926 : /* return number of bytes consumed */
1927 0 : return tot_sz;
1928 0 : }
1929 :
1930 : ulong
1931 : fd_quic_handle_v1_handshake(
1932 : fd_quic_t * quic,
1933 : fd_quic_conn_t * conn,
1934 : fd_quic_pkt_t * pkt,
1935 : uchar * cur_ptr,
1936 : ulong cur_sz
1937 0 : ) {
1938 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
1939 0 : if( FD_UNLIKELY( !conn ) ) {
1940 0 : quic->metrics.pkt_no_conn_cnt[ fd_quic_enc_level_handshake_id ]++;
1941 0 : FD_DTRACE_PROBE_2( fd_quic_handle_v1_handshake_no_conn , state->now, pkt->pkt_number );
1942 0 : return FD_QUIC_PARSE_FAIL;
1943 0 : }
1944 :
1945 0 : if( FD_UNLIKELY( !fd_uint_extract_bit( conn->keys_avail, fd_quic_enc_level_handshake_id ) ) ) {
1946 0 : quic->metrics.pkt_no_key_cnt[ fd_quic_enc_level_handshake_id ]++;
1947 0 : return FD_QUIC_PARSE_FAIL;
1948 0 : }
1949 :
1950 : /* do parse here */
1951 0 : fd_quic_handshake_t handshake[1];
1952 0 : ulong rc = fd_quic_decode_handshake( handshake, cur_ptr, cur_sz );
1953 0 : if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) {
1954 0 : FD_DEBUG( FD_LOG_DEBUG(( "fd_quic_decode_handshake failed" )) );
1955 0 : return FD_QUIC_PARSE_FAIL;
1956 0 : }
1957 :
1958 : /* check bounds on handshake */
1959 :
1960 : /* len indicated the number of bytes after the packet number offset
1961 : so verify this value is within the packet */
1962 0 : ulong len = (ulong)( handshake->pkt_num_pnoff + handshake->len );
1963 0 : if( FD_UNLIKELY( len > cur_sz ) ) {
1964 0 : FD_DEBUG( FD_LOG_DEBUG(( "Handshake packet bounds check failed" )); )
1965 0 : return FD_QUIC_PARSE_FAIL;
1966 0 : }
1967 :
1968 : /* connection ids should already be in the relevant structures */
1969 :
1970 : /* TODO prepare most of the transport parameters, and only append the
1971 : necessary differences */
1972 :
1973 : /* fetch TLS handshake */
1974 0 : fd_quic_tls_hs_t * tls_hs = conn->tls_hs;
1975 0 : if( FD_UNLIKELY( !tls_hs ) ) {
1976 0 : FD_DEBUG( FD_LOG_DEBUG(( "no tls handshake" )) );
1977 0 : return FD_QUIC_PARSE_FAIL;
1978 0 : }
1979 :
1980 : /* decryption */
1981 :
1982 : /* header protection needs the offset to the packet number */
1983 0 : ulong pn_offset = handshake->pkt_num_pnoff;
1984 :
1985 0 : ulong body_sz = handshake->len; /* not a protected field */
1986 : /* length of payload + num packet bytes */
1987 0 : ulong tot_sz = pn_offset + body_sz; /* total including header and payload */
1988 :
1989 0 : # if !FD_QUIC_DISABLE_CRYPTO
1990 : /* this decrypts the header */
1991 0 : if( FD_UNLIKELY(
1992 0 : fd_quic_crypto_decrypt_hdr( cur_ptr, tot_sz,
1993 0 : pn_offset,
1994 0 : &conn->keys[2][0] ) != FD_QUIC_SUCCESS ) ) {
1995 0 : FD_DEBUG( FD_LOG_DEBUG(( "fd_quic_crypto_decrypt_hdr failed" )) );
1996 0 : quic->metrics.pkt_decrypt_fail_cnt[ fd_quic_enc_level_handshake_id ]++;
1997 0 : return FD_QUIC_PARSE_FAIL;
1998 0 : }
1999 0 : # endif /* !FD_QUIC_DISABLE_CRYPTO */
2000 :
2001 : /* number of bytes in the packet header */
2002 0 : ulong pkt_number_sz = fd_quic_h0_pkt_num_len( cur_ptr[0] ) + 1u;
2003 :
2004 : /* now we have decrypted packet number */
2005 0 : ulong pktnum_comp = fd_quic_pktnum_decode( cur_ptr+pn_offset, pkt_number_sz );
2006 :
2007 : /* reconstruct packet number */
2008 0 : ulong pkt_number = fd_quic_reconstruct_pkt_num( pktnum_comp, pkt_number_sz, conn->exp_pkt_number[1] );
2009 :
2010 : /* NOTE from rfc9002 s3
2011 : It is permitted for some packet numbers to never be used, leaving intentional gaps. */
2012 :
2013 0 : # if !FD_QUIC_DISABLE_CRYPTO
2014 : /* this decrypts the header and payload */
2015 0 : if( FD_UNLIKELY(
2016 0 : fd_quic_crypto_decrypt( cur_ptr, tot_sz,
2017 0 : pn_offset,
2018 0 : pkt_number,
2019 0 : &conn->keys[2][0] ) != FD_QUIC_SUCCESS ) ) {
2020 : /* remove connection from map, and insert into free list */
2021 0 : FD_DEBUG( FD_LOG_DEBUG(( "fd_quic_crypto_decrypt failed" )) );
2022 0 : FD_DTRACE_PROBE_3( quic_err_decrypt_handshake_pkt, pkt->ip4, conn->our_conn_id, pkt->pkt_number );
2023 0 : quic->metrics.pkt_decrypt_fail_cnt[ fd_quic_enc_level_handshake_id ]++;
2024 0 : return FD_QUIC_PARSE_FAIL;
2025 0 : }
2026 0 : # endif /* FD_QUIC_DISABLE_CRYPTO */
2027 :
2028 : /* Reserved bits are authenticated; check only after payload decryption. */
2029 0 : if( FD_UNLIKELY( cur_ptr[0] & 0x0c ) ) {
2030 0 : fd_quic_conn_error( conn, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
2031 0 : return FD_QUIC_PARSE_FAIL;
2032 0 : }
2033 :
2034 : /* set packet number on the context */
2035 0 : pkt->pkt_number = pkt_number;
2036 :
2037 : /* check body size large enough for required elements */
2038 0 : if( FD_UNLIKELY( body_sz < pkt_number_sz + FD_QUIC_CRYPTO_TAG_SZ ) ) {
2039 0 : return FD_QUIC_PARSE_FAIL;
2040 0 : }
2041 :
2042 0 : if( FD_UNLIKELY( fd_quic_pkt_num_rx( conn, 1U, pkt_number ) ) ) {
2043 0 : quic->metrics.pkt_dup_cnt++;
2044 0 : return FD_QUIC_PARSE_FAIL;
2045 0 : }
2046 :
2047 : /* handle frames */
2048 0 : ulong payload_off = pn_offset + pkt_number_sz;
2049 0 : uchar const * frame_ptr = cur_ptr + payload_off;
2050 0 : ulong frame_sz = body_sz - pkt_number_sz - FD_QUIC_CRYPTO_TAG_SZ; /* total size of all frames in packet */
2051 :
2052 : /* Reject frame-less packets (RFC 9000 §12.4). */
2053 0 : if( FD_UNLIKELY( frame_sz==0UL ) ) {
2054 0 : fd_quic_conn_error( conn, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
2055 0 : return FD_QUIC_PARSE_FAIL;
2056 0 : }
2057 :
2058 0 : while( frame_sz != 0UL ) {
2059 0 : rc = fd_quic_handle_v1_frame( quic,
2060 0 : conn,
2061 0 : pkt,
2062 0 : FD_QUIC_PKT_TYPE_HANDSHAKE,
2063 0 : frame_ptr,
2064 0 : frame_sz );
2065 0 : if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) {
2066 0 : FD_DEBUG( FD_LOG_DEBUG(( "Failed to handle frame (Handshake, frame=0x%02x)", frame_ptr[0] )) );
2067 0 : quic->metrics.frame_rx_err_cnt++;
2068 0 : return FD_QUIC_PARSE_FAIL;
2069 0 : }
2070 :
2071 0 : if( FD_UNLIKELY( rc == 0UL || rc > frame_sz ) ) {
2072 0 : fd_quic_conn_error( conn, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
2073 0 : return FD_QUIC_PARSE_FAIL;
2074 0 : }
2075 :
2076 : /* next frame and remaining size */
2077 0 : frame_ptr += rc;
2078 0 : frame_sz -= rc;
2079 0 : }
2080 :
2081 : /* RFC 9000 Section 17.2.2.1. Abandoning Initial Packets
2082 : > A server stops sending and processing Initial packets when it
2083 : > receives its first Handshake packet.
2084 :
2085 : RFC 9001 Section 4.9.1 Discarding Initial Keys
2086 : > a server MUST discard Initial keys when it first successfully processes a Handshake packet */
2087 0 : if( FD_LIKELY( quic->config.role==FD_QUIC_ROLE_SERVER ) ) fd_quic_abandon_enc_level( conn, fd_quic_enc_level_initial_id );
2088 :
2089 : /* update last activity */
2090 0 : conn->last_activity = fd_quic_get_state( quic )->now;
2091 0 : conn->flags &= ~( FD_QUIC_CONN_FLAGS_PING_SENT | FD_QUIC_CONN_FLAGS_PING );
2092 :
2093 : /* return number of bytes consumed */
2094 0 : return tot_sz;
2095 0 : }
2096 :
2097 : ulong
2098 : fd_quic_handle_v1_retry(
2099 : fd_quic_t * quic,
2100 : fd_quic_conn_t * conn,
2101 : fd_quic_pkt_t const * pkt,
2102 : uchar const * cur_ptr,
2103 : ulong cur_sz
2104 0 : ) {
2105 0 : (void)pkt;
2106 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
2107 :
2108 0 : if( FD_UNLIKELY( quic->config.role == FD_QUIC_ROLE_SERVER ) ) {
2109 : /* RFC 9000 Section 5.2.2: servers MUST drop incoming packets for
2110 : which the RFC does not specify behavior */
2111 0 : return FD_QUIC_PARSE_FAIL;
2112 0 : }
2113 :
2114 0 : if( FD_UNLIKELY( !conn ) ) {
2115 0 : FD_DTRACE_PROBE_2( fd_quic_handle_v1_retry_no_conn, state->now, pkt->pkt_number );
2116 0 : quic->metrics.pkt_no_conn_cnt[1]++;
2117 0 : return FD_QUIC_PARSE_FAIL;
2118 0 : }
2119 :
2120 : /* RFC 9000 Section 17.2.5.2:
2121 : > A client MUST accept and process at most one Retry packet for
2122 : > each connection attempt. After the client has received and
2123 : > processed an Initial or Retry packet from the server, it MUST
2124 : > discard any subsequent Retry packets that it receives. */
2125 0 : if( FD_UNLIKELY( conn->established | (conn->retry_src_conn_id.sz!=0) ) ) {
2126 0 : FD_DTRACE_PROBE_2( fd_quic_handle_v1_retry_late, state->now, conn->our_conn_id );
2127 0 : quic->metrics.conn_err_retry_fail_cnt++;
2128 0 : return FD_QUIC_PARSE_FAIL;
2129 0 : }
2130 :
2131 0 : fd_quic_conn_id_t const * orig_dst_conn_id = &conn->peer_cids[0];
2132 0 : uchar const * retry_token = NULL;
2133 0 : ulong retry_token_sz = 0UL;
2134 :
2135 0 : int rc = fd_quic_retry_client_verify(
2136 0 : cur_ptr, cur_sz,
2137 0 : orig_dst_conn_id,
2138 0 : &conn->retry_src_conn_id,
2139 0 : &retry_token, &retry_token_sz
2140 0 : );
2141 0 : if( FD_UNLIKELY( rc!=FD_QUIC_SUCCESS ) ) {
2142 0 : quic->metrics.conn_err_retry_fail_cnt++;
2143 0 : return FD_QUIC_PARSE_FAIL;
2144 0 : }
2145 :
2146 : /* RFC 9000 Section 17.2.5.2:
2147 : > A client MUST discard a Retry packet that contains a SCID field
2148 : > that is identical to the DCID field of its Initial packet. */
2149 0 : if( FD_UNLIKELY( ( conn->retry_src_conn_id.sz==orig_dst_conn_id->sz ) &&
2150 0 : ( 0==memcmp( conn->retry_src_conn_id.conn_id, orig_dst_conn_id->conn_id, orig_dst_conn_id->sz ) ) ) ) {
2151 0 : conn->retry_src_conn_id.sz = 0U;
2152 0 : quic->metrics.conn_err_retry_fail_cnt++;
2153 0 : return FD_QUIC_PARSE_FAIL;
2154 0 : }
2155 :
2156 : /* Update the peer using the retry src conn id */
2157 0 : conn->peer_cids[0] = conn->retry_src_conn_id;
2158 :
2159 : /* Re-send the ClientHello */
2160 0 : conn->hs_sent_bytes[fd_quic_enc_level_initial_id] = 0;
2161 :
2162 : /* Need to regenerate keys using the retry source connection id */
2163 0 : fd_quic_gen_initial_secret_and_keys( conn, &conn->retry_src_conn_id, /* is_server */ 0 );
2164 :
2165 : /* The token length is the remaining bytes in the retry packet after subtracting known fields. */
2166 0 : conn->token_len = retry_token_sz;
2167 0 : fd_memcpy( &conn->token, retry_token, conn->token_len );
2168 :
2169 : /* have to rewind the handshake data */
2170 0 : uint enc_level = fd_quic_enc_level_initial_id;
2171 0 : conn->hs_sent_bytes[enc_level] = 0;
2172 :
2173 : /* send the INITIAL */
2174 0 : conn->upd_pkt_number = FD_QUIC_PKT_NUM_PENDING;
2175 :
2176 0 : fd_quic_svc_prep_schedule_now( conn );
2177 :
2178 0 : return cur_sz;
2179 0 : }
2180 :
2181 : int
2182 : fd_quic_lazy_ack_pkt( fd_quic_t * quic,
2183 : fd_quic_conn_t * conn,
2184 0 : fd_quic_pkt_t const * pkt ) {
2185 0 : if( pkt->ack_flag & ACK_FLAG_CANCEL ) {
2186 0 : return FD_QUIC_ACK_TX_CANCEL;
2187 0 : }
2188 :
2189 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
2190 0 : fd_quic_ack_gen_t * ack_gen = conn->ack_gen;
2191 0 : int res = fd_quic_ack_pkt( conn->ack_gen, pkt->pkt_number, pkt->enc_level, state->now );
2192 0 : conn->ack_gen->is_elicited |= fd_uchar_if( pkt->ack_flag & ACK_FLAG_RQD, 1, 0 );
2193 :
2194 : /* Trigger immediate ACK send? */
2195 0 : int ack_sz_threshold_hit = conn->unacked_sz > quic->config.ack_threshold;
2196 0 : int force_instant_ack =
2197 0 : ( !!(pkt->ack_flag & ACK_FLAG_RQD) ) &
2198 0 : ( ( pkt->enc_level == fd_quic_enc_level_initial_id ) |
2199 0 : ( pkt->enc_level == fd_quic_enc_level_handshake_id ) );
2200 0 : if( ack_sz_threshold_hit | force_instant_ack ) {
2201 0 : conn->unacked_sz = 0UL;
2202 0 : fd_quic_svc_prep_schedule( conn, state->now );
2203 0 : } else if( ack_gen->is_elicited ) {
2204 0 : fd_quic_svc_prep_schedule( conn, state->now + quic->config.ack_delay );
2205 0 : }
2206 :
2207 0 : return res;
2208 0 : }
2209 :
2210 : /* This thunk works around a compiler bug (bogus stringop-overflow warning) in GCC 11 */
2211 : __attribute__((noinline)) static void
2212 0 : fd_quic_key_update_derive1( fd_quic_conn_t * conn ) {
2213 0 : fd_quic_key_update_derive( &conn->secrets, conn->new_keys );
2214 0 : }
2215 :
2216 : static void
2217 0 : fd_quic_key_update_complete( fd_quic_conn_t * conn ) {
2218 : /* Key updates are only possible for 1-RTT packets, which are appdata */
2219 0 : ulong const enc_level = fd_quic_enc_level_appdata_id;
2220 :
2221 : /* Update payload keys */
2222 0 : memcpy( conn->keys[enc_level][0].pkt_key, conn->new_keys[0].pkt_key, FD_AES_128_KEY_SZ );
2223 0 : memcpy( conn->keys[enc_level][0].iv, conn->new_keys[0].iv, FD_AES_GCM_IV_SZ );
2224 0 : memcpy( conn->keys[enc_level][1].pkt_key, conn->new_keys[1].pkt_key, FD_AES_128_KEY_SZ );
2225 0 : memcpy( conn->keys[enc_level][1].iv, conn->new_keys[1].iv, FD_AES_GCM_IV_SZ );
2226 :
2227 : /* Update IVs */
2228 0 : memcpy( conn->secrets.secret[enc_level][0], conn->secrets.new_secret[0], FD_QUIC_SECRET_SZ );
2229 0 : memcpy( conn->secrets.secret[enc_level][1], conn->secrets.new_secret[1], FD_QUIC_SECRET_SZ );
2230 :
2231 : /* Packet header encryption keys are not updated */
2232 :
2233 : /* Wind up for next key phase update */
2234 0 : conn->key_phase = !conn->key_phase;
2235 0 : conn->key_update = 0;
2236 0 : conn->key_phase_ack_sent = 0;
2237 0 : conn->key_phase_updated = 1;
2238 0 : fd_quic_key_update_derive1( conn );
2239 :
2240 0 : FD_DEBUG( FD_LOG_DEBUG(( "key update completed" )); )
2241 0 : }
2242 :
2243 : static void
2244 : fd_quic_server_handshake_confirm( fd_quic_t * quic,
2245 0 : fd_quic_conn_t * conn ) {
2246 0 : if( conn->state!=FD_QUIC_CONN_STATE_HANDSHAKE_COMPLETE || !conn->server ) return;
2247 0 : conn->handshake_done_send = 1;
2248 :
2249 : /* move straight to ACTIVE */
2250 0 : fd_quic_set_conn_state( conn, FD_QUIC_CONN_STATE_ACTIVE );
2251 :
2252 : /* RFC 9001 4.9.2. Discarding Handshake Keys
2253 : > An endpoint MUST discard its Handshake keys when the
2254 : > TLS handshake is confirmed
2255 : RFC 9001 4.1.2. Handshake Confirmed
2256 : > [...] the TLS handshake is considered confirmed at the
2257 : > server when the handshake completes */
2258 0 : fd_quic_abandon_enc_level( conn, fd_quic_enc_level_handshake_id );
2259 :
2260 : /* user callback */
2261 0 : fd_quic_cb_conn_new( quic, conn );
2262 :
2263 : /* clear out hs_data here, as we don't need it anymore */
2264 0 : if( conn->tls_hs ) fd_quic_tls_clear_hs_data( conn->tls_hs, fd_quic_enc_level_appdata_id );
2265 0 : }
2266 :
2267 : ulong
2268 : fd_quic_handle_v1_one_rtt( fd_quic_t * quic,
2269 : fd_quic_conn_t * conn,
2270 : fd_quic_pkt_t * pkt,
2271 : uchar * const cur_ptr,
2272 0 : ulong const tot_sz ) {
2273 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
2274 :
2275 0 : if( !conn ) {
2276 0 : quic->metrics.pkt_no_conn_cnt[ fd_quic_enc_level_appdata_id ]++;
2277 0 : FD_DTRACE_PROBE_2( fd_quic_handle_v1_one_rtt_no_conn , state->now, pkt->pkt_number );
2278 0 : FD_DEBUG( FD_LOG_DEBUG(( "one_rtt failed: no connection found" )) );
2279 0 : return FD_QUIC_PARSE_FAIL;
2280 0 : }
2281 :
2282 0 : if( FD_UNLIKELY( !fd_uint_extract_bit( conn->keys_avail, fd_quic_enc_level_appdata_id ) ) ) {
2283 0 : quic->metrics.pkt_no_key_cnt[ fd_quic_enc_level_appdata_id ]++;
2284 0 : return FD_QUIC_PARSE_FAIL;
2285 0 : }
2286 :
2287 : /* RFC 9001 Section 5.7: A server MUST NOT process incoming 1-RTT
2288 : protected packets before the TLS handshake is complete. */
2289 0 : if( FD_UNLIKELY( !conn->handshake_complete ) ) {
2290 0 : quic->metrics.pkt_no_key_cnt[ fd_quic_enc_level_appdata_id ]++;
2291 0 : return FD_QUIC_PARSE_FAIL;
2292 0 : }
2293 :
2294 0 : if( FD_UNLIKELY( tot_sz < (1+FD_QUIC_CONN_ID_SZ+1) ) ) {
2295 : /* One-RTT header: 1 byte
2296 : DCID: FD_QUIC_CONN_ID_SZ
2297 : Pkt number: 1-4 bytes */
2298 0 : quic->metrics.pkt_decrypt_fail_cnt[ fd_quic_enc_level_appdata_id ]++;
2299 0 : return FD_QUIC_PARSE_FAIL;
2300 0 : }
2301 0 : ulong pn_offset = 1UL + FD_QUIC_CONN_ID_SZ;
2302 :
2303 0 : pkt->enc_level = fd_quic_enc_level_appdata_id;
2304 :
2305 0 : # if !FD_QUIC_DISABLE_CRYPTO
2306 0 : if( FD_UNLIKELY(
2307 0 : fd_quic_crypto_decrypt_hdr( cur_ptr, tot_sz,
2308 0 : pn_offset,
2309 0 : &conn->keys[3][0] ) != FD_QUIC_SUCCESS ) ) {
2310 0 : FD_DEBUG( FD_LOG_DEBUG(( "fd_quic_crypto_decrypt_hdr failed" )) );
2311 0 : quic->metrics.pkt_decrypt_fail_cnt[ fd_quic_enc_level_appdata_id ]++;
2312 0 : return FD_QUIC_PARSE_FAIL;
2313 0 : }
2314 0 : # endif /* !FD_QUIC_DISABLE_CRYPTO */
2315 :
2316 0 : uint pkt_number_sz = fd_quic_h0_pkt_num_len( cur_ptr[0] ) + 1u;
2317 0 : uint key_phase = fd_quic_one_rtt_key_phase( cur_ptr[0] );
2318 :
2319 : /* reconstruct packet number */
2320 0 : ulong pktnum_comp = fd_quic_pktnum_decode( cur_ptr+pn_offset, pkt_number_sz );
2321 0 : ulong pkt_number = fd_quic_reconstruct_pkt_num( pktnum_comp, pkt_number_sz, conn->exp_pkt_number[2] );
2322 :
2323 : /* NOTE from rfc9002 s3
2324 : It is permitted for some packet numbers to never be used, leaving intentional gaps. */
2325 :
2326 : /* is current packet in the current key phase? */
2327 0 : int current_key_phase = conn->key_phase == key_phase;
2328 :
2329 0 : # if !FD_QUIC_DISABLE_CRYPTO
2330 : /* If the key phase bit flips, decrypt with the new pair of keys
2331 : instead. Note that the key phase bit is untrusted at this point. */
2332 0 : fd_quic_crypto_keys_t * keys = current_key_phase ? &conn->keys[3][0] : &conn->new_keys[0];
2333 :
2334 : /* this decrypts the header and payload */
2335 0 : if( FD_UNLIKELY(
2336 0 : fd_quic_crypto_decrypt( cur_ptr, tot_sz,
2337 0 : pn_offset,
2338 0 : pkt_number,
2339 0 : keys ) != FD_QUIC_SUCCESS ) ) {
2340 : /* remove connection from map, and insert into free list */
2341 0 : FD_DTRACE_PROBE_3( quic_err_decrypt_1rtt_pkt, pkt->ip4, conn->our_conn_id, pkt->pkt_number );
2342 0 : quic->metrics.pkt_decrypt_fail_cnt[ fd_quic_enc_level_appdata_id ]++;
2343 0 : return FD_QUIC_PARSE_FAIL;
2344 0 : }
2345 0 : # endif /* !FD_QUIC_DISABLE_CRYPTO */
2346 :
2347 : /* Reserved bits are authenticated; check only after payload decryption. */
2348 0 : if( FD_UNLIKELY( cur_ptr[0] & 0x18 ) ) {
2349 0 : fd_quic_conn_error( conn, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
2350 0 : return FD_QUIC_PARSE_FAIL;
2351 0 : }
2352 :
2353 : /* set packet number on the context */
2354 0 : pkt->pkt_number = pkt_number;
2355 :
2356 0 : if( FD_UNLIKELY( fd_quic_pkt_num_rx( conn, 2U, pkt_number ) ) ) {
2357 0 : quic->metrics.pkt_dup_cnt++;
2358 0 : return FD_QUIC_PARSE_FAIL;
2359 0 : }
2360 :
2361 0 : if( !current_key_phase ) {
2362 : /* RFC 9001 Section 6.2: If an endpoint detects a second update
2363 : before it has sent any packets with updated keys containing an
2364 : acknowledgment for the packet that initiated the key update, it
2365 : indicates that its peer has updated keys twice without awaiting
2366 : confirmation. An endpoint MAY treat such consecutive key updates
2367 : as a connection error of type KEY_UPDATE_ERROR.
2368 :
2369 : Enforcing this bounds the rate of (expensive) key derivations a
2370 : peer can trigger. Key updates initiated by us are exempt. */
2371 0 : if( FD_UNLIKELY( conn->key_phase_updated && !conn->key_update && !conn->key_phase_ack_sent ) ) {
2372 0 : FD_DEBUG( FD_LOG_DEBUG(( "peer initiated consecutive key updates" )) );
2373 0 : fd_quic_conn_error( conn, FD_QUIC_CONN_REASON_KEY_UPDATE_ERROR, __LINE__ );
2374 0 : return FD_QUIC_PARSE_FAIL;
2375 0 : }
2376 :
2377 : /* Decryption succeeded. Commit the key phase update and throw
2378 : away the old keys. (May cause a few decryption failures if old
2379 : packets get reordered past the current incoming packet) */
2380 0 : fd_quic_key_update_complete( conn );
2381 0 : }
2382 :
2383 : /* Notify the application before delivering the first 1-RTT frames. */
2384 0 : fd_quic_server_handshake_confirm( quic, conn );
2385 0 : if( FD_UNLIKELY( conn->state!=FD_QUIC_CONN_STATE_ACTIVE &&
2386 0 : conn->state!=FD_QUIC_CONN_STATE_HANDSHAKE_COMPLETE ) ) return FD_QUIC_PARSE_FAIL;
2387 :
2388 : /* handle frames */
2389 0 : ulong payload_off = pn_offset + pkt_number_sz;
2390 0 : uchar const * frame_ptr = cur_ptr + payload_off;
2391 0 : ulong payload_sz = tot_sz - pn_offset - pkt_number_sz; /* includes auth tag */
2392 0 : if( FD_UNLIKELY( payload_sz<FD_QUIC_CRYPTO_TAG_SZ ) ) return FD_QUIC_PARSE_FAIL;
2393 0 : ulong frame_sz = payload_sz - FD_QUIC_CRYPTO_TAG_SZ; /* total size of all frames in packet */
2394 :
2395 : /* Reject frame-less packets (RFC 9000 §12.4). */
2396 0 : if( FD_UNLIKELY( frame_sz==0UL ) ) {
2397 0 : fd_quic_conn_error( conn, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
2398 0 : return FD_QUIC_PARSE_FAIL;
2399 0 : }
2400 :
2401 0 : while( frame_sz != 0UL ) {
2402 0 : ulong rc = fd_quic_handle_v1_frame(
2403 0 : quic, conn, pkt, FD_QUIC_PKT_TYPE_ONE_RTT,
2404 0 : frame_ptr, frame_sz );
2405 0 : if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) {
2406 0 : FD_DEBUG( FD_LOG_DEBUG(( "Failed to handle frame (1-RTT, frame=0x%02x)", frame_ptr[0] )) );
2407 0 : quic->metrics.frame_rx_err_cnt++;
2408 0 : return FD_QUIC_PARSE_FAIL;
2409 0 : }
2410 :
2411 0 : if( FD_UNLIKELY( rc == 0UL || rc > frame_sz ) ) {
2412 0 : FD_LOG_WARNING(( "fd_quic_handle_v1_frame returned invalid size" ));
2413 0 : fd_quic_conn_error( conn, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
2414 0 : return FD_QUIC_PARSE_FAIL;
2415 0 : }
2416 :
2417 : /* next frame, and remaining size */
2418 0 : frame_ptr += rc;
2419 0 : frame_sz -= rc;
2420 0 : }
2421 :
2422 : /* update last activity */
2423 0 : conn->last_activity = state->now;
2424 :
2425 0 : return tot_sz;
2426 0 : }
2427 :
2428 :
2429 : static inline int
2430 : fd_quic_src_ip_allowed( fd_quic_conn_t const * conn,
2431 0 : uint src_ip ) {
2432 0 : if( !conn ) return 1;
2433 0 : if( FD_LIKELY( conn->peer[0].ip_addr==src_ip ) ) return 1;
2434 :
2435 0 : FD_DEBUG( FD_LOG_DEBUG(( "Rejected packet with non-sticky peer IPv4 address; conn=%u expected=" FD_IP4_ADDR_FMT " got=" FD_IP4_ADDR_FMT,
2436 0 : conn->conn_idx,
2437 0 : FD_IP4_ADDR_FMT_ARGS( conn->peer[0].ip_addr ),
2438 0 : FD_IP4_ADDR_FMT_ARGS( src_ip ) )); )
2439 0 : return 0;
2440 0 : }
2441 :
2442 : /* process v1 quic packets
2443 : returns number of bytes consumed, or FD_QUIC_PARSE_FAIL upon error */
2444 :
2445 : ulong
2446 : fd_quic_process_quic_packet_v1( fd_quic_t * quic,
2447 : fd_quic_pkt_t * pkt,
2448 : uchar * cur_ptr,
2449 0 : ulong cur_sz ) {
2450 :
2451 : /* bounds check packet size */
2452 0 : if( FD_UNLIKELY( cur_sz < FD_QUIC_SHORTEST_PKT ) ) {
2453 0 : quic->metrics.pkt_undersz_cnt++;
2454 0 : return FD_QUIC_PARSE_FAIL;
2455 0 : }
2456 0 : if( FD_UNLIKELY( cur_sz > 1500 ) ) {
2457 0 : quic->metrics.pkt_oversz_cnt++;
2458 0 : return FD_QUIC_PARSE_FAIL;
2459 0 : }
2460 :
2461 : /* This entry point handles QUIC v1; Version Negotiation is dispatched separately. */
2462 0 : if( FD_UNLIKELY( !(cur_ptr[0] & 0x40) ) ) return FD_QUIC_PARSE_FAIL;
2463 :
2464 0 : pkt->ack_flag = 0U;
2465 0 : pkt->rtt_pkt_number = 0UL;
2466 0 : pkt->rtt_ack_time = 0L;
2467 0 : pkt->rtt_ack_delay = 0UL;
2468 :
2469 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
2470 0 : fd_quic_conn_t * conn = NULL;
2471 :
2472 :
2473 : /* keep end */
2474 0 : uchar * orig_ptr = cur_ptr;
2475 :
2476 : /* No need for cur_sz check, since we are safe from the above check.
2477 : Decrementing cur_sz is done in the long header branch, the short header
2478 : branch parses the first byte again using the parser generator.
2479 : */
2480 0 : uchar hdr_form = fd_quic_h0_hdr_form( *cur_ptr );
2481 0 : ulong rc;
2482 :
2483 0 : if( hdr_form ) { /* long header */
2484 0 : fd_quic_long_hdr_t long_hdr[1];
2485 0 : rc = fd_quic_decode_long_hdr( long_hdr, cur_ptr+1, cur_sz-1 );
2486 0 : if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) {
2487 0 : FD_DEBUG( FD_LOG_DEBUG(( "fd_quic_decode_long_hdr failed" )); )
2488 0 : quic->metrics.pkt_quic_hdr_err_cnt++;
2489 0 : return FD_QUIC_PARSE_FAIL;
2490 0 : }
2491 :
2492 0 : fd_quic_conn_id_t dcid = fd_quic_conn_id_new( long_hdr->dst_conn_id, long_hdr->dst_conn_id_len );
2493 0 : if( dcid.sz == FD_QUIC_CONN_ID_SZ ) {
2494 0 : conn = fd_quic_conn_query( state->conn_map, fd_ulong_load_8( dcid.conn_id ) );
2495 0 : }
2496 0 : fd_quic_conn_id_t scid = fd_quic_conn_id_new( long_hdr->src_conn_id, long_hdr->src_conn_id_len );
2497 :
2498 0 : uchar long_packet_type = fd_quic_h0_long_packet_type( *cur_ptr );
2499 :
2500 : /* encryption level matches that of TLS */
2501 0 : pkt->enc_level = long_packet_type; /* V2 uses an indirect mapping */
2502 :
2503 : /* initialize packet number to unused value */
2504 0 : pkt->pkt_number = FD_QUIC_PKT_NUM_UNUSED;
2505 :
2506 0 : if( FD_UNLIKELY( !fd_quic_src_ip_allowed( conn, pkt->ip4->saddr ) ) ) {
2507 0 : quic->metrics.pkt_wrong_src_cnt++;
2508 0 : return FD_QUIC_PARSE_FAIL;
2509 0 : }
2510 :
2511 0 : switch( long_packet_type ) {
2512 0 : case FD_QUIC_PKT_TYPE_INITIAL:
2513 0 : rc = fd_quic_handle_v1_initial( quic, &conn, pkt, &dcid, &scid, cur_ptr, cur_sz );
2514 0 : if( FD_UNLIKELY( !conn ) ) {
2515 : /* FIXME not really a fail - Could be a retry */
2516 0 : return FD_QUIC_PARSE_FAIL;
2517 0 : }
2518 0 : break;
2519 0 : case FD_QUIC_PKT_TYPE_HANDSHAKE:
2520 0 : rc = fd_quic_handle_v1_handshake( quic, conn, pkt, cur_ptr, cur_sz );
2521 0 : break;
2522 0 : case FD_QUIC_PKT_TYPE_RETRY:
2523 0 : rc = fd_quic_handle_v1_retry( quic, conn, pkt, cur_ptr, cur_sz );
2524 0 : break;
2525 0 : case FD_QUIC_PKT_TYPE_ZERO_RTT:
2526 : /* fd_quic does not support 0-RTT */
2527 0 : rc = FD_QUIC_PARSE_FAIL;
2528 0 : break;
2529 0 : }
2530 :
2531 0 : fd_quic_svc_timers_schedule( state->svc_timers, conn, state->now );
2532 :
2533 0 : if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) {
2534 0 : FD_DEBUG( FD_LOG_DEBUG(( "Rejected packet (type=%d)", long_packet_type )); )
2535 0 : return FD_QUIC_PARSE_FAIL;
2536 0 : }
2537 :
2538 0 : } else { /* short header */
2539 : /* encryption level of short header packets is fd_quic_enc_level_appdata_id */
2540 0 : pkt->enc_level = fd_quic_enc_level_appdata_id;
2541 :
2542 : /* initialize packet number to unused value */
2543 0 : pkt->pkt_number = FD_QUIC_PKT_NUM_UNUSED;
2544 :
2545 : /* find connection id */
2546 0 : ulong dst_conn_id = fd_ulong_load_8( cur_ptr+1 );
2547 0 : conn = fd_quic_conn_query( state->conn_map, dst_conn_id );
2548 :
2549 0 : if( FD_UNLIKELY( !fd_quic_src_ip_allowed( conn, pkt->ip4->saddr ) ) ) {
2550 0 : quic->metrics.pkt_wrong_src_cnt++;
2551 0 : return FD_QUIC_PARSE_FAIL;
2552 0 : }
2553 :
2554 0 : rc = fd_quic_handle_v1_one_rtt( quic, conn, pkt, cur_ptr, cur_sz );
2555 :
2556 0 : fd_quic_svc_timers_schedule( state->svc_timers, conn, state->now );
2557 :
2558 0 : if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) {
2559 0 : return FD_QUIC_PARSE_FAIL;
2560 0 : }
2561 0 : }
2562 :
2563 0 : if( FD_UNLIKELY( rc == 0UL ) ) {
2564 : /* this is an error because it causes infinite looping */
2565 0 : return FD_QUIC_PARSE_FAIL;
2566 0 : }
2567 0 : cur_ptr += rc;
2568 :
2569 : /* if we get here we parsed all the frames, so ack the packet */
2570 0 : int ack_type = fd_quic_lazy_ack_pkt( quic, conn, pkt );
2571 0 : quic->metrics.ack_tx[ ack_type ]++;
2572 :
2573 : /* fd_quic_lazy_ack_pkt may have prepped schedule */
2574 0 : fd_quic_svc_timers_schedule( state->svc_timers, conn, state->now );
2575 :
2576 0 : if( pkt->rtt_ack_time ) {
2577 0 : fd_quic_sample_rtt( conn, (long)pkt->rtt_ack_time, (long)pkt->rtt_ack_delay );
2578 0 : }
2579 :
2580 : /* return bytes consumed */
2581 0 : return (ulong)( cur_ptr - orig_ptr );
2582 0 : }
2583 :
2584 :
2585 : /* version negotiation packet has version 0 */
2586 : static inline int
2587 0 : is_version_invalid( fd_quic_t * quic, uint version ) {
2588 0 : if( version == 0 ) {
2589 : /* TODO implement version negotiation */
2590 0 : quic->metrics.pkt_verneg_cnt++;
2591 0 : FD_DEBUG( FD_LOG_DEBUG(( "Got version negotiation packet" )) );
2592 0 : return 1;
2593 0 : }
2594 :
2595 : /* 0x?a?a?a?au is intended to force version negotiation
2596 : TODO implement */
2597 0 : if( ( version & 0x0a0a0a0au ) == 0x0a0a0a0au ) {
2598 : /* at present, ignore */
2599 0 : quic->metrics.pkt_verneg_cnt++;
2600 0 : FD_DEBUG( FD_LOG_DEBUG(( "Got version negotiation packet (forced)" )) );
2601 0 : return 1;
2602 0 : }
2603 :
2604 0 : if( version != 1 ) {
2605 : /* cannot interpret length, so discard entire packet */
2606 : /* TODO send version negotiation */
2607 0 : quic->metrics.pkt_verneg_cnt++;
2608 0 : FD_DEBUG( FD_LOG_DEBUG(( "Got unknown version QUIC packet" )) );
2609 0 : return 1;
2610 0 : }
2611 0 : return 0;
2612 0 : }
2613 :
2614 : static ulong
2615 : fd_quic_long_pkt_varint( uchar const * cur_ptr,
2616 : ulong cur_sz,
2617 0 : ulong * off ) {
2618 0 : if( FD_UNLIKELY( *off>=cur_sz ) ) return ULONG_MAX;
2619 0 : uint msb2 = (uint)cur_ptr[ *off ]>>6;
2620 0 : ulong vsz = 1UL<<msb2;
2621 0 : if( FD_UNLIKELY( *off+vsz>cur_sz ) ) return ULONG_MAX;
2622 0 : ulong val = fd_quic_varint_decode( cur_ptr+*off, msb2 );
2623 0 : *off += vsz;
2624 0 : return val;
2625 0 : }
2626 :
2627 : static ulong
2628 : fd_quic_long_pkt_sz( uchar const * cur_ptr,
2629 0 : ulong cur_sz ) {
2630 0 : uchar type = fd_quic_h0_long_packet_type( *cur_ptr );
2631 0 : if( FD_UNLIKELY( type==FD_QUIC_PKT_TYPE_RETRY ) ) return FD_QUIC_PARSE_FAIL; /* no Length field */
2632 0 : ulong off = 5UL; /* h0, version */
2633 0 : if( FD_UNLIKELY( off>=cur_sz ) ) return FD_QUIC_PARSE_FAIL;
2634 0 : off += 1UL+cur_ptr[ off ]; /* dcid */
2635 0 : if( FD_UNLIKELY( off>=cur_sz ) ) return FD_QUIC_PARSE_FAIL;
2636 0 : off += 1UL+cur_ptr[ off ]; /* scid */
2637 0 : if( type==FD_QUIC_PKT_TYPE_INITIAL ) { /* token */
2638 0 : ulong token_sz = fd_quic_long_pkt_varint( cur_ptr, cur_sz, &off );
2639 0 : if( FD_UNLIKELY( token_sz>cur_sz ) ) return FD_QUIC_PARSE_FAIL;
2640 0 : off += token_sz;
2641 0 : }
2642 0 : ulong len = fd_quic_long_pkt_varint( cur_ptr, cur_sz, &off );
2643 0 : if( FD_UNLIKELY( len>cur_sz || off+len>cur_sz ) ) return FD_QUIC_PARSE_FAIL;
2644 0 : return off+len;
2645 0 : }
2646 :
2647 : static inline void
2648 : fd_quic_process_packet_impl( fd_quic_t * quic,
2649 : uchar * data,
2650 : ulong data_sz,
2651 0 : long now ) {
2652 0 : fd_quic_get_state( quic )->now = now;
2653 0 : quic->metrics.net_rx_byte_cnt += data_sz;
2654 0 : quic->metrics.net_rx_pkt_cnt++;
2655 :
2656 0 : ulong rc = 0;
2657 :
2658 : /* holds the remainder of the packet*/
2659 0 : uchar * cur_ptr = data;
2660 0 : ulong cur_sz = data_sz;
2661 :
2662 0 : if( FD_UNLIKELY( data_sz > 0xffffu ) ) {
2663 0 : FD_DTRACE_PROBE( quic_err_rx_oversz );
2664 0 : quic->metrics.pkt_oversz_cnt++;
2665 0 : return;
2666 0 : }
2667 :
2668 0 : fd_quic_pkt_t pkt = { .datagram_sz = (uint)data_sz };
2669 :
2670 0 : pkt.rcv_time = now;
2671 0 : pkt.rtt_pkt_number = 0;
2672 0 : pkt.rtt_ack_time = 0;
2673 :
2674 : /* parse ip, udp */
2675 :
2676 0 : rc = fd_quic_decode_ip4( pkt.ip4, cur_ptr, cur_sz );
2677 0 : if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) {
2678 : /* TODO count failure */
2679 0 : FD_DTRACE_PROBE( quic_err_rx_net_hdr );
2680 0 : quic->metrics.pkt_net_hdr_err_cnt++;
2681 0 : FD_DEBUG( FD_LOG_DEBUG(( "fd_quic_decode_ip4 failed" )) );
2682 0 : return;
2683 0 : }
2684 :
2685 : /* check version, tot_len, protocol, checksum? */
2686 0 : if( FD_UNLIKELY( pkt.ip4->protocol != FD_IP4_HDR_PROTOCOL_UDP ) ) {
2687 0 : FD_DTRACE_PROBE( quic_err_rx_net_hdr );
2688 0 : quic->metrics.pkt_net_hdr_err_cnt++;
2689 0 : FD_DEBUG( FD_LOG_DEBUG(( "Packet is not UDP" )) );
2690 0 : return;
2691 0 : }
2692 :
2693 : /* verify ip4 packet isn't truncated
2694 : * AF_XDP can silently do this */
2695 0 : if( FD_UNLIKELY( pkt.ip4->net_tot_len > cur_sz ) ) {
2696 0 : FD_DTRACE_PROBE( quic_err_rx_net_hdr );
2697 0 : quic->metrics.pkt_net_hdr_err_cnt++;
2698 0 : FD_DEBUG( FD_LOG_DEBUG(( "IPv4 header indicates truncation" )) );
2699 0 : return;
2700 0 : }
2701 :
2702 : /* update pointer + size */
2703 0 : cur_ptr += rc;
2704 0 : cur_sz -= rc;
2705 :
2706 0 : rc = fd_quic_decode_udp( pkt.udp, cur_ptr, cur_sz );
2707 0 : if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) {
2708 : /* TODO count failure */
2709 0 : FD_DTRACE_PROBE( quic_err_rx_net_hdr );
2710 0 : quic->metrics.pkt_net_hdr_err_cnt++;
2711 0 : FD_DEBUG( FD_LOG_DEBUG(( "fd_quic_decode_udp failed" )) );
2712 0 : return;
2713 0 : }
2714 :
2715 : /* sanity check udp length */
2716 0 : if( FD_UNLIKELY( pkt.udp->net_len < sizeof(fd_udp_hdr_t) ||
2717 0 : pkt.udp->net_len > cur_sz ) ) {
2718 0 : FD_DTRACE_PROBE( quic_err_rx_net_hdr );
2719 0 : quic->metrics.pkt_net_hdr_err_cnt++;
2720 0 : FD_DEBUG( FD_LOG_DEBUG(( "UDP header indicates truncation" )) );
2721 0 : return;
2722 0 : }
2723 :
2724 : /* update pointer + size */
2725 0 : cur_ptr += rc;
2726 0 : cur_sz = pkt.udp->net_len - rc; /* replace with udp length */
2727 :
2728 : /* cur_ptr[0..cur_sz-1] should be payload */
2729 :
2730 : /* filter */
2731 : /* check dst eth address, ip address? probably not necessary */
2732 : /* usually look up port here, but let's jump straight into decoding as-if
2733 : quic */
2734 :
2735 : /* update counters */
2736 :
2737 : /* shortest valid quic payload? */
2738 0 : if( FD_UNLIKELY( cur_sz < FD_QUIC_SHORTEST_PKT ) ) {
2739 0 : FD_DTRACE_PROBE( quic_err_rx_net_hdr );
2740 0 : quic->metrics.pkt_net_hdr_err_cnt++;
2741 0 : FD_DEBUG( FD_LOG_DEBUG(( "Undersize QUIC packet" )) );
2742 0 : return;
2743 0 : }
2744 :
2745 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
2746 :
2747 : /* short packets don't have version */
2748 0 : int long_pkt = !!( (uint)cur_ptr[0] & 0x80u );
2749 :
2750 0 : if( long_pkt ) {
2751 : /* version at offset 1..4 */
2752 0 : uint version = fd_uint_bswap( FD_LOAD( uint, cur_ptr + 1 ) );
2753 : /* we only support version 1 */
2754 0 : if( FD_UNLIKELY( is_version_invalid( quic, version ) ) ) {
2755 0 : return;
2756 0 : }
2757 :
2758 : /* multiple QUIC packets in a UDP packet */
2759 : /* shortest valid quic payload? */
2760 0 : uchar const * dcid0 = cur_ptr+6;
2761 0 : ulong dcid0_sz = cur_ptr[5];
2762 0 : ulong pkt_idx;
2763 0 : for( pkt_idx=0UL; pkt_idx<FD_QUIC_PKT_COALESCE_LIMIT; pkt_idx++ ) {
2764 : /* Are we done? Omit short packet handling that follows */
2765 0 : if( FD_UNLIKELY( cur_sz < FD_QUIC_SHORTEST_PKT ) ) return;
2766 :
2767 : /* short packet requires different handling */
2768 0 : int short_pkt = !( (uint)cur_ptr[0] & 0x80u );
2769 :
2770 0 : if( FD_UNLIKELY( short_pkt ) ) break;
2771 :
2772 : /* check version */
2773 0 : uint cur_version = fd_uint_bswap( FD_LOAD( uint, cur_ptr + 1 ) );
2774 :
2775 0 : if( cur_version != version ) {
2776 : /* multiple versions in a single connection is a violation, and by
2777 : extension so is multiple versions in a single udp datagram
2778 : these are silently ignored
2779 :
2780 : for reference
2781 : all quic packets in a udp datagram must be for the same connection id
2782 : (section 12.2) and therefore the same connection
2783 : all packets on a connection must be of the same version (5.2) */
2784 0 : quic->metrics.pkt_quic_hdr_err_cnt++;
2785 0 : FD_DEBUG( FD_LOG_DEBUG(( "Mixed QUIC versions in packet" )) );
2786 0 : return;
2787 0 : }
2788 :
2789 : /* RFC 9000 Section 12.2: ignore coalesced packets with a different DCID */
2790 0 : if( FD_UNLIKELY( cur_ptr[5]!=dcid0_sz || 6UL+dcid0_sz>cur_sz ||
2791 0 : ( pkt_idx && memcmp( cur_ptr+6, dcid0, dcid0_sz ) ) ) ) {
2792 0 : quic->metrics.pkt_quic_hdr_err_cnt++;
2793 0 : return;
2794 0 : }
2795 :
2796 0 : rc = fd_quic_process_quic_packet_v1( quic, &pkt, cur_ptr, cur_sz );
2797 0 : svc_cnt_eq_alloc_conn( state->svc_timers, quic );
2798 0 : if( FD_UNLIKELY( pkt.retry_sent ) ) return;
2799 :
2800 : /* RFC 9000 Section 12.2: a packet that fails (e.g. Handshake after
2801 : its keys are discarded) does not void the rest of the datagram.
2802 : Skip it by its Length field. */
2803 0 : if( FD_UNLIKELY( rc==FD_QUIC_PARSE_FAIL ) ) rc = fd_quic_long_pkt_sz( cur_ptr, cur_sz );
2804 :
2805 : /* 0UL means no progress, so fail */
2806 0 : if( FD_UNLIKELY( ( rc == FD_QUIC_PARSE_FAIL ) |
2807 0 : ( rc == 0UL ) ) ) {
2808 0 : FD_DEBUG( FD_LOG_DEBUG(( "fd_quic_process_quic_packet_v1 failed (stuck=%d)", rc==0UL )) );
2809 0 : return;
2810 0 : }
2811 :
2812 0 : if( FD_UNLIKELY( rc > cur_sz ) ) {
2813 0 : FD_DEBUG( FD_LOG_WARNING(( "fd_quic_process_quic_packet_v1 read too much" )) );
2814 0 : return;
2815 0 : }
2816 :
2817 : /* return code (rc) is the number of bytes consumed */
2818 0 : cur_sz -= rc;
2819 0 : cur_ptr += rc;
2820 0 : }
2821 0 : if( pkt_idx==FD_QUIC_PKT_COALESCE_LIMIT ) {
2822 : /* too many packets in a single udp datagram */
2823 0 : return;
2824 0 : }
2825 0 : }
2826 :
2827 : /* above can drop out of loop if a short packet is detected */
2828 0 : if( FD_UNLIKELY( cur_sz < FD_QUIC_SHORTEST_PKT ) ) return;
2829 :
2830 : /* short header packet
2831 : only one_rtt packets currently have short headers */
2832 0 : fd_quic_process_quic_packet_v1( quic, &pkt, cur_ptr, cur_sz );
2833 0 : svc_cnt_eq_alloc_conn( state->svc_timers, quic );
2834 0 : }
2835 :
2836 : void
2837 : fd_quic_process_packet( fd_quic_t * quic,
2838 : uchar * data,
2839 : ulong data_sz,
2840 0 : long now ) {
2841 0 : long now_ticks = fd_tickcount();
2842 0 : fd_quic_process_packet_impl( quic, data, data_sz, now );
2843 0 : long delta_ticks = fd_tickcount() - now_ticks;
2844 0 : fd_histf_sample( quic->metrics.receive_duration, (ulong)delta_ticks );
2845 0 : }
2846 :
2847 : /* main receive-side entry point */
2848 : int
2849 : fd_quic_aio_cb_receive( void * context,
2850 : fd_aio_pkt_info_t const * batch,
2851 : ulong batch_cnt,
2852 : ulong * opt_batch_idx,
2853 0 : int flush ) {
2854 0 : (void)flush;
2855 :
2856 0 : fd_quic_t * quic = context;
2857 0 : long now = fd_quic_get_state( quic )->now;
2858 :
2859 : /* this aio interface is configured as one-packet per buffer
2860 : so batch[0] refers to one buffer
2861 : as such, we simply forward each individual packet to a handling function */
2862 0 : for( ulong j = 0; j < batch_cnt; ++j ) {
2863 0 : fd_quic_process_packet( quic, batch[ j ].buf, batch[ j ].buf_sz, now );
2864 0 : }
2865 :
2866 : /* the assumption here at present is that any packet that could not be processed
2867 : is simply dropped
2868 : hence, all packets were consumed */
2869 0 : if( FD_LIKELY( opt_batch_idx ) ) {
2870 0 : *opt_batch_idx = batch_cnt;
2871 0 : }
2872 :
2873 0 : return FD_AIO_SUCCESS;
2874 0 : }
2875 :
2876 : void
2877 : fd_quic_tls_cb_alert( fd_quic_tls_hs_t * hs,
2878 : void * context,
2879 0 : int alert ) {
2880 0 : (void)hs;
2881 0 : fd_quic_conn_t * conn = (fd_quic_conn_t *)context;
2882 0 : (void)conn;
2883 0 : (void)alert;
2884 0 : FD_DEBUG( FD_LOG_DEBUG(( "TLS callback: %s", conn->server ? "SERVER" : "CLIENT" ));
2885 0 : FD_LOG_DEBUG(( "TLS alert: (%d-%s)", alert, fd_tls_alert_cstr( (uint)alert ) )); );
2886 :
2887 : /* TODO store alert to reply to peer */
2888 0 : }
2889 :
2890 : void
2891 : fd_quic_tls_cb_secret( fd_quic_tls_hs_t * hs,
2892 : void * context,
2893 0 : fd_quic_tls_secret_t const * secret ) {
2894 :
2895 0 : fd_quic_conn_t * conn = (fd_quic_conn_t*)context;
2896 0 : fd_quic_t * quic = conn->quic;
2897 :
2898 : /* look up suite */
2899 : /* set secrets */
2900 0 : FD_TEST( secret->enc_level < FD_QUIC_NUM_ENC_LEVELS );
2901 :
2902 0 : uint enc_level = secret->enc_level;
2903 :
2904 0 : fd_quic_crypto_secrets_t * crypto_secret = &conn->secrets;
2905 :
2906 0 : memcpy( crypto_secret->secret[enc_level][0], secret->read_secret, FD_QUIC_SECRET_SZ );
2907 0 : memcpy( crypto_secret->secret[enc_level][1], secret->write_secret, FD_QUIC_SECRET_SZ );
2908 :
2909 0 : conn->keys_avail = fd_uint_set_bit( conn->keys_avail, (int)enc_level );
2910 :
2911 : /* gen local keys */
2912 0 : fd_quic_gen_keys(
2913 0 : &conn->keys[enc_level][0],
2914 0 : conn->secrets.secret[enc_level][0] );
2915 :
2916 : /* gen peer keys */
2917 0 : fd_quic_gen_keys(
2918 0 : &conn->keys[enc_level][1],
2919 0 : conn->secrets.secret[enc_level][1] );
2920 :
2921 0 : if( enc_level==fd_quic_enc_level_appdata_id ) {
2922 0 : fd_quic_key_update_derive( &conn->secrets, conn->new_keys );
2923 0 : }
2924 :
2925 : /* Key logging */
2926 :
2927 0 : void * keylog_ctx = quic->cb.quic_ctx;
2928 0 : fd_quic_cb_tls_keylog_t keylog_fn = quic->cb.tls_keylog;
2929 0 : if( FD_UNLIKELY( keylog_fn ) ) {
2930 : /* Ignore stdout, stderr, stdin */
2931 :
2932 0 : uchar const * recv_secret = secret->read_secret;
2933 0 : uchar const * send_secret = secret->write_secret;
2934 :
2935 0 : uchar const * client_secret = hs->is_server ? recv_secret : send_secret;
2936 0 : uchar const * server_secret = hs->is_server ? send_secret : recv_secret;
2937 :
2938 0 : char buf[256];
2939 0 : char * s;
2940 0 : switch( enc_level ) {
2941 0 : case FD_TLS_LEVEL_HANDSHAKE:
2942 0 : /* 0 chars */ s = fd_cstr_init( buf );
2943 0 : /* 0+32 chars */ s = fd_cstr_append_cstr( s, "CLIENT_HANDSHAKE_TRAFFIC_SECRET " );
2944 0 : /* 32+64 chars */ s = fd_hex_encode( s, hs->hs.base.client_random, 32UL );
2945 0 : /* 96+ 1 chars */ s = fd_cstr_append_char( s, ' ' );
2946 0 : /* 97+64 chars */ s = fd_hex_encode( s, client_secret, 32UL );
2947 0 : /* 161 chars */ fd_cstr_fini( s );
2948 0 : keylog_fn( keylog_ctx, buf );
2949 0 : /* 0 chars */ s = fd_cstr_init( buf );
2950 0 : /* 0+32 chars */ s = fd_cstr_append_cstr( s, "SERVER_HANDSHAKE_TRAFFIC_SECRET " );
2951 0 : /* 32+64 chars */ s = fd_hex_encode( s, hs->hs.base.client_random, 32UL );
2952 0 : /* 96+ 1 chars */ s = fd_cstr_append_char( s, ' ' );
2953 0 : /* 97+64 chars */ s = fd_hex_encode( s, server_secret, 32UL );
2954 0 : /* 161 chars */ fd_cstr_fini( s );
2955 0 : keylog_fn( keylog_ctx, buf );
2956 0 : break;
2957 0 : case FD_TLS_LEVEL_APPLICATION:
2958 0 : /* 0 chars */ s = fd_cstr_init( buf );
2959 0 : /* 0+24 chars */ s = fd_cstr_append_cstr( s, "CLIENT_TRAFFIC_SECRET_0 " );
2960 0 : /* 24+64 chars */ s = fd_hex_encode( s, hs->hs.base.client_random, 32UL );
2961 0 : /* 88+ 1 chars */ s = fd_cstr_append_char( s, ' ' );
2962 0 : /* 89+64 chars */ s = fd_hex_encode( s, client_secret, 32UL );
2963 0 : /* 153 chars */ fd_cstr_fini( s );
2964 0 : keylog_fn( keylog_ctx, buf );
2965 0 : /* 0 chars */ s = fd_cstr_init( buf );
2966 0 : /* 0+24 chars */ s = fd_cstr_append_cstr( s, "SERVER_TRAFFIC_SECRET_0 " );
2967 0 : /* 24+64 chars */ s = fd_hex_encode( s, hs->hs.base.client_random, 32UL );
2968 0 : /* 88+ 1 chars */ s = fd_cstr_append_char( s, ' ' );
2969 0 : /* 89+64 chars */ s = fd_hex_encode( s, server_secret, 32UL );
2970 0 : /* 153 chars */ fd_cstr_fini( s );
2971 0 : keylog_fn( keylog_ctx, buf );
2972 0 : break;
2973 0 : }
2974 0 : }
2975 :
2976 0 : }
2977 :
2978 : void
2979 : fd_quic_apply_peer_params( fd_quic_conn_t * conn,
2980 0 : fd_quic_transport_params_t const * peer_tp ) {
2981 : /* flow control parameters */
2982 0 : conn->tx_max_data = peer_tp->initial_max_data;
2983 0 : conn->tx_initial_max_stream_data_uni= peer_tp->initial_max_stream_data_uni;
2984 :
2985 0 : if( !conn->server ) {
2986 : /* verify retry_src_conn_id */
2987 0 : uint retry_src_conn_id_sz = conn->retry_src_conn_id.sz;
2988 0 : if( retry_src_conn_id_sz ) {
2989 0 : if( FD_UNLIKELY( !peer_tp->retry_source_connection_id_present
2990 0 : || peer_tp->retry_source_connection_id_len != retry_src_conn_id_sz
2991 0 : || 0 != memcmp( peer_tp->retry_source_connection_id,
2992 0 : conn->retry_src_conn_id.conn_id,
2993 0 : retry_src_conn_id_sz ) ) ) {
2994 0 : fd_quic_conn_error( conn, FD_QUIC_CONN_REASON_TRANSPORT_PARAMETER_ERROR, __LINE__ );
2995 0 : return;
2996 0 : }
2997 0 : } else {
2998 0 : if( FD_UNLIKELY( peer_tp->retry_source_connection_id_present ) ) {
2999 0 : fd_quic_conn_error( conn, FD_QUIC_CONN_REASON_TRANSPORT_PARAMETER_ERROR, __LINE__ );
3000 0 : return;
3001 0 : }
3002 0 : }
3003 0 : }
3004 :
3005 : /* max datagram size */
3006 0 : ulong tx_max_datagram_sz = peer_tp->max_udp_payload_size;
3007 0 : if( tx_max_datagram_sz < FD_QUIC_INITIAL_PAYLOAD_SZ_MAX ) {
3008 0 : tx_max_datagram_sz = FD_QUIC_INITIAL_PAYLOAD_SZ_MAX;
3009 0 : }
3010 0 : if( tx_max_datagram_sz > FD_QUIC_INITIAL_PAYLOAD_SZ_MAX ) {
3011 0 : tx_max_datagram_sz = FD_QUIC_INITIAL_PAYLOAD_SZ_MAX;
3012 0 : }
3013 0 : conn->tx_max_datagram_sz = (uint)tx_max_datagram_sz;
3014 0 : conn->tx_max_datagram_frame_sz = fd_ulong_if(
3015 0 : peer_tp->max_datagram_frame_size_present,
3016 0 : peer_tp->max_datagram_frame_size,
3017 0 : 0UL );
3018 :
3019 : /* initial max_streams */
3020 :
3021 0 : if( conn->server ) {
3022 0 : conn->tx_sup_stream_id = ( (ulong)peer_tp->initial_max_streams_uni << 2UL ) + FD_QUIC_STREAM_TYPE_UNI_SERVER;
3023 0 : } else {
3024 0 : conn->tx_sup_stream_id = ( (ulong)peer_tp->initial_max_streams_uni << 2UL ) + FD_QUIC_STREAM_TYPE_UNI_CLIENT;
3025 0 : }
3026 :
3027 : /* set the max_idle_timeout to the min of our and peer max_idle_timeout */
3028 0 : if( peer_tp->max_idle_timeout_ms ) {
3029 0 : long peer_max_idle_timeout_ns = fd_long_sat_mul( (long)peer_tp->max_idle_timeout_ms, (long)1e6);
3030 0 : conn->idle_timeout_ns = fd_long_min( peer_max_idle_timeout_ns, conn->idle_timeout_ns );
3031 0 : }
3032 :
3033 : /* set ack_delay_exponent so we can properly interpret peer's ack_delays
3034 : if unspecified, the value is 3 */
3035 0 : ulong peer_ack_delay_exponent = fd_ulong_if(
3036 0 : peer_tp->ack_delay_exponent_present,
3037 0 : peer_tp->ack_delay_exponent,
3038 0 : 3UL );
3039 :
3040 0 : conn->peer_ack_delay_scale = (float)( 1UL << peer_ack_delay_exponent ) * 1e3f;
3041 :
3042 : /* peer max ack delay in microseconds
3043 : peer_tp->max_ack_delay is milliseconds */
3044 0 : float peer_max_ack_delay_us = (float)fd_ulong_if(
3045 0 : peer_tp->max_ack_delay_present,
3046 0 : peer_tp->max_ack_delay * 1000UL,
3047 0 : 25000UL );
3048 0 : conn->peer_max_ack_delay_ns = peer_max_ack_delay_us * 1e3f;
3049 :
3050 0 : conn->transport_params_set = 1;
3051 0 : }
3052 :
3053 : void
3054 : fd_quic_tls_cb_peer_params( void * context,
3055 : uchar const * peer_tp_enc,
3056 0 : ulong peer_tp_enc_sz ) {
3057 0 : fd_quic_conn_t * conn = (fd_quic_conn_t*)context;
3058 :
3059 : /* decode peer transport parameters */
3060 0 : fd_quic_transport_params_t peer_tp[1] = {0};
3061 0 : int rc = fd_quic_decode_transport_params( peer_tp, peer_tp_enc, peer_tp_enc_sz );
3062 0 : if( FD_UNLIKELY( rc != 0 ) ) {
3063 0 : FD_DEBUG( FD_LOG_NOTICE(( "fd_quic_decode_transport_params failed" )); )
3064 : /* failed to parse transport params */
3065 0 : fd_quic_conn_error( conn, FD_QUIC_CONN_REASON_TRANSPORT_PARAMETER_ERROR, __LINE__ );
3066 0 : return;
3067 0 : }
3068 :
3069 0 : fd_quic_apply_peer_params( conn, peer_tp );
3070 0 : }
3071 :
3072 : void
3073 : fd_quic_tls_cb_handshake_complete( fd_quic_tls_hs_t * hs,
3074 0 : void * context ) {
3075 0 : (void)hs;
3076 0 : fd_quic_conn_t * conn = (fd_quic_conn_t *)context;
3077 :
3078 : /* need to send quic handshake completion */
3079 0 : switch( conn->state ) {
3080 0 : case FD_QUIC_CONN_STATE_ABORT:
3081 0 : case FD_QUIC_CONN_STATE_CLOSE_PENDING:
3082 0 : case FD_QUIC_CONN_STATE_PEER_CLOSE:
3083 0 : case FD_QUIC_CONN_STATE_DEAD:
3084 : /* ignore */
3085 0 : return;
3086 :
3087 0 : case FD_QUIC_CONN_STATE_HANDSHAKE:
3088 0 : if( FD_UNLIKELY( !conn->transport_params_set ) ) { /* unreachable */
3089 0 : FD_LOG_WARNING(( "Handshake marked as completed but transport params are not set. This is a bug!" ));
3090 0 : fd_quic_conn_error( conn, FD_QUIC_CONN_REASON_INTERNAL_ERROR, __LINE__ );
3091 0 : return;
3092 0 : }
3093 0 : conn->handshake_complete = 1;
3094 0 : fd_quic_set_conn_state( conn, FD_QUIC_CONN_STATE_HANDSHAKE_COMPLETE );
3095 0 : return;
3096 :
3097 0 : default:
3098 0 : FD_LOG_WARNING(( "handshake in unexpected state: %u", conn->state ));
3099 0 : }
3100 0 : }
3101 :
3102 : static ulong
3103 : fd_quic_handle_crypto_frame( fd_quic_frame_ctx_t * context,
3104 : fd_quic_crypto_frame_t * crypto,
3105 : uchar const * p,
3106 0 : ulong p_sz ) {
3107 : /* determine whether any of the data was already provided */
3108 0 : fd_quic_conn_t * conn = context->conn;
3109 0 : fd_quic_tls_hs_t * tls_hs = conn->tls_hs;
3110 0 : uint enc_level = context->pkt->enc_level;
3111 :
3112 : /* offset expected */
3113 0 : ulong rcv_off = crypto->offset; /* in [0,2^62-1] */
3114 0 : ulong rcv_sz = crypto->length; /* in [0,2^62-1] */
3115 0 : ulong rcv_hi = rcv_off + rcv_sz; /* in [0,2^63-1] */
3116 :
3117 0 : if( FD_UNLIKELY( rcv_sz > p_sz ) ) {
3118 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_FRAME_ENCODING_ERROR, __LINE__ );
3119 0 : return FD_QUIC_PARSE_FAIL;
3120 0 : }
3121 :
3122 0 : if( enc_level==fd_quic_enc_level_appdata_id && conn->handshake_complete ) {
3123 0 : if( rcv_off>conn->post_hs_rx_off ) {
3124 0 : context->pkt->ack_flag |= ACK_FLAG_CANCEL;
3125 0 : return rcv_sz;
3126 0 : }
3127 0 : ulong off = conn->post_hs_rx_off;
3128 0 : while( off<rcv_hi ) {
3129 0 : if( conn->post_hs_remaining ) {
3130 0 : uint skip = (uint)fd_ulong_min( conn->post_hs_remaining, rcv_hi-off );
3131 0 : conn->post_hs_remaining -= skip;
3132 0 : off += skip;
3133 0 : continue;
3134 0 : }
3135 0 : uchar byte = p[off-rcv_off];
3136 0 : if( !conn->post_hs_hdr_used ) {
3137 : /* Only clients can receive NewSessionTicket. QUIC forbids
3138 : KeyUpdate (RFC 9001 Section 6). */
3139 0 : if( conn->server || byte!=FD_TLS_MSG_NEW_SESSION_TICKET ) {
3140 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_CRYPTO_BASE+FD_TLS_ALERT_UNEXPECTED_MESSAGE, __LINE__ );
3141 0 : return FD_QUIC_PARSE_FAIL;
3142 0 : }
3143 0 : } else {
3144 0 : conn->post_hs_length = (conn->post_hs_length<<8) | byte;
3145 0 : }
3146 0 : off++;
3147 0 : if( ++conn->post_hs_hdr_used==4U ) {
3148 0 : conn->post_hs_remaining = conn->post_hs_length;
3149 0 : conn->post_hs_length = 0U;
3150 0 : conn->post_hs_hdr_used = 0U;
3151 0 : }
3152 0 : }
3153 0 : conn->post_hs_rx_off = off;
3154 0 : return rcv_sz;
3155 0 : }
3156 :
3157 0 : if( !tls_hs ) {
3158 : /* Handshake already completed. Ignore frame */
3159 : /* TODO consider aborting conn if too many unsolicited crypto frames arrive */
3160 0 : return rcv_sz;
3161 0 : }
3162 :
3163 0 : if( enc_level < tls_hs->rx_enc_level ) {
3164 0 : return rcv_sz;
3165 0 : }
3166 :
3167 0 : if( enc_level > tls_hs->rx_enc_level ) {
3168 : /* Discard data from any previous handshake level. Currently only
3169 : happens at the Initial->Handshake encryption level change. */
3170 0 : tls_hs->rx_enc_level = (uchar)enc_level;
3171 0 : tls_hs->rx_off = 0;
3172 0 : tls_hs->rx_sz = 0;
3173 0 : }
3174 :
3175 0 : if( rcv_off > tls_hs->rx_sz ) {
3176 0 : context->pkt->ack_flag |= ACK_FLAG_CANCEL;
3177 0 : return rcv_sz;
3178 0 : }
3179 :
3180 0 : if( rcv_hi < tls_hs->rx_off ) {
3181 0 : return rcv_sz;
3182 0 : }
3183 :
3184 0 : if( rcv_hi > FD_QUIC_TLS_RX_DATA_SZ ) {
3185 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_CRYPTO_BUFFER_EXCEEDED, __LINE__ );
3186 0 : return FD_QUIC_PARSE_FAIL;
3187 0 : }
3188 :
3189 0 : if( rcv_hi > tls_hs->rx_sz ) tls_hs->rx_sz = (ushort)rcv_hi;
3190 0 : fd_memcpy( tls_hs->rx_hs_buf + rcv_off, p, rcv_sz );
3191 :
3192 0 : int provide_rc = fd_quic_tls_process( conn->tls_hs );
3193 0 : if( provide_rc == FD_QUIC_FAILED ) {
3194 : /* if TLS fails, ABORT connection */
3195 :
3196 : /* if TLS returns an error, we present that as reason:
3197 : FD_QUIC_CONN_REASON_CRYPTO_BASE + tls-alert
3198 : otherwise, send INTERNAL_ERROR */
3199 0 : uint alert = conn->tls_hs->alert;
3200 0 : uint reason = conn->tls_hs->hs.base.reason;
3201 0 : FD_DTRACE_PROBE_3( quic_handle_crypto_frame, conn->our_conn_id, alert, reason );
3202 0 : if( reason==FD_TLS_REASON_CH_SESSION_ID ) {
3203 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
3204 0 : } else if( alert == 0u ) {
3205 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_INTERNAL_ERROR, __LINE__ );
3206 0 : } else {
3207 0 : FD_DEBUG(
3208 0 : FD_LOG_DEBUG(( "QUIC TLS handshake failed (alert %u-%s; reason %u-%s)",
3209 0 : alert, fd_tls_alert_cstr( alert ),
3210 0 : reason, fd_tls_reason_cstr( reason ) ));
3211 0 : )
3212 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_CRYPTO_BASE + alert, __LINE__ );
3213 0 : }
3214 0 : return FD_QUIC_PARSE_FAIL;
3215 0 : }
3216 :
3217 0 : return rcv_sz;
3218 0 : }
3219 :
3220 : static int
3221 : fd_quic_svc_poll( fd_quic_t * quic,
3222 : fd_quic_conn_t * conn,
3223 0 : long now ) {
3224 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
3225 0 : if( FD_UNLIKELY( conn->state == FD_QUIC_CONN_STATE_INVALID ) ) {
3226 : /* connection shouldn't have been scheduled,
3227 : and is now removed, so just continue */
3228 0 : FD_LOG_CRIT(( "Invalid conn in schedule" ));
3229 0 : return 1;
3230 0 : }
3231 :
3232 0 : if( FD_UNLIKELY( now >= conn->last_activity + ( conn->idle_timeout_ns / 2 ) ) ) {
3233 0 : if( FD_UNLIKELY( now >= conn->last_activity + conn->idle_timeout_ns ) ) {
3234 0 : if( FD_LIKELY( conn->state != FD_QUIC_CONN_STATE_DEAD ) ) {
3235 : /* rfc9000 10.1 Idle Timeout
3236 : "... the connection is silently closed and its state is discarded
3237 : when it remains idle for longer than the minimum of the
3238 : max_idle_timeout value advertised by both endpoints." */
3239 0 : FD_DEBUG( FD_LOG_WARNING(("%s conn %p conn_idx: %u closing due to idle timeout=%gms last_activity=%ld now=%ld",
3240 0 : conn->server?"SERVER":"CLIENT",
3241 0 : (void *)conn, conn->conn_idx,
3242 0 : (double)conn->idle_timeout_ns / 1e6,
3243 0 : conn->last_activity,
3244 0 : now
3245 0 : )); )
3246 :
3247 0 : fd_quic_set_conn_state( conn, FD_QUIC_CONN_STATE_DEAD );
3248 0 : quic->metrics.conn_timeout_cnt++;
3249 0 : }
3250 0 : } else if( quic->config.keep_alive & !!(conn->let_die_time_ns > now) ) {
3251 : /* send PING */
3252 0 : if( !( conn->flags & ( FD_QUIC_CONN_FLAGS_PING | FD_QUIC_CONN_FLAGS_PING_SENT ) ) ) {
3253 0 : conn->flags |= FD_QUIC_CONN_FLAGS_PING;
3254 0 : conn->upd_pkt_number = FD_QUIC_PKT_NUM_PENDING; /* update to be sent in next packet */
3255 0 : }
3256 0 : }
3257 0 : }
3258 :
3259 0 : if( FD_UNLIKELY( conn->state == FD_QUIC_CONN_STATE_DEAD ) ) {
3260 0 : fd_quic_cb_conn_final( quic, conn ); /* inform user before freeing */
3261 0 : fd_quic_conn_free( quic, conn );
3262 0 : return 1; /* do NOT reschedule freed connection */
3263 0 : }
3264 :
3265 : /* state cannot be DEAD here */
3266 0 : fd_quic_conn_service( quic, conn, now );
3267 :
3268 : /* dead? don't reinsert, just clean up */
3269 0 : switch( conn->state ) {
3270 0 : case FD_QUIC_CONN_STATE_INVALID:
3271 : /* skip entirely */
3272 0 : break;
3273 0 : case FD_QUIC_CONN_STATE_DEAD:
3274 0 : fd_quic_cb_conn_final( quic, conn ); /* inform user before freeing */
3275 0 : fd_quic_conn_free( quic, conn );
3276 0 : break;
3277 0 : default: {
3278 : /* Should we schedule for keep-alive or timeout?
3279 : 1. If keep_alive not configured, for timeout
3280 : 2. Else, if we've crossed the halfway point, we must have just pinged,
3281 : and should therefore schedule timeout. Otherwise, we should schedule
3282 : for the keep-alive time. */
3283 0 : long const timeout_ns = conn->idle_timeout_ns;
3284 0 : long const last_activity = conn->last_activity;
3285 0 : int const keep_alive = quic->config.keep_alive & (now < last_activity+timeout_ns/2L);
3286 0 : fd_quic_svc_prep_schedule( conn, last_activity + (timeout_ns>>keep_alive) );
3287 0 : fd_quic_svc_timers_schedule( state->svc_timers, conn, state->now );
3288 0 : break;
3289 0 : }
3290 0 : }
3291 :
3292 0 : return 1;
3293 0 : }
3294 :
3295 : int
3296 : fd_quic_service( fd_quic_t * quic,
3297 0 : long now ) {
3298 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
3299 :
3300 0 : state->now = now;
3301 0 : long now_ticks = fd_tickcount();
3302 :
3303 0 : fd_quic_svc_timers_t * timers = state->svc_timers;
3304 0 : svc_cnt_eq_alloc_conn( timers, quic );
3305 0 : fd_quic_svc_event_t next = fd_quic_svc_timers_next( timers, now, 1 /* pop */);
3306 0 : if( FD_UNLIKELY( next.conn == NULL ) ) {
3307 0 : return 0;
3308 0 : }
3309 :
3310 0 : int cnt = fd_quic_svc_poll( quic, next.conn, now );
3311 :
3312 0 : long delta_ticks = fd_tickcount() - now_ticks;
3313 :
3314 0 : fd_histf_sample( quic->metrics.service_duration, (ulong)delta_ticks );
3315 :
3316 0 : return cnt;
3317 0 : }
3318 :
3319 : static inline ulong FD_FN_UNUSED
3320 0 : fd_quic_conn_tx_buf_remaining( fd_quic_conn_t * conn ) {
3321 0 : return (ulong)( sizeof( conn->tx_buf_conn ) - (ulong)( conn->tx_ptr - conn->tx_buf_conn ) );
3322 0 : }
3323 :
3324 : static inline int
3325 : fd_quic_conn_can_acquire_pkt_meta( fd_quic_conn_t * conn,
3326 0 : fd_quic_pkt_meta_tracker_t * tracker ) {
3327 0 : fd_quic_state_t * state = fd_quic_get_state( conn->quic );
3328 0 : fd_quic_metrics_t * metrics = &conn->quic->metrics;
3329 :
3330 0 : ulong pool_free = fd_quic_pkt_meta_pool_free( tracker->pool );
3331 0 : if( !pool_free || conn->used_pkt_meta >= state->max_inflight_frame_cnt_conn ) {
3332 0 : if( !pool_free ) {
3333 0 : metrics->frame_tx_alloc_cnt[FD_METRICS_ENUM_PKT_META_ACQUIRE_RESULT_V_POOL_EMPTY_IDX]++;
3334 0 : } else {
3335 0 : metrics->frame_tx_alloc_cnt[FD_METRICS_ENUM_PKT_META_ACQUIRE_RESULT_V_CONN_LIMIT_IDX]++;
3336 0 : }
3337 0 : return 0;
3338 0 : }
3339 0 : metrics->frame_tx_alloc_cnt[FD_METRICS_ENUM_PKT_META_ACQUIRE_RESULT_V_SUCCESS_IDX]++;
3340 :
3341 0 : return 1;
3342 0 : }
3343 :
3344 : /* fd_quic_gen_frame_store_pkt_meta stores a pkt_meta into tracker.
3345 : Value and type take the passed args; all other fields are copied
3346 : from pkt_meta_tmpl. Returns 1 if successful, 0 if not.
3347 : Failure reasons include empty pkt_meta pool, or this conn reached
3348 : its pkt_meta limit. Theoretically only need latter, but let's be safe! */
3349 : static inline int
3350 : fd_quic_gen_frame_store_pkt_meta( const fd_quic_pkt_meta_t * pkt_meta_tmpl,
3351 : uchar type,
3352 : fd_quic_pkt_meta_value_t value,
3353 : fd_quic_pkt_meta_tracker_t * tracker,
3354 0 : fd_quic_conn_t * conn ) {
3355 0 : if( !fd_quic_conn_can_acquire_pkt_meta( conn, tracker ) ) return 0;
3356 :
3357 0 : conn->used_pkt_meta++;
3358 0 : fd_quic_pkt_meta_t * pkt_meta = fd_quic_pkt_meta_pool_ele_acquire( tracker->pool );
3359 0 : *pkt_meta = *pkt_meta_tmpl;
3360 0 : FD_QUIC_PKT_META_SET_TYPE( pkt_meta, type );
3361 0 : pkt_meta->val = value;
3362 0 : fd_quic_pkt_meta_insert( &tracker->sent_pkt_metas[pkt_meta->enc_level], pkt_meta, tracker->pool );
3363 0 : return 1;
3364 0 : }
3365 :
3366 : ulong
3367 : fd_quic_conn_tx_dgram( fd_quic_conn_t * conn,
3368 : uchar * pkt,
3369 : ulong pkt_sz,
3370 : uchar const * dgram,
3371 : ulong dgram_sz,
3372 0 : ulong * opt_pkt_num ) {
3373 0 : if( FD_UNLIKELY( !conn || !pkt || (!dgram && dgram_sz) ) ) return 0UL;
3374 0 : if( FD_UNLIKELY( conn->state!=FD_QUIC_CONN_STATE_ACTIVE ) ) return 0UL;
3375 0 : if( FD_UNLIKELY( !fd_uint_extract_bit( conn->keys_avail, fd_quic_enc_level_appdata_id ) ) ) return 0UL;
3376 0 : if( FD_UNLIKELY( dgram_sz>USHORT_MAX ) ) return 0UL;
3377 :
3378 : /* RFC 9221 Section 3: max_datagram_frame_size covers the complete
3379 : DATAGRAM frame, including type and length fields. */
3380 0 : ulong const len_sz = fd_quic_varint_min_sz( dgram_sz );
3381 0 : ulong const frame_sz = 1UL + len_sz + dgram_sz;
3382 0 : if( FD_UNLIKELY( !conn->tx_max_datagram_frame_sz ||
3383 0 : frame_sz>conn->tx_max_datagram_frame_sz ) ) return 0UL;
3384 :
3385 0 : uint const pn_space = fd_quic_enc_level_to_pn_space( fd_quic_enc_level_appdata_id );
3386 0 : ulong const pkt_num = conn->pkt_number[ pn_space ];
3387 0 : uint const pn_sz = 4U;
3388 0 : uint const pn_sz_enc= pn_sz-1U;
3389 :
3390 0 : fd_quic_conn_id_t const * peer_conn_id = &conn->peer_cids[0];
3391 0 : if( FD_UNLIKELY( peer_conn_id->sz>FD_QUIC_MAX_CONN_ID_SZ ) ) return 0UL;
3392 :
3393 0 : fd_quic_state_t * state = fd_quic_get_state( conn->quic );
3394 0 : uchar * scratch = state->crypt_scratch;
3395 0 : ulong scratch_sz = sizeof( state->crypt_scratch );
3396 :
3397 0 : int const key_phase_upd = (int)conn->key_update;
3398 0 : uint const key_phase_tx = conn->key_phase ^ (uint)key_phase_upd;
3399 0 : fd_quic_one_rtt_t one_rtt = {0};
3400 0 : one_rtt.h0 = fd_quic_one_rtt_h0( 0, !!key_phase_tx, pn_sz_enc );
3401 0 : one_rtt.dst_conn_id_len = peer_conn_id->sz;
3402 0 : one_rtt.pkt_num = pkt_num;
3403 0 : fd_memcpy( one_rtt.dst_conn_id, peer_conn_id->conn_id, peer_conn_id->sz );
3404 :
3405 0 : ulong const hdr_sz = fd_quic_encode_one_rtt( scratch, scratch_sz, &one_rtt );
3406 0 : if( FD_UNLIKELY( hdr_sz==FD_QUIC_ENCODE_FAIL ) ) return 0UL;
3407 :
3408 : /* Carry pending ACKs, even for ACK-only packets: this packet is
3409 : ack-eliciting, and a peer that only ever sends ACKs (Agave votor
3410 : server) otherwise never sees its ACKs acknowledged and keeps
3411 : re-sending every gap. */
3412 0 : uchar ack_buf[ 128 ];
3413 0 : ulong ack_room = fd_ulong_min( sizeof(ack_buf), fd_ulong_sat_sub( fd_ulong_min( pkt_sz, conn->tx_max_datagram_sz ), hdr_sz+frame_sz+FD_QUIC_CRYPTO_TAG_SZ ) );
3414 0 : uint ack_tail = conn->ack_gen->tail; /* restored if the send fails */
3415 0 : uchar ack_elicited = conn->ack_gen->is_elicited;
3416 0 : ulong ack_sz = (ulong)( fd_quic_gen_ack_frames( conn->ack_gen, ack_buf, ack_buf+ack_room, fd_quic_enc_level_appdata_id, state->now, 1 )-ack_buf );
3417 :
3418 0 : ulong padding = 0UL;
3419 0 : ulong const protected_payload_sz = ack_sz + frame_sz + FD_QUIC_CRYPTO_TAG_SZ;
3420 0 : ulong const sample_min_sz = FD_QUIC_CRYPTO_SAMPLE_OFFSET_FROM_PKT_NUM_START +
3421 0 : FD_QUIC_CRYPTO_SAMPLE_SZ;
3422 0 : if( protected_payload_sz+pn_sz < sample_min_sz ) {
3423 0 : padding = sample_min_sz - pn_sz - protected_payload_sz;
3424 0 : }
3425 :
3426 0 : ulong const plain_sz = ack_sz + frame_sz + padding;
3427 0 : ulong const out_sz = hdr_sz + plain_sz + FD_QUIC_CRYPTO_TAG_SZ;
3428 0 : if( FD_UNLIKELY(
3429 0 : out_sz>pkt_sz ||
3430 0 : out_sz>conn->tx_max_datagram_sz ||
3431 0 : hdr_sz+plain_sz>scratch_sz
3432 0 : ) ) {
3433 0 : conn->ack_gen->tail = ack_tail; conn->ack_gen->is_elicited = ack_elicited;
3434 0 : return 0UL;
3435 0 : }
3436 :
3437 0 : uchar * p = scratch + hdr_sz;
3438 0 : fd_memcpy( p, ack_buf, ack_sz );
3439 0 : p += ack_sz;
3440 0 : *p++ = 0x31U; /* DATAGRAM with an explicit Length field */
3441 0 : p += fd_quic_varint_encode( p, dgram_sz );
3442 0 : if( dgram_sz ) fd_memcpy( p, dgram, dgram_sz );
3443 0 : p += dgram_sz;
3444 0 : fd_memset( p, 0, padding );
3445 :
3446 : #if FD_QUIC_DISABLE_CRYPTO
3447 : fd_memcpy( pkt, scratch, hdr_sz+plain_sz );
3448 : fd_memset( pkt+hdr_sz+plain_sz, 0, FD_QUIC_CRYPTO_TAG_SZ );
3449 : #else
3450 0 : ulong cipher_sz = pkt_sz;
3451 0 : fd_quic_crypto_keys_t * hp_keys = &conn->keys[fd_quic_enc_level_appdata_id][1];
3452 0 : fd_quic_crypto_keys_t * pkt_keys = key_phase_upd ? &conn->new_keys[1] : hp_keys;
3453 0 : int enc_res = fd_quic_crypto_encrypt(
3454 0 : pkt,
3455 0 : &cipher_sz,
3456 0 : scratch, hdr_sz,
3457 0 : scratch + hdr_sz,
3458 0 : plain_sz,
3459 0 : pkt_keys, hp_keys,
3460 0 : pkt_num
3461 0 : );
3462 0 : if( FD_UNLIKELY( enc_res!=FD_QUIC_SUCCESS || cipher_sz!=out_sz ) ) {
3463 0 : conn->ack_gen->tail = ack_tail;
3464 0 : conn->ack_gen->is_elicited = ack_elicited;
3465 0 : return 0UL;
3466 0 : }
3467 0 : #endif
3468 :
3469 0 : if( ack_sz ) conn->key_phase_ack_sent = 1;
3470 0 : conn->pkt_number[ pn_space ] = pkt_num + 1UL;
3471 0 : if( conn->ack_gen->head==conn->ack_gen->tail ) conn->unacked_sz = 0UL;
3472 :
3473 : /* RFC 9221 Section 5.2: DATAGRAM frames are ack-eliciting, so track
3474 : the packet for RTT samples and loss detection like any other */
3475 0 : long expiry = state->now + fd_quic_calc_expiry_duration( conn, 0, conn->server );
3476 0 : fd_quic_pkt_meta_t pkt_meta_tmpl[1] = {{ .expiry = expiry, .tx_time = state->now, .enc_level = fd_quic_enc_level_appdata_id, .pn_space = (uchar)pn_space }};
3477 0 : FD_QUIC_PKT_META_SET_PKT_NUM( pkt_meta_tmpl, pkt_num );
3478 0 : if( FD_LIKELY( fd_quic_gen_frame_store_pkt_meta( pkt_meta_tmpl, FD_QUIC_PKT_META_TYPE_DATAGRAM, (fd_quic_pkt_meta_value_t){0}, &conn->pkt_meta_tracker, conn ) ) ) {
3479 0 : fd_quic_svc_prep_schedule( conn, expiry );
3480 0 : fd_quic_svc_schedule1( conn );
3481 0 : }
3482 0 : if( opt_pkt_num ) *opt_pkt_num = pkt_num;
3483 0 : return out_sz;
3484 0 : }
3485 :
3486 : /* attempt to transmit buffered data
3487 :
3488 : prior to call, conn->tx_ptr points to the first free byte in tx_buf
3489 : the data in tx_buf..tx_ptr is prepended by networking headers
3490 : and put on the wire
3491 :
3492 : returns 0 if successful, or 1 otherwise */
3493 : uint
3494 : fd_quic_tx_buffered_raw(
3495 : fd_quic_t * quic,
3496 : uchar ** tx_ptr_ptr,
3497 : uchar * tx_buf,
3498 : ushort * ipv4_id,
3499 : uint dst_ipv4_addr,
3500 : ushort dst_udp_port,
3501 : uint src_ipv4_addr,
3502 : ushort src_udp_port
3503 0 : ) {
3504 :
3505 : /* TODO leave space at front of tx_buf for header
3506 : then encode directly into it to avoid 1 copy */
3507 0 : uchar *tx_ptr = *tx_ptr_ptr;
3508 0 : long payload_sz = tx_ptr - tx_buf;
3509 :
3510 : /* nothing to do */
3511 0 : if( FD_UNLIKELY( payload_sz<=0L ) ) {
3512 0 : return 0u;
3513 0 : }
3514 :
3515 0 : fd_quic_config_t * config = &quic->config;
3516 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
3517 :
3518 0 : uchar * const crypt_scratch = state->crypt_scratch;
3519 :
3520 0 : uchar * cur_ptr = state->crypt_scratch;
3521 0 : ulong cur_sz = sizeof( state->crypt_scratch );
3522 :
3523 : /* TODO much of this may be prepared ahead of time */
3524 0 : fd_quic_pkt_t pkt;
3525 :
3526 0 : pkt.ip4->verihl = FD_IP4_VERIHL(4,5);
3527 0 : pkt.ip4->tos = (uchar)(config->net.dscp << 2); /* could make this per-connection or per-stream */
3528 0 : pkt.ip4->net_tot_len = (ushort)( 20 + 8 + payload_sz );
3529 0 : pkt.ip4->net_id = *ipv4_id;
3530 0 : pkt.ip4->net_frag_off = 0x4000u; /* don't fragment */
3531 0 : pkt.ip4->ttl = 64; /* TODO make configurable */
3532 0 : pkt.ip4->protocol = FD_IP4_HDR_PROTOCOL_UDP;
3533 0 : pkt.ip4->check = 0;
3534 0 : pkt.ip4->saddr = src_ipv4_addr;
3535 0 : pkt.ip4->daddr = dst_ipv4_addr;
3536 0 : pkt.udp->net_sport = src_udp_port;
3537 0 : pkt.udp->net_dport = dst_udp_port;
3538 0 : pkt.udp->net_len = (ushort)( 8 + payload_sz );
3539 0 : pkt.udp->check = 0x0000;
3540 0 : *ipv4_id = (ushort)( *ipv4_id + 1 );
3541 :
3542 0 : ulong rc = fd_quic_encode_ip4( cur_ptr, cur_sz, pkt.ip4 );
3543 0 : if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) {
3544 0 : FD_LOG_ERR(( "fd_quic_encode_ip4 failed with buffer overrun" ));
3545 0 : }
3546 :
3547 : /* Compute checksum over network byte order header */
3548 0 : fd_ip4_hdr_t * ip4_encoded = (fd_ip4_hdr_t *)fd_type_pun( cur_ptr );
3549 0 : ip4_encoded->check = (ushort)fd_ip4_hdr_check_fast( ip4_encoded );
3550 :
3551 0 : cur_ptr += rc;
3552 0 : cur_sz -= rc;
3553 :
3554 0 : rc = fd_quic_encode_udp( cur_ptr, cur_sz, pkt.udp );
3555 0 : if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) {
3556 0 : FD_LOG_ERR(( "fd_quic_encode_udp failed with buffer overrun" ));
3557 0 : }
3558 :
3559 0 : cur_ptr += rc;
3560 0 : cur_sz -= rc;
3561 :
3562 : /* need enough space for payload */
3563 0 : if( FD_UNLIKELY( (ulong)payload_sz > cur_sz ) ) {
3564 0 : FD_LOG_WARNING(( "%s : payload too big for buffer", __func__ ));
3565 :
3566 : /* reset buffer, since we can't use its contents */
3567 0 : *tx_ptr_ptr = tx_buf;
3568 0 : return FD_QUIC_FAILED;
3569 0 : }
3570 0 : fd_memcpy( cur_ptr, tx_buf, (ulong)payload_sz );
3571 :
3572 0 : cur_ptr += (ulong)payload_sz;
3573 0 : cur_sz -= (ulong)payload_sz;
3574 :
3575 0 : fd_aio_pkt_info_t aio_buf = { .buf = crypt_scratch, .buf_sz = (ushort)( cur_ptr - crypt_scratch ) };
3576 0 : int aio_rc = fd_aio_send( &quic->aio_tx, &aio_buf, 1, NULL, 1 );
3577 0 : if( aio_rc == FD_AIO_ERR_AGAIN ) {
3578 : /* transient condition - try later */
3579 0 : return FD_QUIC_FAILED;
3580 0 : } else if( aio_rc != FD_AIO_SUCCESS ) {
3581 0 : FD_LOG_WARNING(( "Fatal error reported by aio peer" ));
3582 : /* fallthrough to reset buffer */
3583 0 : }
3584 :
3585 : /* after send, reset tx_ptr and tx_sz */
3586 0 : *tx_ptr_ptr = tx_buf;
3587 :
3588 0 : quic->metrics.net_tx_pkt_cnt += aio_rc==FD_AIO_SUCCESS;
3589 0 : if( FD_LIKELY( aio_rc==FD_AIO_SUCCESS ) ) {
3590 0 : quic->metrics.net_tx_byte_cnt += aio_buf.buf_sz;
3591 0 : }
3592 :
3593 0 : return FD_QUIC_SUCCESS; /* success */
3594 0 : }
3595 :
3596 : uint
3597 : fd_quic_tx_buffered( fd_quic_t * quic,
3598 0 : fd_quic_conn_t * conn ) {
3599 0 : fd_quic_net_endpoint_t const * endpoint = conn->peer;
3600 0 : return fd_quic_tx_buffered_raw(
3601 0 : quic,
3602 0 : &conn->tx_ptr,
3603 0 : conn->tx_buf_conn,
3604 0 : &conn->ipv4_id,
3605 0 : endpoint->ip_addr,
3606 0 : endpoint->udp_port,
3607 0 : conn->host.ip_addr,
3608 0 : conn->host.udp_port);
3609 0 : }
3610 :
3611 : static ulong
3612 : fd_quic_gen_close_frame( fd_quic_conn_t * conn,
3613 : uchar * payload_ptr,
3614 : uchar * payload_end,
3615 : const fd_quic_pkt_meta_t * pkt_meta_tmpl,
3616 0 : fd_quic_pkt_meta_tracker_t * tracker ) {
3617 :
3618 0 : if( conn->flags & FD_QUIC_CONN_FLAGS_CLOSE_SENT ) return 0UL;
3619 0 : conn->flags |= FD_QUIC_CONN_FLAGS_CLOSE_SENT;
3620 :
3621 0 : uint reason = conn->reason;
3622 0 : if( !reason && conn->state!=FD_QUIC_CONN_STATE_PEER_CLOSE &&
3623 0 : ( pkt_meta_tmpl->enc_level==fd_quic_enc_level_initial_id ||
3624 0 : pkt_meta_tmpl->enc_level==fd_quic_enc_level_handshake_id ) ) {
3625 0 : reason = FD_QUIC_CONN_REASON_APPLICATION_ERROR;
3626 0 : }
3627 :
3628 0 : ulong frame_sz;
3629 0 : if( reason != 0u || conn->state == FD_QUIC_CONN_STATE_PEER_CLOSE ) {
3630 0 : fd_quic_conn_close_0_frame_t frame = {
3631 0 : .error_code = reason,
3632 0 : .frame_type = 0u, /* we do not know the frame in question */
3633 0 : .reason_phrase_length = 0u /* no reason phrase */
3634 0 : };
3635 0 : frame_sz = fd_quic_encode_conn_close_0_frame( payload_ptr,
3636 0 : (ulong)( payload_end - payload_ptr ),
3637 0 : &frame );
3638 0 : } else {
3639 0 : fd_quic_conn_close_1_frame_t frame = {
3640 0 : .error_code = conn->app_reason,
3641 0 : .reason_phrase_length = 0u /* no reason phrase */
3642 0 : };
3643 0 : frame_sz = fd_quic_encode_conn_close_1_frame( payload_ptr,
3644 0 : (ulong)( payload_end - payload_ptr ),
3645 0 : &frame );
3646 0 : }
3647 :
3648 0 : if( FD_UNLIKELY( frame_sz == FD_QUIC_PARSE_FAIL ) ) {
3649 0 : FD_LOG_WARNING(( "fd_quic_encode_conn_close_frame failed, but space should have been available" ));
3650 0 : return 0UL;
3651 0 : }
3652 :
3653 : /* create and save pkt_meta, return 0 if fail */
3654 0 : if( !fd_quic_gen_frame_store_pkt_meta( pkt_meta_tmpl,
3655 0 : FD_QUIC_PKT_META_TYPE_CLOSE,
3656 0 : (fd_quic_pkt_meta_value_t){0}, /* value doesn't matter */
3657 0 : tracker,
3658 0 : conn )) return 0UL;
3659 :
3660 0 : return frame_sz;
3661 0 : }
3662 :
3663 : static uchar *
3664 : fd_quic_gen_handshake_frames( fd_quic_conn_t * conn,
3665 : uchar * payload_ptr,
3666 : uchar * payload_end,
3667 : const fd_quic_pkt_meta_t * pkt_meta_tmpl,
3668 0 : fd_quic_pkt_meta_tracker_t * tracker ) {
3669 0 : uint enc_level = pkt_meta_tmpl->enc_level;
3670 0 : fd_quic_tls_hs_data_t * hs_data = fd_quic_tls_get_hs_data( conn->tls_hs, enc_level );
3671 0 : if( !hs_data ) return payload_ptr;
3672 :
3673 : /* confirm we have pkt_meta space */
3674 0 : if( !fd_quic_conn_can_acquire_pkt_meta( conn, tracker ) ) return payload_ptr;
3675 :
3676 0 : ulong hs_offset = 0; /* offset within the current hs_data */
3677 0 : ulong sent_offset = conn->hs_sent_bytes[enc_level];
3678 0 : ulong ackd_offset = conn->hs_ackd_bytes[enc_level];
3679 : /* offset within stream */
3680 0 : ulong offset = fd_ulong_max( sent_offset, ackd_offset );
3681 :
3682 : /* track pkt_meta values */
3683 0 : ulong offset_lo = offset;
3684 0 : ulong offset_hi = offset;
3685 :
3686 0 : while( hs_data ) {
3687 : /* skip data we've sent */
3688 0 : if( hs_data->offset + hs_data->data_sz <= offset ) {
3689 0 : hs_data = fd_quic_tls_get_next_hs_data( conn->tls_hs, hs_data );
3690 0 : continue;
3691 0 : }
3692 :
3693 0 : if( FD_UNLIKELY( hs_data->offset > offset ) ) {
3694 : /* we have a gap - this shouldn't happen */
3695 0 : FD_LOG_WARNING(( "%s - gap in TLS handshake data", __func__ ));
3696 : /* TODO should probably tear down connection */
3697 0 : break;
3698 0 : }
3699 :
3700 : /* encode hs_data into frame */
3701 0 : hs_offset = offset - hs_data->offset;
3702 :
3703 : /* handshake data to send */
3704 0 : uchar const * cur_data = hs_data->data + hs_offset;
3705 0 : ulong cur_data_sz = hs_data->data_sz - hs_offset;
3706 :
3707 : /* 9 bytes header + cur_data_sz */
3708 0 : if( payload_ptr + 9UL + cur_data_sz > payload_end ) break;
3709 : /* FIXME reduce cur_data_sz if it doesn't fit in frame
3710 : Practically don't need to, because fd_tls generates a small amount of data */
3711 :
3712 0 : payload_ptr[0] = 0x06; /* CRYPTO frame */
3713 0 : uint offset_varint = 0x80U | ( fd_uint_bswap( (uint)offset & 0x3fffffffU ) );
3714 0 : uint length_varint = 0x80U | ( fd_uint_bswap( (uint)cur_data_sz & 0x3fffffffU ) );
3715 0 : FD_STORE( uint, payload_ptr+1, offset_varint );
3716 0 : FD_STORE( uint, payload_ptr+5, length_varint );
3717 0 : payload_ptr += 9;
3718 :
3719 0 : fd_memcpy( payload_ptr, cur_data, cur_data_sz );
3720 0 : payload_ptr += cur_data_sz;
3721 :
3722 : /* update pkt_meta values */
3723 0 : offset_hi += cur_data_sz;
3724 :
3725 : /* move to next hs_data */
3726 0 : offset += cur_data_sz;
3727 0 : conn->hs_sent_bytes[enc_level] += cur_data_sz;
3728 :
3729 : /* TODO load more hs_data into a crypto frame, if available
3730 : currently tricky, because encode_crypto_frame copies payload */
3731 0 : }
3732 :
3733 : /* update packet meta */
3734 0 : if( offset_hi > offset_lo ) {
3735 0 : fd_quic_gen_frame_store_pkt_meta( pkt_meta_tmpl,
3736 0 : FD_QUIC_PKT_META_TYPE_HS_DATA,
3737 0 : (fd_quic_pkt_meta_value_t){
3738 0 : .range = {
3739 0 : .offset_lo = offset_lo,
3740 0 : .offset_hi = offset_hi
3741 0 : }
3742 0 : },
3743 0 : tracker,
3744 0 : conn );
3745 0 : }
3746 :
3747 0 : return payload_ptr;
3748 0 : }
3749 :
3750 : static ulong
3751 : fd_quic_gen_handshake_done_frame( fd_quic_conn_t * conn,
3752 : uchar * payload_ptr,
3753 : uchar * payload_end,
3754 : const fd_quic_pkt_meta_t * pkt_meta_tmpl,
3755 0 : fd_quic_pkt_meta_tracker_t * tracker ) {
3756 0 : FD_DTRACE_PROBE_1( quic_gen_handshake_done_frame, conn->our_conn_id );
3757 0 : if( conn->handshake_done_send==0 ) return 0UL;
3758 0 : conn->handshake_done_send = 0;
3759 0 : if( FD_UNLIKELY( conn->handshake_done_ackd ) ) return 0UL;
3760 0 : if( FD_UNLIKELY( payload_ptr >= payload_end ) ) return 0UL;
3761 : /* send handshake done frame */
3762 0 : payload_ptr[0] = 0x1E;
3763 :
3764 : /* record the send for retx */
3765 0 : if( !fd_quic_gen_frame_store_pkt_meta( pkt_meta_tmpl,
3766 0 : FD_QUIC_PKT_META_TYPE_HS_DONE,
3767 0 : (fd_quic_pkt_meta_value_t){0}, /* value doesn't matter */
3768 0 : tracker,
3769 0 : conn) ) return 0UL;
3770 :
3771 0 : return 1UL;
3772 0 : }
3773 :
3774 : static ulong
3775 : fd_quic_gen_max_data_frame( fd_quic_conn_t * conn,
3776 : uchar * payload_ptr,
3777 : uchar * payload_end,
3778 : const fd_quic_pkt_meta_t * pkt_meta_tmpl,
3779 0 : fd_quic_pkt_meta_tracker_t * tracker ) {
3780 0 : fd_quic_conn_stream_rx_t * srx = conn->srx;
3781 :
3782 0 : if( !( conn->flags & FD_QUIC_CONN_FLAGS_MAX_DATA ) ) return 0UL;
3783 0 : if( srx->rx_max_data <= srx->rx_max_data_ackd ) return 0UL; /* peer would ignore anyway */
3784 :
3785 : /* send max_data frame */
3786 0 : fd_quic_max_data_frame_t frame = { .max_data = srx->rx_max_data };
3787 :
3788 : /* attempt to write into buffer */
3789 0 : ulong frame_sz = fd_quic_encode_max_data_frame( payload_ptr,
3790 0 : (ulong)( payload_end - payload_ptr ),
3791 0 : &frame );
3792 0 : if( FD_UNLIKELY( frame_sz==FD_QUIC_ENCODE_FAIL ) ) return 0UL;
3793 :
3794 : /* acquire and set a pkt_meta, return 0 if not successful */
3795 0 : if( !fd_quic_gen_frame_store_pkt_meta( pkt_meta_tmpl,
3796 0 : FD_QUIC_PKT_META_TYPE_MAX_DATA,
3797 0 : (fd_quic_pkt_meta_value_t){
3798 0 : .scalar = srx->rx_max_data
3799 0 : },
3800 0 : tracker,
3801 0 : conn ) ) return 0UL;
3802 :
3803 0 : conn->upd_pkt_number = pkt_meta_tmpl->key.pkt_num;
3804 0 : return frame_sz;
3805 0 : }
3806 :
3807 : static ulong
3808 : fd_quic_gen_max_streams_frame( fd_quic_conn_t * conn,
3809 : uchar * payload_ptr,
3810 : uchar * payload_end,
3811 : const fd_quic_pkt_meta_t * pkt_meta_tmpl,
3812 0 : fd_quic_pkt_meta_tracker_t * tracker ) {
3813 0 : fd_quic_conn_stream_rx_t * srx = conn->srx;
3814 :
3815 : /* 0x02 Client-Initiated, Unidirectional
3816 : 0x03 Server-Initiated, Unidirectional */
3817 0 : ulong max_streams_unidir = srx->rx_sup_stream_id >> 2;
3818 :
3819 0 : uint flags = conn->flags;
3820 0 : if( !FD_QUIC_MAX_STREAMS_ALWAYS_UNLESS_ACKED ) {
3821 0 : if( !( flags & FD_QUIC_CONN_FLAGS_MAX_STREAMS_UNIDIR ) ) return 0UL;
3822 0 : if( max_streams_unidir <= srx->rx_max_streams_unidir_ackd ) return 0UL;
3823 0 : }
3824 :
3825 0 : fd_quic_max_streams_frame_t max_streams = {
3826 0 : .type = 0x13, /* unidirectional */
3827 0 : .max_streams = max_streams_unidir
3828 0 : };
3829 0 : ulong frame_sz = fd_quic_encode_max_streams_frame( payload_ptr,
3830 0 : (ulong)( payload_end - payload_ptr ),
3831 0 : &max_streams );
3832 0 : if( FD_UNLIKELY( frame_sz==FD_QUIC_ENCODE_FAIL ) ) return 0UL;
3833 :
3834 0 : if( !fd_quic_gen_frame_store_pkt_meta( pkt_meta_tmpl,
3835 0 : FD_QUIC_PKT_META_TYPE_MAX_STREAMS_UNIDIR,
3836 0 : (fd_quic_pkt_meta_value_t){0}, /* value doesn't matter */
3837 0 : tracker,
3838 0 : conn ) ) return 0UL;
3839 :
3840 0 : conn->flags = flags & (~FD_QUIC_CONN_FLAGS_MAX_STREAMS_UNIDIR);
3841 0 : conn->upd_pkt_number = pkt_meta_tmpl->key.pkt_num;
3842 0 : return frame_sz;
3843 0 : }
3844 :
3845 : static ulong
3846 : fd_quic_gen_ping_frame( fd_quic_conn_t * conn,
3847 : uchar * payload_ptr,
3848 : uchar * payload_end,
3849 : const fd_quic_pkt_meta_t * pkt_meta_tmpl,
3850 0 : fd_quic_pkt_meta_tracker_t * tracker ) {
3851 :
3852 0 : if( ~conn->flags & FD_QUIC_CONN_FLAGS_PING ) return 0UL;
3853 0 : if( conn->flags & FD_QUIC_CONN_FLAGS_PING_SENT ) return 0UL;
3854 :
3855 0 : fd_quic_ping_frame_t ping = {0};
3856 0 : ulong frame_sz = fd_quic_encode_ping_frame( payload_ptr,
3857 0 : (ulong)( payload_end - payload_ptr ),
3858 0 : &ping );
3859 0 : if( FD_UNLIKELY( frame_sz==FD_QUIC_ENCODE_FAIL ) ) return 0UL;
3860 0 : conn->flags |= FD_QUIC_CONN_FLAGS_PING_SENT;
3861 0 : conn->flags &= ~FD_QUIC_CONN_FLAGS_PING;
3862 :
3863 0 : conn->upd_pkt_number = pkt_meta_tmpl->key.pkt_num;
3864 : /* record the send for retx, 0 if fail */
3865 0 : if( !fd_quic_gen_frame_store_pkt_meta( pkt_meta_tmpl,
3866 0 : FD_QUIC_PKT_META_TYPE_PING,
3867 0 : (fd_quic_pkt_meta_value_t){0}, /* value doesn't matter */
3868 0 : tracker,
3869 0 : conn ) ) return 0UL;
3870 :
3871 0 : return frame_sz;
3872 0 : }
3873 :
3874 : uchar *
3875 : fd_quic_gen_stream_frames( fd_quic_conn_t * conn,
3876 : uchar * payload_ptr,
3877 : uchar * payload_end,
3878 : fd_quic_pkt_meta_t * pkt_meta_tmpl,
3879 0 : fd_quic_pkt_meta_tracker_t * tracker ) {
3880 :
3881 : /* loop serves two purposes:
3882 : 1. finds a stream with data to send
3883 : 2. appends max_stream_data frames as necessary */
3884 0 : fd_quic_stream_t * sentinel = conn->send_streams;
3885 0 : fd_quic_stream_t * cur_stream = sentinel->next;
3886 0 : ulong pkt_num = pkt_meta_tmpl->key.pkt_num;
3887 0 : while( !cur_stream->sentinel ) {
3888 : /* required, since cur_stream may get removed from list */
3889 0 : fd_quic_stream_t * nxt_stream = cur_stream->next;
3890 0 : _Bool sent_all_data = 1u;
3891 :
3892 0 : if( cur_stream->upd_pkt_number >= pkt_num ) {
3893 :
3894 : /* any stream data? */
3895 0 : if( FD_LIKELY( FD_QUIC_STREAM_ACTION( cur_stream ) ) ) {
3896 :
3897 : /* data_avail is the number of stream bytes available for sending.
3898 : fin_flag_set is 1 if no more bytes will get added to the stream. */
3899 0 : ulong const data_avail = cur_stream->tx_buf.head - cur_stream->tx_sent;
3900 0 : int const fin_flag_set = !!(cur_stream->state & FD_QUIC_STREAM_STATE_TX_FIN);
3901 0 : ulong const stream_id = cur_stream->stream_id;
3902 0 : ulong const stream_off = cur_stream->tx_sent;
3903 :
3904 : /* No information to send? */
3905 0 : if( data_avail==0u && !fin_flag_set ) break;
3906 :
3907 : /* No space to write frame?
3908 : (Buffer should fit max stream header size and at least 1 byte of data) */
3909 0 : if( payload_ptr+FD_QUIC_MAX_FOOTPRINT( stream_e_frame )+1 > payload_end ) break;
3910 :
3911 : /* check pkt_meta availability */
3912 0 : if( !fd_quic_conn_can_acquire_pkt_meta( conn, tracker ) ) break;
3913 :
3914 : /* Leave placeholder for frame/stream type */
3915 0 : uchar * const frame_type_p = payload_ptr++;
3916 0 : uint frame_type = 0x0a; /* stream frame with length */
3917 :
3918 : /* Encode stream ID */
3919 0 : payload_ptr += fd_quic_varint_encode( payload_ptr, stream_id );
3920 :
3921 : /* Optionally encode offset */
3922 0 : if( stream_off>0 ) {
3923 0 : frame_type |= 0x04; /* with offset field */
3924 0 : payload_ptr += fd_quic_varint_encode( payload_ptr, stream_off );
3925 0 : }
3926 :
3927 : /* Leave placeholder for length length */
3928 0 : uchar * data_sz_p = payload_ptr;
3929 0 : payload_ptr += 2;
3930 :
3931 : /* Stream metadata */
3932 0 : ulong data_max = (ulong)payload_end - (ulong)payload_ptr; /* assume no underflow */
3933 0 : ulong data_sz = fd_ulong_min( data_avail, data_max );
3934 0 : /* */ data_sz = fd_ulong_min( data_sz, 0x3fffUL ); /* max 2 byte varint */
3935 0 : /* */ sent_all_data = data_sz == data_avail;
3936 0 : _Bool fin = fin_flag_set && sent_all_data;
3937 :
3938 : /* Finish encoding stream header */
3939 0 : ushort data_sz_varint = fd_ushort_bswap( (ushort)( 0x4000u | (uint)data_sz ) );
3940 0 : FD_STORE( ushort, data_sz_p, data_sz_varint );
3941 0 : frame_type |= fin;
3942 0 : *frame_type_p = (uchar)frame_type;
3943 :
3944 : /* Write stream payload */
3945 0 : fd_quic_buffer_t * tx_buf = &cur_stream->tx_buf;
3946 0 : fd_quic_buffer_load( tx_buf, stream_off, payload_ptr, data_sz );
3947 0 : payload_ptr += data_sz;
3948 :
3949 : /* Update stream metadata */
3950 0 : cur_stream->tx_sent += data_sz;
3951 0 : cur_stream->upd_pkt_number = fd_ulong_if( fin, pkt_num, FD_QUIC_PKT_NUM_PENDING );
3952 0 : cur_stream->stream_flags &= fd_uint_if( fin, ~FD_QUIC_STREAM_FLAGS_ACTION, UINT_MAX );
3953 :
3954 : /* Packet metadata for potential retransmits */
3955 0 : pkt_meta_tmpl->key.stream_id = cur_stream->stream_id;
3956 0 : fd_quic_gen_frame_store_pkt_meta( pkt_meta_tmpl,
3957 0 : FD_QUIC_PKT_META_TYPE_STREAM,
3958 0 : (fd_quic_pkt_meta_value_t){
3959 0 : .range = {
3960 0 : .offset_lo = stream_off,
3961 0 : .offset_hi = stream_off + data_sz
3962 0 : }
3963 0 : },
3964 0 : tracker,
3965 0 : conn );
3966 0 : }
3967 0 : }
3968 :
3969 0 : if( sent_all_data ) {
3970 0 : cur_stream->stream_flags &= ~FD_QUIC_STREAM_FLAGS_ACTION;
3971 0 : FD_QUIC_STREAM_LIST_REMOVE( cur_stream );
3972 0 : FD_QUIC_STREAM_LIST_INSERT_BEFORE( conn->used_streams, cur_stream );
3973 0 : }
3974 :
3975 0 : cur_stream = nxt_stream;
3976 0 : }
3977 :
3978 0 : return payload_ptr;
3979 0 : }
3980 :
3981 : uchar *
3982 : fd_quic_gen_frames( fd_quic_conn_t * conn,
3983 : uchar * payload_ptr,
3984 : uchar * payload_end,
3985 : fd_quic_pkt_meta_t * pkt_meta_tmpl,
3986 0 : long now ) {
3987 :
3988 0 : uint closing = 0U;
3989 0 : switch( conn->state ) {
3990 0 : case FD_QUIC_CONN_STATE_PEER_CLOSE:
3991 0 : case FD_QUIC_CONN_STATE_ABORT:
3992 0 : case FD_QUIC_CONN_STATE_CLOSE_PENDING:
3993 0 : closing = 1u;
3994 0 : }
3995 :
3996 0 : fd_quic_pkt_meta_tracker_t * tracker = &conn->pkt_meta_tracker;
3997 :
3998 0 : uchar * ack_ptr = payload_ptr;
3999 0 : payload_ptr = fd_quic_gen_ack_frames( conn->ack_gen, payload_ptr, payload_end, pkt_meta_tmpl->enc_level, now, 0 );
4000 0 : if( conn->ack_gen->head == conn->ack_gen->tail ) conn->unacked_sz = 0UL;
4001 0 : if( ( payload_ptr != ack_ptr ) & ( pkt_meta_tmpl->enc_level == fd_quic_enc_level_appdata_id ) ) {
4002 0 : conn->key_phase_ack_sent = 1;
4003 0 : }
4004 :
4005 0 : if( FD_UNLIKELY( closing ) ) {
4006 0 : payload_ptr += fd_quic_gen_close_frame( conn, payload_ptr, payload_end, pkt_meta_tmpl, tracker );
4007 0 : } else {
4008 0 : payload_ptr = fd_quic_gen_handshake_frames( conn, payload_ptr, payload_end, pkt_meta_tmpl, tracker );
4009 0 : if( pkt_meta_tmpl->enc_level == fd_quic_enc_level_appdata_id ) {
4010 0 : payload_ptr += fd_quic_gen_handshake_done_frame( conn, payload_ptr, payload_end, pkt_meta_tmpl, tracker );
4011 0 : if( conn->upd_pkt_number >= pkt_meta_tmpl->key.pkt_num ) {
4012 0 : payload_ptr += fd_quic_gen_max_data_frame ( conn, payload_ptr, payload_end, pkt_meta_tmpl, tracker );
4013 0 : payload_ptr += fd_quic_gen_max_streams_frame( conn, payload_ptr, payload_end, pkt_meta_tmpl, tracker );
4014 0 : payload_ptr += fd_quic_gen_ping_frame ( conn, payload_ptr, payload_end, pkt_meta_tmpl, tracker );
4015 0 : }
4016 0 : if( FD_LIKELY( !conn->tls_hs ) ) {
4017 0 : payload_ptr = fd_quic_gen_stream_frames( conn, payload_ptr, payload_end, pkt_meta_tmpl, tracker );
4018 0 : }
4019 0 : }
4020 0 : }
4021 :
4022 0 : fd_quic_svc_prep_schedule( conn, pkt_meta_tmpl->expiry );
4023 :
4024 0 : return payload_ptr;
4025 0 : }
4026 :
4027 : /* transmit
4028 : looks at each of the following dependent on state, and creates
4029 : a packet to transmit:
4030 : acks
4031 : handshake data (tls)
4032 : handshake done
4033 : ping
4034 : stream data */
4035 : static void
4036 : fd_quic_conn_tx( fd_quic_t * quic,
4037 0 : fd_quic_conn_t * conn ) {
4038 :
4039 0 : if( FD_UNLIKELY( conn->state == FD_QUIC_CONN_STATE_DEAD ) ) return;
4040 :
4041 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
4042 :
4043 : /* used for encoding frames into before encrypting */
4044 0 : uchar * crypt_scratch = state->crypt_scratch;
4045 0 : ulong crypt_scratch_sz = sizeof( state->crypt_scratch );
4046 :
4047 : /* max packet size */
4048 : /* TODO probably should be called tx_max_udp_payload_sz */
4049 0 : ulong tx_max_datagram_sz = conn->tx_max_datagram_sz;
4050 :
4051 0 : if( conn->tx_ptr != conn->tx_buf_conn ) {
4052 0 : fd_quic_tx_buffered( quic, conn );
4053 0 : fd_quic_svc_prep_schedule( conn, state->now );
4054 0 : return;
4055 0 : }
4056 :
4057 : /* choose enc_level to tx at */
4058 : /* this function accepts an argument "acks"
4059 : * We want to minimize the number of packets that carry only acks.
4060 : * fd_quic_tx_enc_level determines whether a packet needs sending,
4061 : * and when encryption level should be used.
4062 : * If "acks" is set to 1 (true), fd_quic_tx_enc_level checks for acks.
4063 : * Otherwise, it does not check for acks
4064 : * We set "acks" only on the first call in this function. All subsequent
4065 : * calls do not set it.
4066 : * This ensures that ack-only packets only occur when nothing else needs
4067 : * to be sent */
4068 0 : uint enc_level = fd_quic_tx_enc_level( conn, 1 /* acks */ );
4069 :
4070 : /* nothing to send / bad state? */
4071 0 : if( enc_level == ~0u ) return;
4072 :
4073 0 : int key_phase_upd = (int)conn->key_update;
4074 0 : uint key_phase = conn->key_phase;
4075 0 : int key_phase_tx = (int)key_phase ^ key_phase_upd;
4076 :
4077 : /* get time */
4078 0 : long now = state->now;
4079 :
4080 : /* initialize expiry and tx_time */
4081 0 : long expiry = now + fd_quic_calc_expiry_duration( conn, 0 /* use PTO */, conn->server );
4082 0 : fd_quic_pkt_meta_t pkt_meta_tmpl[1] = {{.expiry = expiry, .tx_time = now}};
4083 :
4084 0 : while( enc_level != ~0u ) {
4085 : /* RFC 9000 Section 17.2.2.1. Abandoning Initial Packets
4086 : > A client stops both sending and processing Initial packets when
4087 : > it sends its first Handshake packet.
4088 :
4089 : RFC 9001 Section 4.9.1 Discarding Initial Keys
4090 : > a client MUST discard Initial keys when it first sends a Handshake packet */
4091 0 : if( FD_UNLIKELY( (quic->config.role==FD_QUIC_ROLE_CLIENT) & (enc_level==fd_quic_enc_level_handshake_id) ) ) {
4092 0 : fd_quic_abandon_enc_level( conn, fd_quic_enc_level_initial_id );
4093 0 : }
4094 :
4095 0 : uint initial_pkt = 0; /* is this the first initial packet? */
4096 :
4097 : /* remaining in datagram */
4098 : /* invariant: tx_ptr >= tx_buf */
4099 0 : ulong datagram_rem = tx_max_datagram_sz - (ulong)( conn->tx_ptr - conn->tx_buf_conn );
4100 :
4101 : /* encode into here */
4102 : /* this is the start of a new quic packet
4103 : cur_ptr points at the next byte to fill with a quic pkt */
4104 : /* currently, cur_ptr just points at the start of crypt_scratch
4105 : each quic packet gets encrypted into tx_buf, and the space in
4106 : crypt_scratch is reused */
4107 0 : uchar * cur_ptr = crypt_scratch;
4108 0 : ulong cur_sz = crypt_scratch_sz;
4109 :
4110 : /* TODO determine actual datagrams size to use */
4111 0 : cur_sz = fd_ulong_min( cur_sz, datagram_rem );
4112 :
4113 : /* determine pn_space */
4114 0 : uint pn_space = fd_quic_enc_level_to_pn_space( enc_level );
4115 0 : pkt_meta_tmpl->pn_space = (uchar)pn_space;
4116 0 : pkt_meta_tmpl->enc_level = (uchar)(enc_level&0x3);
4117 :
4118 : /* get next packet number
4119 : Returned to pool if not sent as gaps are harmful for ACK frame
4120 : compression. */
4121 0 : ulong pkt_number = conn->pkt_number[pn_space];
4122 0 : FD_QUIC_PKT_META_SET_PKT_NUM( pkt_meta_tmpl, pkt_number );
4123 :
4124 : /* are we the client initial packet? */
4125 0 : ulong hs_data_offset = conn->hs_sent_bytes[enc_level];
4126 0 : initial_pkt = (uint)( hs_data_offset == 0 ) & (uint)( !conn->server ) & (uint)( enc_level == fd_quic_enc_level_initial_id );
4127 :
4128 : /* current peer endpoint */
4129 0 : fd_quic_conn_id_t const * peer_conn_id = &conn->peer_cids[0];
4130 :
4131 : /* our current conn_id */
4132 0 : ulong conn_id = conn->our_conn_id;
4133 0 : uint const pkt_num_len = 4u; /* 4-byte packet number */
4134 0 : uint const pkt_num_len_enc = pkt_num_len - 1; /* -1 offset for protocol */
4135 :
4136 :
4137 : /* encode packet header (including packet number)
4138 : While encoding, remember where the 'length' field is, if one
4139 : exists. We'll have to update it later. */
4140 0 : uchar * hdr_ptr = cur_ptr;
4141 0 : ulong hdr_sz = 0UL;
4142 0 : uchar _hdr_len_field[2]; /* if no len field exists, catch the write here */
4143 0 : uchar * hdr_len_field = _hdr_len_field;
4144 0 : switch( enc_level ) {
4145 0 : case fd_quic_enc_level_initial_id: {
4146 0 : fd_quic_initial_t initial = {0};
4147 0 : initial.h0 = fd_quic_initial_h0( pkt_num_len_enc );
4148 0 : initial.version = 1;
4149 0 : initial.dst_conn_id_len = peer_conn_id->sz;
4150 : // .dst_conn_id
4151 0 : initial.src_conn_id_len = FD_QUIC_CONN_ID_SZ;
4152 : // .src_conn_id
4153 : // .token - below
4154 0 : initial.len = 0x3fff; /* use 2 byte varint encoding */
4155 0 : initial.pkt_num = pkt_number;
4156 :
4157 0 : fd_memcpy( initial.dst_conn_id, peer_conn_id->conn_id, peer_conn_id->sz );
4158 0 : memcpy( initial.src_conn_id, &conn_id, FD_QUIC_CONN_ID_SZ );
4159 :
4160 : /* Initial packets sent by the server MUST set the Token Length field to 0. */
4161 0 : initial.token = conn->token;
4162 0 : if( conn->quic->config.role == FD_QUIC_ROLE_CLIENT && conn->token_len ) {
4163 0 : initial.token_len = conn->token_len;
4164 0 : } else {
4165 0 : initial.token_len = 0;
4166 0 : }
4167 :
4168 0 : hdr_sz = fd_quic_encode_initial( cur_ptr, cur_sz, &initial );
4169 0 : hdr_len_field = cur_ptr + hdr_sz - 6; /* 2 byte len, 4 byte packet number */
4170 0 : FD_DTRACE_PROBE_2( quic_encode_initial, initial.src_conn_id, initial.dst_conn_id );
4171 0 : break;
4172 0 : }
4173 :
4174 0 : case fd_quic_enc_level_handshake_id: {
4175 0 : fd_quic_handshake_t handshake = {0};
4176 0 : handshake.h0 = fd_quic_handshake_h0( pkt_num_len_enc );
4177 0 : handshake.version = 1;
4178 :
4179 : /* destination */
4180 0 : fd_memcpy( handshake.dst_conn_id, peer_conn_id->conn_id, peer_conn_id->sz );
4181 0 : handshake.dst_conn_id_len = peer_conn_id->sz;
4182 :
4183 : /* source */
4184 0 : FD_STORE( ulong, handshake.src_conn_id, conn_id );
4185 0 : handshake.src_conn_id_len = sizeof(ulong);
4186 :
4187 0 : handshake.len = 0x3fff; /* use 2 byte varint encoding */
4188 0 : handshake.pkt_num = pkt_number;
4189 :
4190 0 : hdr_sz = fd_quic_encode_handshake( cur_ptr, cur_sz, &handshake );
4191 0 : hdr_len_field = cur_ptr + hdr_sz - 6; /* 2 byte len, 4 byte packet number */
4192 0 : FD_DTRACE_PROBE_2( quic_encode_handshake, handshake.src_conn_id, handshake.dst_conn_id );
4193 0 : break;
4194 0 : }
4195 :
4196 0 : case fd_quic_enc_level_appdata_id:
4197 0 : {
4198 0 : fd_quic_one_rtt_t one_rtt = {0};
4199 0 : one_rtt.h0 = fd_quic_one_rtt_h0( /* spin */ 0, !!key_phase_tx, pkt_num_len_enc );
4200 :
4201 : /* destination */
4202 0 : fd_memcpy( one_rtt.dst_conn_id, peer_conn_id->conn_id, peer_conn_id->sz );
4203 0 : one_rtt.dst_conn_id_len = peer_conn_id->sz;
4204 :
4205 0 : one_rtt.pkt_num = pkt_number;
4206 :
4207 0 : hdr_sz = fd_quic_encode_one_rtt( cur_ptr, cur_sz, &one_rtt );
4208 0 : FD_DTRACE_PROBE_2( quic_encode_one_rtt, one_rtt.dst_conn_id, one_rtt.pkt_num );
4209 0 : break;
4210 0 : }
4211 :
4212 0 : default:
4213 0 : FD_LOG_ERR(( "%s - logic error: unexpected enc_level", __func__ ));
4214 0 : }
4215 :
4216 : /* if we don't have reasonable amt of space for a new packet, tx to free space */
4217 0 : const ulong min_rqd = 64;
4218 0 : if( FD_UNLIKELY( hdr_sz==FD_QUIC_ENCODE_FAIL || hdr_sz + min_rqd > cur_sz ) ) {
4219 : /* try to free space */
4220 0 : fd_quic_tx_buffered( quic, conn );
4221 :
4222 : /* we have lots of space, so try again */
4223 0 : if( conn->tx_buf_conn == conn->tx_ptr ) {
4224 0 : enc_level = fd_quic_tx_enc_level( conn, 0 /* acks */ );
4225 0 : continue;
4226 0 : }
4227 :
4228 : /* reschedule, since some data was unable to be sent */
4229 : /* TODO might want to add a backoff here */
4230 0 : fd_quic_svc_prep_schedule( conn, now );
4231 :
4232 0 : break;
4233 0 : }
4234 :
4235 0 : cur_ptr += hdr_sz;
4236 0 : cur_sz -= hdr_sz;
4237 :
4238 : /* start writing payload, leaving room for header and expansion
4239 : due to varint coding */
4240 :
4241 0 : uchar * payload_ptr = cur_ptr;
4242 0 : ulong payload_sz = cur_sz;
4243 : /* payload_end leaves room for TAG */
4244 0 : uchar * payload_end = payload_ptr + payload_sz - FD_QUIC_CRYPTO_TAG_SZ;
4245 :
4246 0 : uchar * const frame_start = payload_ptr;
4247 0 : payload_ptr = fd_quic_gen_frames( conn, frame_start, payload_end, pkt_meta_tmpl, now );
4248 0 : if( FD_UNLIKELY( payload_ptr < frame_start ) ) FD_LOG_CRIT(( "fd_quic_gen_frames failed" ));
4249 :
4250 : /* did we add any frames? */
4251 :
4252 0 : if( payload_ptr==frame_start ) {
4253 : /* we have data to add, but none was added, presumably due
4254 : so space in the datagram */
4255 0 : ulong free_bytes = (ulong)( payload_end - payload_ptr );
4256 : /* sanity check */
4257 0 : if( free_bytes > 64 ) {
4258 : /* we should have been able to fit data into 64 bytes
4259 : so stop trying here */
4260 0 : break;
4261 0 : }
4262 :
4263 : /* try to free space */
4264 0 : fd_quic_tx_buffered( quic, conn );
4265 :
4266 : /* we have lots of space, so try again */
4267 0 : if( conn->tx_buf_conn == conn->tx_ptr ) {
4268 0 : enc_level = fd_quic_tx_enc_level( conn, 0 /* acks */ );
4269 0 : continue;
4270 0 : }
4271 0 : }
4272 :
4273 : /* initial padding */
4274 0 : uint tot_frame_sz = (uint)( payload_ptr - frame_start );
4275 0 : uint base_pkt_len = (uint)tot_frame_sz + pkt_num_len + FD_QUIC_CRYPTO_TAG_SZ;
4276 0 : ulong datagram_sz = (ulong)( conn->tx_ptr - conn->tx_buf_conn ) + hdr_sz + tot_frame_sz + FD_QUIC_CRYPTO_TAG_SZ;
4277 0 : uint padding = ( initial_pkt && (datagram_sz < FD_QUIC_INITIAL_PAYLOAD_SZ_MIN) )
4278 0 : ? (uint)( FD_QUIC_INITIAL_PAYLOAD_SZ_MIN - datagram_sz ) : 0u;
4279 :
4280 0 : if( base_pkt_len + padding < FD_QUIC_CRYPTO_SAMPLE_OFFSET_FROM_PKT_NUM_START + FD_QUIC_CRYPTO_SAMPLE_SZ ) {
4281 0 : padding = FD_QUIC_CRYPTO_SAMPLE_SZ + FD_QUIC_CRYPTO_SAMPLE_OFFSET_FROM_PKT_NUM_START - base_pkt_len;
4282 0 : }
4283 :
4284 0 : if( FD_UNLIKELY( datagram_sz + padding > tx_max_datagram_sz ||
4285 0 : (ulong)padding > (ulong)( payload_end - payload_ptr ) ) ) {
4286 0 : conn->tx_ptr = conn->tx_buf_conn;
4287 0 : fd_quic_set_conn_state( conn, FD_QUIC_CONN_STATE_DEAD );
4288 0 : fd_quic_svc_prep_schedule_now( conn );
4289 0 : quic->metrics.conn_aborted_cnt++;
4290 0 : return;
4291 0 : }
4292 :
4293 : /* this length includes the packet number length (pkt_number_len_enc+1),
4294 : padding and the final TAG */
4295 0 : uint quic_pkt_len = base_pkt_len + padding;
4296 :
4297 : /* set the length on the packet header */
4298 0 : uint quic_pkt_len_varint = 0x4000u | fd_uint_min( quic_pkt_len, 0x3fff );
4299 0 : FD_STORE( ushort, hdr_len_field, fd_ushort_bswap( (ushort)quic_pkt_len_varint ) );
4300 :
4301 : /* add padding */
4302 0 : if( padding ) {
4303 0 : fd_memset( payload_ptr, 0, padding );
4304 0 : payload_ptr += padding;
4305 0 : }
4306 :
4307 : /* everything successful up to here
4308 : encrypt into tx_ptr,tx_ptr+tx_sz */
4309 :
4310 : #if FD_QUIC_DISABLE_CRYPTO
4311 : ulong quic_pkt_sz = hdr_sz + tot_frame_sz + padding;
4312 : fd_memcpy( conn->tx_ptr, hdr_ptr, quic_pkt_sz );
4313 : conn->tx_ptr += quic_pkt_sz;
4314 :
4315 : /* append MAC tag */
4316 : memset( conn->tx_ptr, 0, FD_QUIC_CRYPTO_TAG_SZ );
4317 : conn->tx_ptr += FD_QUIC_CRYPTO_TAG_SZ;
4318 : #else
4319 0 : ulong cipher_text_sz = fd_quic_conn_tx_buf_remaining( conn );
4320 0 : ulong frames_sz = (ulong)( payload_ptr - frame_start ); /* including padding */
4321 :
4322 0 : fd_quic_crypto_keys_t * hp_keys = &conn->keys[enc_level][1];
4323 0 : fd_quic_crypto_keys_t * pkt_keys = key_phase_upd ? &conn->new_keys[1] : &conn->keys[enc_level][1];
4324 :
4325 0 : if( FD_UNLIKELY( fd_quic_crypto_encrypt( conn->tx_ptr, &cipher_text_sz, hdr_ptr, hdr_sz,
4326 0 : frame_start, frames_sz, pkt_keys, hp_keys, pkt_number ) != FD_QUIC_SUCCESS ) ) {
4327 0 : FD_LOG_WARNING(( "fd_quic_crypto_encrypt failed" ));
4328 :
4329 : /* this situation is unlikely to improve, so kill the connection */
4330 0 : fd_quic_set_conn_state( conn, FD_QUIC_CONN_STATE_DEAD );
4331 0 : fd_quic_svc_prep_schedule_now( conn );
4332 0 : quic->metrics.conn_aborted_cnt++;
4333 0 : break;
4334 0 : }
4335 :
4336 0 : conn->tx_ptr += cipher_text_sz;
4337 0 : #endif
4338 :
4339 : /* we have committed the packet into the buffer, so inc pkt_number */
4340 0 : conn->pkt_number[pn_space]++;
4341 :
4342 0 : if( enc_level == fd_quic_enc_level_appdata_id ) {
4343 : /* short header must be last in datagram
4344 : so send in packet immediately */
4345 0 : fd_quic_tx_buffered( quic, conn );
4346 :
4347 0 : if( conn->tx_ptr == conn->tx_buf_conn ) {
4348 0 : enc_level = fd_quic_tx_enc_level( conn, 0 /* acks */ );
4349 0 : continue;
4350 0 : }
4351 :
4352 : /* TODO count here */
4353 :
4354 : /* drop packet */
4355 : /* this is a workaround for leaving a short=header-packet in the buffer
4356 : for the next tx_conn call. Next time around the tx_conn call will
4357 : not be aware that the buffer cannot be added to */
4358 0 : conn->tx_ptr = conn->tx_buf_conn;
4359 :
4360 0 : break;
4361 0 : }
4362 :
4363 : /* Refresh enc_level in case we can coalesce another packet */
4364 0 : enc_level = fd_quic_tx_enc_level( conn, 0 /* acks */ );
4365 0 : FD_DEBUG( if( enc_level!=~0u) FD_LOG_DEBUG(( "Attempting to append enc_level=%u packet", enc_level )); )
4366 0 : }
4367 :
4368 : /* try to send? */
4369 0 : fd_quic_tx_buffered( quic, conn );
4370 0 : }
4371 :
4372 : void
4373 0 : fd_quic_conn_service( fd_quic_t * quic, fd_quic_conn_t * conn, long now ) {
4374 :
4375 : /* Send new rtt measurement probe? */
4376 0 : if( FD_UNLIKELY( now > conn->last_ack + (long)conn->rtt_period_ns ) ) {
4377 : /* send PING */
4378 0 : if( !( conn->flags & ( FD_QUIC_CONN_FLAGS_PING | FD_QUIC_CONN_FLAGS_PING_SENT ) ) ) {
4379 0 : conn->flags |= FD_QUIC_CONN_FLAGS_PING;
4380 0 : conn->upd_pkt_number = FD_QUIC_PKT_NUM_PENDING; /* update to be sent in next packet */
4381 0 : }
4382 0 : }
4383 :
4384 : /* handle expiry on pkt_meta */
4385 0 : fd_quic_pkt_meta_retry( quic, conn, 0 /* don't force */, ~0u /* all enc_levels */ );
4386 :
4387 : /* check state
4388 : need reset?
4389 : need close?
4390 : need acks?
4391 : replies?
4392 : data to send?
4393 : dead */
4394 0 : switch( conn->state ) {
4395 0 : case FD_QUIC_CONN_STATE_HANDSHAKE:
4396 0 : case FD_QUIC_CONN_STATE_HANDSHAKE_COMPLETE:
4397 0 : {
4398 0 : if( conn->tls_hs ) {
4399 : /* if we're the server, we send "handshake-done" frame */
4400 0 : fd_quic_server_handshake_confirm( quic, conn );
4401 :
4402 : /* if we're the client, fd_quic_conn_tx will flush the hs
4403 : buffer so we can receive the HANDSHAKE_DONE frame, and
4404 : transition from CONN_STATE HANDSHAKE_COMPLETE to ACTIVE. */
4405 0 : }
4406 :
4407 : /* do we have data to transmit? */
4408 0 : fd_quic_conn_tx( quic, conn );
4409 :
4410 0 : break;
4411 0 : }
4412 :
4413 0 : case FD_QUIC_CONN_STATE_CLOSE_PENDING:
4414 0 : case FD_QUIC_CONN_STATE_PEER_CLOSE:
4415 : /* user requested close, and may have set a reason code */
4416 : /* transmit the failure reason */
4417 0 : fd_quic_conn_tx( quic, conn );
4418 :
4419 : /* schedule another fd_quic_conn_service to free the conn */
4420 0 : fd_quic_set_conn_state( conn, FD_QUIC_CONN_STATE_DEAD ); /* TODO need draining state wait for 3 * TPO */
4421 0 : quic->metrics.conn_closed_cnt++;
4422 :
4423 0 : break;
4424 :
4425 0 : case FD_QUIC_CONN_STATE_ABORT:
4426 : /* transmit the failure reason */
4427 0 : fd_quic_conn_tx( quic, conn );
4428 :
4429 : /* schedule another fd_quic_conn_service to free the conn */
4430 0 : fd_quic_set_conn_state( conn, FD_QUIC_CONN_STATE_DEAD );
4431 0 : quic->metrics.conn_aborted_cnt++;
4432 :
4433 0 : break;
4434 :
4435 0 : case FD_QUIC_CONN_STATE_ACTIVE:
4436 : /* do we have data to transmit? */
4437 0 : fd_quic_conn_tx( quic, conn );
4438 :
4439 0 : break;
4440 :
4441 0 : case FD_QUIC_CONN_STATE_DEAD:
4442 0 : case FD_QUIC_CONN_STATE_INVALID:
4443 : /* fall thru */
4444 0 : default:
4445 0 : FD_LOG_CRIT(( "invalid conn state %u", conn->state ));
4446 0 : return;
4447 0 : }
4448 :
4449 : /* check routing and arp for this connection */
4450 :
4451 0 : }
4452 :
4453 : void
4454 : fd_quic_conn_free( fd_quic_t * quic,
4455 0 : fd_quic_conn_t * conn ) {
4456 0 : if( FD_UNLIKELY( !conn ) ) {
4457 0 : FD_LOG_WARNING(( "NULL conn" ));
4458 0 : return;
4459 0 : }
4460 0 : if( FD_UNLIKELY( conn->state == FD_QUIC_CONN_STATE_INVALID ) ) {
4461 0 : FD_LOG_CRIT(( "double free detected" ));
4462 0 : return;
4463 0 : }
4464 :
4465 0 : FD_COMPILER_MFENCE();
4466 0 : fd_quic_set_conn_state( conn, FD_QUIC_CONN_STATE_INVALID );
4467 0 : FD_COMPILER_MFENCE();
4468 :
4469 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
4470 :
4471 : /* remove connection id from conn_map */
4472 :
4473 0 : fd_quic_conn_map_t * entry = fd_quic_conn_map_query( state->conn_map, conn->our_conn_id, NULL );
4474 0 : if( FD_LIKELY( entry ) ) fd_quic_conn_map_remove( state->conn_map, entry );
4475 :
4476 : /* no need to remove this connection from the events queue
4477 : free is called from two places:
4478 : fini - service will never be called again. All events are destroyed
4479 : service - removes event before calling free. Event only allowed to be
4480 : enqueued once */
4481 :
4482 : /* free pkt_meta */
4483 0 : for( uint j=0; j<=fd_quic_enc_level_appdata_id; ++j ) fd_quic_conn_free_pkt_meta( conn, j );
4484 :
4485 : /* remove all stream ids from map, and free stream */
4486 :
4487 : /* remove used streams */
4488 0 : fd_quic_stream_t * used_sentinel = conn->used_streams;
4489 0 : while( 1 ) {
4490 0 : fd_quic_stream_t * stream = used_sentinel->next;
4491 :
4492 0 : if( FD_UNLIKELY( stream == used_sentinel ) ) break;
4493 :
4494 0 : fd_quic_tx_stream_free( quic, conn, stream, FD_QUIC_STREAM_NOTIFY_CONN );
4495 0 : }
4496 :
4497 : /* remove send streams */
4498 0 : fd_quic_stream_t * send_sentinel = conn->send_streams;
4499 0 : while( 1 ) {
4500 0 : fd_quic_stream_t * stream = send_sentinel->next;
4501 :
4502 0 : if( FD_UNLIKELY( stream == send_sentinel ) ) break;
4503 :
4504 0 : fd_quic_tx_stream_free( quic, conn, stream, FD_QUIC_STREAM_NOTIFY_CONN );
4505 0 : }
4506 :
4507 : /* if any stream map entries are left over, remove them
4508 : this should not occur, so this branch should not execute
4509 : but if a stream doesn't get cleaned up properly, this fixes
4510 : the stream map */
4511 0 : if( FD_UNLIKELY( conn->stream_map && fd_quic_stream_map_key_cnt( conn->stream_map ) > 0 ) ) {
4512 0 : FD_LOG_WARNING(( "stream_map not empty. cnt: %lu",
4513 0 : (ulong)fd_quic_stream_map_key_cnt( conn->stream_map ) ));
4514 0 : while( fd_quic_stream_map_key_cnt( conn->stream_map ) > 0 ) {
4515 0 : int removed = 0;
4516 0 : for( ulong j = 0; j < fd_quic_stream_map_slot_cnt( conn->stream_map ); ++j ) {
4517 0 : if( conn->stream_map[j].stream_id != FD_QUIC_STREAM_ID_UNUSED ) {
4518 0 : fd_quic_stream_map_remove( conn->stream_map, &conn->stream_map[j] );
4519 0 : removed = 1;
4520 0 : j--; /* retry this entry */
4521 0 : }
4522 0 : }
4523 0 : if( !removed ) {
4524 0 : FD_LOG_WARNING(( "None removed. Remain: %lu",
4525 0 : (ulong)fd_quic_stream_map_key_cnt( conn->stream_map ) ));
4526 0 : break;
4527 0 : }
4528 0 : }
4529 0 : }
4530 :
4531 0 : if( conn->tls_hs ) {
4532 : /* free tls-hs */
4533 0 : fd_quic_tls_hs_delete( conn->tls_hs );
4534 :
4535 : /* Remove the handshake from the cache before releasing it */
4536 0 : fd_quic_tls_hs_cache_ele_remove( &state->hs_cache, conn->tls_hs, state->hs_pool);
4537 0 : fd_quic_tls_hs_pool_ele_release( state->hs_pool, conn->tls_hs );
4538 0 : }
4539 0 : conn->tls_hs = NULL;
4540 :
4541 : /* remove from service queue */
4542 0 : fd_quic_svc_timers_cancel( state->svc_timers, conn );
4543 :
4544 : /* put connection back in free list */
4545 0 : conn->free_conn_next = state->free_conn_list;
4546 0 : state->free_conn_list = conn->conn_idx;
4547 :
4548 0 : quic->metrics.conn_alloc_cnt--;
4549 :
4550 : /* clear keys */
4551 0 : memset( &conn->secrets, 0, sizeof(fd_quic_crypto_secrets_t) );
4552 0 : memset( conn->keys, 0, sizeof( conn->keys ) );
4553 0 : memset( conn->new_keys, 0, sizeof( conn->new_keys ) );
4554 0 : }
4555 :
4556 : fd_quic_conn_t *
4557 : fd_quic_connect( fd_quic_t * quic,
4558 : uint dst_ip_addr,
4559 : ushort dst_udp_port,
4560 : uint src_ip_addr,
4561 : ushort src_udp_port,
4562 0 : long now ) {
4563 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
4564 0 : state->now = now;
4565 :
4566 0 : if( FD_UNLIKELY( !fd_quic_tls_hs_pool_free( state->hs_pool ) ) ) {
4567 : /* try evicting, 0 if oldest is too young so fail */
4568 0 : if( !fd_quic_tls_hs_cache_evict( quic, state ) ) {
4569 0 : return NULL;
4570 0 : }
4571 0 : }
4572 :
4573 : /* create conn ids for us and them
4574 : client creates connection id for the peer, peer immediately replaces it */
4575 0 : ulong our_conn_id_u64 = fd_quic_rng_ulong( state );
4576 0 : fd_quic_conn_id_t peer_conn_id; fd_quic_conn_id_from_u64( &peer_conn_id, fd_quic_rng_ulong( state ) );
4577 :
4578 0 : fd_quic_conn_t * conn = fd_quic_conn_create(
4579 0 : quic,
4580 0 : our_conn_id_u64,
4581 0 : &peer_conn_id,
4582 0 : dst_ip_addr,
4583 0 : dst_udp_port,
4584 0 : src_ip_addr,
4585 0 : src_udp_port,
4586 0 : 0 /* client */ );
4587 :
4588 0 : if( FD_UNLIKELY( !conn ) ) {
4589 0 : FD_DEBUG( FD_LOG_DEBUG(( "fd_quic_conn_create failed" )) );
4590 0 : return NULL;
4591 0 : }
4592 :
4593 : /* Prepare QUIC-TLS transport params object (sent as a TLS extension).
4594 : Take template from state and mutate certain params in-place.
4595 :
4596 : See RFC 9000 Section 18 */
4597 :
4598 0 : fd_quic_transport_params_t tp[1] = { state->transport_params };
4599 :
4600 : /* The original_destination_connection_id is omitted by clients.
4601 : Since this is a mutable field, explicitly clear it here. */
4602 :
4603 0 : tp->original_destination_connection_id_present = 0;
4604 0 : tp->original_destination_connection_id_len = 0;
4605 :
4606 : /* Similarly, explicitly zero out retry fields. */
4607 0 : tp->retry_source_connection_id_present = 0;
4608 0 : tp->retry_source_connection_id_len = 0;
4609 :
4610 : /* Repeat source conn ID -- rationale see fd_quic_handle_v1_initial */
4611 :
4612 0 : FD_STORE( ulong, tp->initial_source_connection_id, conn->initial_source_conn_id );
4613 0 : tp->initial_source_connection_id_present = 1;
4614 0 : tp->initial_source_connection_id_len = FD_QUIC_CONN_ID_SZ;
4615 :
4616 : /* Create a TLS handshake (free>0 validated above) */
4617 :
4618 0 : fd_quic_tls_hs_t * tls_hs = fd_quic_tls_hs_new(
4619 0 : fd_quic_tls_hs_pool_ele_acquire( state->hs_pool ),
4620 0 : state->tls,
4621 0 : (void*)conn,
4622 0 : 0 /*is_server*/,
4623 0 : tp,
4624 0 : now );
4625 0 : if( FD_UNLIKELY( tls_hs->alert ) ) {
4626 0 : FD_LOG_WARNING(( "fd_quic_tls_hs_client_new failed" ));
4627 : /* free hs and the conn */
4628 0 : fd_quic_tls_hs_delete( tls_hs );
4629 0 : fd_quic_tls_hs_pool_ele_release( state->hs_pool, tls_hs );
4630 0 : fd_quic_conn_free( quic, conn );
4631 0 : return NULL;
4632 0 : }
4633 0 : fd_quic_tls_hs_cache_ele_push_tail( &state->hs_cache, tls_hs, state->hs_pool );
4634 :
4635 0 : quic->metrics.hs_created_cnt++;
4636 0 : conn->tls_hs = tls_hs;
4637 :
4638 0 : fd_quic_gen_initial_secret_and_keys( conn, &peer_conn_id, /* is_server */ 0 );
4639 :
4640 : /* set "called_conn_new" to indicate we should call conn_final
4641 : upon teardown */
4642 0 : conn->called_conn_new = 1;
4643 :
4644 0 : fd_quic_svc_prep_schedule( conn, now );
4645 0 : fd_quic_svc_timers_schedule( state->svc_timers, conn, now );
4646 :
4647 : /* everything initialized */
4648 0 : return conn;
4649 :
4650 0 : }
4651 :
4652 : fd_quic_conn_t *
4653 : fd_quic_conn_create( fd_quic_t * quic,
4654 : ulong our_conn_id,
4655 : fd_quic_conn_id_t const * peer_conn_id,
4656 : uint peer_ip_addr,
4657 : ushort peer_udp_port,
4658 : uint self_ip_addr,
4659 : ushort self_udp_port,
4660 0 : int server ) {
4661 0 : if( FD_UNLIKELY( !our_conn_id ) ) return NULL;
4662 0 : if( FD_UNLIKELY( !peer_ip_addr ) ) return NULL;
4663 :
4664 0 : fd_quic_config_t * config = &quic->config;
4665 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
4666 :
4667 : /* fetch top of connection free list */
4668 0 : uint conn_idx = state->free_conn_list;
4669 0 : if( FD_UNLIKELY( conn_idx==UINT_MAX ) ) {
4670 0 : FD_DEBUG( FD_LOG_DEBUG(( "fd_quic_conn_create failed: no free conn slots" )) );
4671 0 : quic->metrics.conn_err_no_slots_cnt++;
4672 0 : return NULL;
4673 0 : }
4674 0 : if( FD_UNLIKELY( conn_idx >= quic->limits.conn_cnt ) ) {
4675 0 : FD_LOG_CRIT(( "Conn free list corruption detected" ));
4676 0 : return NULL;
4677 0 : }
4678 0 : fd_quic_conn_t * conn = fd_quic_conn_at_idx( state, conn_idx );
4679 0 : if( FD_UNLIKELY( conn->state != FD_QUIC_CONN_STATE_INVALID ) ) {
4680 0 : FD_LOG_CRIT(( "conn %p not free, this is a bug", (void *)conn ));
4681 0 : return NULL;
4682 0 : }
4683 :
4684 : /* insert into conn map */
4685 0 : fd_quic_conn_map_t * insert_entry = fd_quic_conn_map_insert( state->conn_map, our_conn_id );
4686 :
4687 : /* if insert failed (should be impossible) fail, and do not remove connection
4688 : from free list */
4689 0 : if( FD_UNLIKELY( insert_entry == NULL ) ) {
4690 : /* FIXME This has ~1e-6 probability of happening with 10M conns
4691 : Retry generating our_conn_id instead of logging a warning */
4692 0 : FD_LOG_WARNING(( "fd_quic_conn_create failed: failed to register new conn ID %lu with map size %lu", our_conn_id, fd_quic_conn_map_key_cnt( state->conn_map ) ));
4693 0 : return NULL;
4694 0 : }
4695 :
4696 : /* set connection map insert_entry to new connection */
4697 0 : insert_entry->conn = conn;
4698 :
4699 : /* remove from free list */
4700 0 : state->free_conn_list = conn->free_conn_next;
4701 0 : conn->free_conn_next = UINT_MAX;
4702 :
4703 : /* if conn not marked free, skip */
4704 0 : if( FD_UNLIKELY( conn->state != FD_QUIC_CONN_STATE_INVALID ) ) {
4705 0 : FD_LOG_CRIT(( "conn %p not free, this is a bug", (void *)conn ));
4706 0 : return NULL;
4707 0 : }
4708 :
4709 : /* initialize connection members */
4710 0 : fd_quic_conn_clear( conn );
4711 :
4712 0 : conn->server = !!server;
4713 0 : conn->our_conn_id = our_conn_id;
4714 0 : conn->host = (fd_quic_net_endpoint_t){
4715 0 : .ip_addr = self_ip_addr, /* may be 0, if outgoing */
4716 0 : .udp_port = self_udp_port,
4717 0 : };
4718 0 : conn->conn_gen++;
4719 :
4720 :
4721 : /* pkt_meta */
4722 0 : fd_quic_pkt_meta_tracker_init( &conn->pkt_meta_tracker,
4723 0 : quic->limits.inflight_frame_cnt,
4724 0 : state->pkt_meta_pool );
4725 :
4726 : /* Initialize streams */
4727 0 : FD_QUIC_STREAM_LIST_SENTINEL( conn->send_streams );
4728 0 : FD_QUIC_STREAM_LIST_SENTINEL( conn->used_streams );
4729 :
4730 : /* initialize stream_id members */
4731 0 : fd_quic_conn_stream_rx_t * srx = conn->srx;
4732 0 : fd_quic_transport_params_t * our_tp = &state->transport_params;
4733 0 : srx->rx_hi_stream_id = server ? FD_QUIC_STREAM_TYPE_UNI_CLIENT : FD_QUIC_STREAM_TYPE_UNI_SERVER;
4734 0 : srx->rx_sup_stream_id = server ? FD_QUIC_STREAM_TYPE_UNI_CLIENT : FD_QUIC_STREAM_TYPE_UNI_SERVER;
4735 0 : conn->tx_next_stream_id = server ? FD_QUIC_STREAM_TYPE_UNI_SERVER : FD_QUIC_STREAM_TYPE_UNI_CLIENT;
4736 0 : conn->tx_sup_stream_id = server ? FD_QUIC_STREAM_TYPE_UNI_SERVER : FD_QUIC_STREAM_TYPE_UNI_CLIENT;
4737 :
4738 0 : srx->rx_max_data = our_tp->initial_max_data;
4739 :
4740 0 : if( state->transport_params.initial_max_streams_uni_present ) {
4741 0 : srx->rx_sup_stream_id = (state->transport_params.initial_max_streams_uni<<2) + FD_QUIC_STREAM_TYPE_UNI_CLIENT;
4742 0 : }
4743 0 : if( state->transport_params.initial_max_data ) {
4744 0 : srx->rx_max_data = state->transport_params.initial_max_data;
4745 0 : }
4746 :
4747 : /* points to free tx space */
4748 0 : conn->tx_ptr = conn->tx_buf_conn;
4749 :
4750 0 : conn->keys_avail = fd_uint_set_bit( 0U, fd_quic_enc_level_initial_id );
4751 :
4752 : /* Packet numbers left as 0
4753 : rfc9000: s12.3:
4754 : Packet numbers in each packet space start at 0.
4755 : Subsequent packets sent in the same packet number space
4756 : MUST increase the packet number by at least 1
4757 : rfc9002: s3
4758 : It is permitted for some packet numbers to never be used, leaving intentional gaps. */
4759 :
4760 0 : fd_quic_set_conn_state( conn, FD_QUIC_CONN_STATE_HANDSHAKE );
4761 :
4762 : /* start with minimum supported max datagram */
4763 : /* peers may allow more */
4764 0 : conn->tx_max_datagram_sz = FD_QUIC_INITIAL_PAYLOAD_SZ_MAX;
4765 :
4766 : /* initial source connection id */
4767 0 : conn->initial_source_conn_id = our_conn_id;
4768 :
4769 : /* peer connection id */
4770 0 : conn->peer_cids[0] = *peer_conn_id;
4771 0 : conn->peer[0].ip_addr = peer_ip_addr;
4772 0 : conn->peer[0].udp_port = peer_udp_port;
4773 :
4774 0 : fd_quic_ack_gen_init( conn->ack_gen );
4775 :
4776 : /* initial rtt */
4777 : /* overridden when acks start returning */
4778 0 : fd_rtt_estimate_t * rtt = conn->rtt;
4779 :
4780 0 : ulong peer_ack_delay_exponent = 3UL; /* by spec, default is 3 */
4781 0 : conn->peer_ack_delay_scale = (float)( 1UL << peer_ack_delay_exponent ) * 1e3f;
4782 0 : conn->peer_max_ack_delay_ns = 0.0f; /* starts at zero, since peers respond immediately to */
4783 : /* INITIAL and HANDSHAKE */
4784 : /* updated when we get transport parameters */
4785 0 : rtt->smoothed_rtt = FD_QUIC_INITIAL_RTT_US * 1e3f;
4786 0 : rtt->latest_rtt = FD_QUIC_INITIAL_RTT_US * 1e3f;
4787 0 : rtt->min_rtt = FD_QUIC_INITIAL_RTT_US * 1e3f;
4788 0 : rtt->var_rtt = FD_QUIC_INITIAL_RTT_US * 1e3f * 0.5f;
4789 0 : conn->rtt_period_ns = FD_QUIC_RTT_PERIOD_US * 1e3f;
4790 0 : conn->last_ack = state->now;
4791 :
4792 : /* idle timeout */
4793 0 : conn->idle_timeout_ns = config->idle_timeout;
4794 0 : conn->last_activity = state->now;
4795 0 : if( !conn->last_activity ) FD_LOG_CRIT(( "last activity: %ld", conn->last_activity ));
4796 0 : conn->let_die_time_ns = LONG_MAX;
4797 :
4798 : /* update metrics */
4799 0 : quic->metrics.conn_alloc_cnt++;
4800 0 : quic->metrics.conn_created_cnt++;
4801 :
4802 0 : fd_quic_svc_timers_init_conn( conn );
4803 :
4804 : /* prep idle timeout or keep alive at idle timeout/2 */
4805 0 : long delay = quic->config.idle_timeout>>(quic->config.keep_alive);
4806 0 : fd_quic_svc_prep_schedule( conn, state->now+delay );
4807 :
4808 : /* return connection */
4809 0 : return conn;
4810 0 : }
4811 :
4812 : long
4813 0 : fd_quic_get_next_wakeup( fd_quic_t * quic ) {
4814 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
4815 0 : long now = state->now;
4816 0 : fd_quic_svc_event_t next = fd_quic_svc_timers_next( state->svc_timers, now, 0 );
4817 0 : return next.timeout;
4818 0 : }
4819 :
4820 : /* frame handling function default definitions */
4821 : static ulong
4822 : fd_quic_handle_padding_frame(
4823 : fd_quic_frame_ctx_t * ctx FD_PARAM_UNUSED,
4824 : fd_quic_padding_frame_t * data FD_PARAM_UNUSED,
4825 : uchar const * const p0,
4826 0 : ulong p_sz ) {
4827 0 : uchar const * p = p0;
4828 0 : uchar const * const p_end = p + p_sz;
4829 0 : while( p<p_end && p[0]==0 ) p++;
4830 0 : return (ulong)( p - p0 );
4831 0 : }
4832 :
4833 : static ulong
4834 : fd_quic_handle_ping_frame(
4835 : fd_quic_frame_ctx_t * ctx,
4836 : fd_quic_ping_frame_t * data FD_PARAM_UNUSED,
4837 : uchar const * p0,
4838 0 : ulong p_sz ) {
4839 0 : FD_DTRACE_PROBE_1( quic_handle_ping_frame, ctx->conn->our_conn_id );
4840 : /* skip pings and pads */
4841 0 : uchar const * p = p0;
4842 0 : uchar const * const p_end = p + p_sz;
4843 0 : while( p < p_end && ((uint)p[0] & 0xfeu) == 0 ) p++;
4844 0 : return (ulong)( p - p0 );
4845 0 : }
4846 :
4847 : /* Retry packet metadata
4848 : This will force pkt_meta to be returned to the free list
4849 : for use. It does so by finding unack'ed packet metadata
4850 : and setting the data up for retransmission.
4851 : force_below_pkt_num will force retry of all frames
4852 : with pkt_num < force_below_pkt_num.
4853 : 'arg_enc_level' is the enc_level to retry, or can be
4854 : set to ~0u for all enc_levels.
4855 : */
4856 : void
4857 : fd_quic_pkt_meta_retry( fd_quic_t * quic,
4858 : fd_quic_conn_t * conn,
4859 : ulong force_below_pkt_num,
4860 0 : uint arg_enc_level ) {
4861 0 : fd_quic_conn_stream_rx_t * srx = conn->srx;
4862 :
4863 0 : long now = fd_quic_get_state( quic )->now;
4864 :
4865 0 : fd_quic_pkt_meta_tracker_t * tracker = &conn->pkt_meta_tracker;
4866 0 : fd_quic_pkt_meta_t * pool = tracker->pool;
4867 :
4868 : /* used for metric tracking */
4869 0 : ulong prev_retx_pkt_num[FD_QUIC_NUM_ENC_LEVELS] = { ~0ul, ~0ul, ~0ul, ~0ul };
4870 :
4871 0 : long const pto_duration = fd_quic_calc_expiry_duration( conn, 0, conn->server );
4872 0 : long const loss_duration = fd_quic_calc_expiry_duration( conn, 1, conn->server );
4873 :
4874 0 : int pto_expired = 0;
4875 :
4876 0 : while(1) {
4877 : /* find earliest expiring pkt_meta, over smallest pkt number at each enc_level */
4878 0 : fd_quic_pkt_meta_t * pkt_meta = NULL;
4879 0 : long expiry = LONG_MAX;
4880 0 : for( uint j=0u; j<4u; ++j ) {
4881 : /* if arg_enc_level set, only consider that enc_level */
4882 0 : if( !(arg_enc_level==~0u) & !(j==arg_enc_level) ) continue;
4883 :
4884 0 : fd_quic_pkt_meta_t * pkt_meta_cand = fd_quic_pkt_meta_min( &tracker->sent_pkt_metas[j], pool );
4885 0 : if( !pkt_meta_cand ) continue;
4886 :
4887 0 : uint const pn_space = fd_quic_enc_level_to_pn_space( j );
4888 0 : ulong const highest_acked = conn->highest_acked[pn_space];
4889 0 : long const cand_duration = fd_long_if( pkt_meta_cand->key.pkt_num < highest_acked, loss_duration, pto_duration );
4890 :
4891 0 : pkt_meta_cand->expiry = fd_long_min( pkt_meta_cand->tx_time + cand_duration, pkt_meta_cand->expiry );
4892 0 : if( !conn->server && pkt_meta_cand->key.pkt_num>=highest_acked ) {
4893 0 : pkt_meta_cand->expiry = fd_long_max( pkt_meta_cand->expiry, conn->pto_expiry );
4894 0 : }
4895 :
4896 0 : if( !pkt_meta || pkt_meta_cand->expiry < expiry ) {
4897 0 : pkt_meta = pkt_meta_cand;
4898 0 : expiry = pkt_meta_cand->expiry;
4899 0 : }
4900 0 : }
4901 :
4902 0 : if( !pkt_meta ) break;
4903 :
4904 0 : uint const enc_level = pkt_meta->enc_level;
4905 0 : ulong const pkt_num = pkt_meta->key.pkt_num;
4906 :
4907 : /* Continue until nothing expired nor to be skipped */
4908 0 : if( !!(pkt_num >= force_below_pkt_num) & !!(expiry > now) ) {
4909 : /* safe even when expiry is LONG_MAX, because prep_schedule takes min */
4910 0 : fd_quic_svc_prep_schedule( conn, expiry );
4911 0 : break;
4912 0 : };
4913 :
4914 0 : pto_expired |= (pkt_num >= force_below_pkt_num) &
4915 0 : (pkt_num >= conn->highest_acked[ fd_quic_enc_level_to_pn_space( enc_level ) ]);
4916 :
4917 0 : uint type = pkt_meta->key.type;
4918 0 : int retx = type!=FD_QUIC_PKT_META_TYPE_DATAGRAM; /* a lost DATAGRAM is only reclaimed */
4919 :
4920 0 : quic->metrics.pkt_retransmissions_cnt[enc_level] += (ulong)( retx & !(pkt_meta->key.pkt_num == prev_retx_pkt_num[enc_level]) );
4921 0 : prev_retx_pkt_num[enc_level] = pkt_num;
4922 :
4923 0 : FD_DTRACE_PROBE_4( quic_pkt_meta_retry, conn->our_conn_id, pkt_num, expiry, type);
4924 : /* set the data to retry */
4925 0 : switch( type ) {
4926 0 : case FD_QUIC_PKT_META_TYPE_HS_DATA:
4927 0 : do {
4928 0 : ulong offset = fd_ulong_max( conn->hs_ackd_bytes[enc_level], pkt_meta->val.range.offset_lo );
4929 0 : if( offset < conn->hs_sent_bytes[enc_level] ) {
4930 0 : conn->hs_sent_bytes[enc_level] = offset;
4931 0 : conn->upd_pkt_number = FD_QUIC_PKT_NUM_PENDING;
4932 0 : }
4933 0 : } while(0);
4934 0 : break;
4935 :
4936 0 : case FD_QUIC_PKT_META_TYPE_STREAM:
4937 0 : do {
4938 0 : ulong stream_id = pkt_meta->key.stream_id;
4939 :
4940 : /* find the stream */
4941 0 : fd_quic_stream_t * stream = NULL;
4942 0 : fd_quic_stream_map_t * stream_entry = fd_quic_stream_map_query( conn->stream_map, stream_id, NULL );
4943 0 : if( FD_LIKELY( stream_entry && stream_entry->stream &&
4944 0 : ( stream_entry->stream->stream_flags & FD_QUIC_STREAM_FLAGS_DEAD ) == 0 ) ) {
4945 0 : stream = stream_entry->stream;
4946 :
4947 : /* do not try sending data that has been acked */
4948 0 : ulong offset = fd_ulong_max( pkt_meta->val.range.offset_lo, stream->unacked_low );
4949 :
4950 : /* This pkt_meta may be stale: when ACK-driven loss detection
4951 : force-retries an earlier pkt_meta for the same stream, the
4952 : retransmitted data can be ACKed (advancing unacked_low)
4953 : before this pkt_meta expires. Skip the retry if the
4954 : stream has nothing left to send. */
4955 0 : if( FD_UNLIKELY( offset>=stream->tx_buf.head &&
4956 0 : !( stream->state & FD_QUIC_STREAM_STATE_TX_FIN ) ) ) {
4957 0 : break;
4958 0 : }
4959 :
4960 : /* any data left to retry? */
4961 0 : stream->tx_sent = fd_ulong_min( stream->tx_sent, offset );
4962 :
4963 : /* We must have something to send if the stream was found */
4964 0 : if( !( (stream->tx_sent < stream->tx_buf.head) | (stream->state & FD_QUIC_STREAM_STATE_TX_FIN) ) ) {
4965 0 : FD_LOG_CRIT(( "unexpected retry for completed stream %lu", stream_id ));
4966 0 : }
4967 :
4968 : /* insert into send list */
4969 0 : FD_QUIC_STREAM_LIST_REMOVE( stream );
4970 0 : FD_QUIC_STREAM_LIST_INSERT_BEFORE( conn->send_streams, stream );
4971 :
4972 : /* set the data to go out on the next packet */
4973 0 : stream->stream_flags |= FD_QUIC_STREAM_FLAGS_UNSENT; /* we have unsent data */
4974 0 : stream->upd_pkt_number = FD_QUIC_PKT_NUM_PENDING;
4975 0 : }
4976 0 : } while(0);
4977 0 : break;
4978 :
4979 0 : case FD_QUIC_PKT_META_TYPE_HS_DONE:
4980 0 : if( FD_LIKELY( !conn->handshake_done_ackd ) ) {
4981 0 : conn->handshake_done_send = 1;
4982 0 : conn->upd_pkt_number = FD_QUIC_PKT_NUM_PENDING;
4983 0 : }
4984 0 : break;
4985 :
4986 0 : case FD_QUIC_PKT_META_TYPE_MAX_DATA:
4987 0 : if( srx->rx_max_data_ackd < srx->rx_max_data ) {
4988 0 : conn->flags |= FD_QUIC_CONN_FLAGS_MAX_DATA;
4989 0 : conn->upd_pkt_number = FD_QUIC_PKT_NUM_PENDING;
4990 0 : }
4991 0 : break;
4992 :
4993 0 : case FD_QUIC_PKT_META_TYPE_MAX_STREAMS_UNIDIR:
4994 0 : do {
4995 : /* do we still need to send? */
4996 : /* get required value */
4997 0 : ulong max_streams_unidir = srx->rx_sup_stream_id >> 2;
4998 :
4999 0 : if( max_streams_unidir > srx->rx_max_streams_unidir_ackd ) {
5000 : /* set the data to go out on the next packet */
5001 0 : conn->flags |= FD_QUIC_CONN_FLAGS_MAX_STREAMS_UNIDIR;
5002 0 : conn->upd_pkt_number = FD_QUIC_PKT_NUM_PENDING;
5003 0 : }
5004 0 : } while(0);
5005 0 : break;
5006 :
5007 0 : case FD_QUIC_PKT_META_TYPE_CLOSE:
5008 0 : conn->flags &= ~FD_QUIC_CONN_FLAGS_CLOSE_SENT;
5009 0 : conn->upd_pkt_number = FD_QUIC_PKT_NUM_PENDING;
5010 0 : break;
5011 :
5012 0 : case FD_QUIC_PKT_META_TYPE_PING:
5013 0 : conn->flags = ( conn->flags & ~FD_QUIC_CONN_FLAGS_PING_SENT )
5014 0 : | FD_QUIC_CONN_FLAGS_PING;
5015 0 : conn->upd_pkt_number = FD_QUIC_PKT_NUM_PENDING;
5016 0 : break;
5017 0 : }
5018 :
5019 : /* reschedule to ensure the data gets processed */
5020 0 : if( retx ) fd_quic_svc_prep_schedule_now( conn );
5021 :
5022 0 : fd_quic_pkt_meta_remove( &tracker->sent_pkt_metas[enc_level], pool, pkt_meta );
5023 0 : conn->used_pkt_meta--;
5024 0 : }
5025 :
5026 : /* RFC 9002 Section 6.2.1: back off the PTO once per timer expiration */
5027 0 : if( pto_expired ) {
5028 0 : conn->pto_count = fd_uint_min( conn->pto_count+1U, FD_QUIC_PTO_COUNT_MAX );
5029 0 : conn->pto_expiry = now + fd_quic_calc_expiry_duration( conn, 0, conn->server );
5030 0 : }
5031 0 : }
5032 :
5033 : /* reclaim resources associated with packet metadata
5034 : this is called in response to received acks */
5035 : void
5036 : fd_quic_reclaim_pkt_meta( fd_quic_conn_t * conn,
5037 : fd_quic_pkt_meta_t * pkt_meta,
5038 0 : uint enc_level ) {
5039 0 : fd_quic_conn_stream_rx_t * srx = conn->srx;
5040 :
5041 0 : uint type = pkt_meta->key.type;
5042 0 : fd_quic_range_t range = pkt_meta->val.range;
5043 :
5044 0 : switch( type ) {
5045 :
5046 0 : case FD_QUIC_PKT_META_TYPE_PING:
5047 0 : do {
5048 0 : conn->flags &= ~( FD_QUIC_CONN_FLAGS_PING | FD_QUIC_CONN_FLAGS_PING_SENT );
5049 0 : } while(0);
5050 0 : break;
5051 :
5052 0 : case FD_QUIC_PKT_META_TYPE_HS_DATA:
5053 0 : do {
5054 : /* Note that tls_hs could already be freed */
5055 : /* is this ack'ing the next consecutive bytes?
5056 : if so, we can increase the ack'd bytes
5057 : if not, we retransmit the bytes expected to be ack'd
5058 : we assume a gap means a dropped packet, and
5059 : this policy allows us to free up the pkt_meta here */
5060 0 : ulong hs_ackd_bytes = conn->hs_ackd_bytes[enc_level];
5061 0 : if( range.offset_lo <= hs_ackd_bytes ) {
5062 0 : hs_ackd_bytes = conn->hs_ackd_bytes[enc_level]
5063 0 : = fd_ulong_max( hs_ackd_bytes, range.offset_hi );
5064 :
5065 : /* remove any unused hs_data */
5066 0 : fd_quic_tls_hs_data_t * hs_data = NULL;
5067 :
5068 0 : hs_data = fd_quic_tls_get_hs_data( conn->tls_hs, enc_level );
5069 0 : while( hs_data && hs_data->offset + hs_data->data_sz <= hs_ackd_bytes ) {
5070 0 : fd_quic_tls_pop_hs_data( conn->tls_hs, enc_level );
5071 0 : hs_data = fd_quic_tls_get_hs_data( conn->tls_hs, enc_level );
5072 0 : }
5073 0 : } else {
5074 0 : conn->hs_sent_bytes[enc_level] =
5075 0 : fd_ulong_min( conn->hs_sent_bytes[enc_level], hs_ackd_bytes );
5076 0 : conn->upd_pkt_number = FD_QUIC_PKT_NUM_PENDING;
5077 0 : }
5078 0 : } while(0);
5079 0 : break;
5080 :
5081 0 : case FD_QUIC_PKT_META_TYPE_HS_DONE:
5082 0 : do {
5083 0 : conn->handshake_done_ackd = 1;
5084 0 : conn->handshake_done_send = 0;
5085 0 : if( FD_LIKELY( conn->tls_hs ) ) {
5086 0 : fd_quic_state_t * state = fd_quic_get_state( conn->quic );
5087 0 : fd_quic_tls_hs_delete( conn->tls_hs );
5088 0 : fd_quic_tls_hs_cache_ele_remove( &state->hs_cache, conn->tls_hs, state->hs_pool );
5089 0 : fd_quic_tls_hs_pool_ele_release( state->hs_pool, conn->tls_hs );
5090 0 : conn->tls_hs = NULL;
5091 0 : }
5092 0 : } while(0);
5093 0 : break;
5094 :
5095 0 : case FD_QUIC_PKT_META_TYPE_MAX_DATA:
5096 0 : do {
5097 0 : ulong max_data_ackd = pkt_meta->val.scalar;
5098 :
5099 : /* ack can only increase max_data_ackd */
5100 0 : max_data_ackd = fd_ulong_max( max_data_ackd, srx->rx_max_data_ackd );
5101 :
5102 : /* max_data_ackd > rx_max_data is a protocol violation */
5103 0 : if( FD_UNLIKELY( max_data_ackd > srx->rx_max_data ) ) {
5104 : /* this is a protocol violation, so inform the peer */
5105 0 : fd_quic_conn_error( conn, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
5106 0 : return;
5107 0 : }
5108 :
5109 : /* clear flag only if acked value == current value */
5110 0 : if( FD_LIKELY( max_data_ackd == srx->rx_max_data ) ) {
5111 0 : conn->flags &= ~FD_QUIC_CONN_FLAGS_MAX_DATA;
5112 0 : }
5113 :
5114 : /* set the ackd value */
5115 0 : srx->rx_max_data_ackd = max_data_ackd;
5116 0 : } while(0);
5117 0 : break;
5118 :
5119 0 : case FD_QUIC_PKT_META_TYPE_MAX_STREAMS_UNIDIR:
5120 0 : do {
5121 0 : ulong max_streams_unidir_ackd = pkt_meta->val.scalar;
5122 :
5123 : /* ack can only increase max_streams_unidir_ackd */
5124 0 : max_streams_unidir_ackd = fd_ulong_max( max_streams_unidir_ackd, srx->rx_max_streams_unidir_ackd );
5125 :
5126 : /* get required value */
5127 0 : ulong max_streams_unidir = srx->rx_sup_stream_id >> 2;
5128 :
5129 : /* clear flag only if acked value == current value */
5130 0 : if( FD_LIKELY( max_streams_unidir_ackd == max_streams_unidir ) ) {
5131 0 : conn->flags &= ~FD_QUIC_CONN_FLAGS_MAX_STREAMS_UNIDIR;
5132 0 : }
5133 :
5134 : /* set the ackd value */
5135 0 : srx->rx_max_streams_unidir_ackd = max_streams_unidir_ackd;
5136 0 : } while(0);
5137 0 : break;
5138 :
5139 0 : case FD_QUIC_PKT_META_TYPE_STREAM:
5140 0 : do {
5141 0 : ulong stream_id = pkt_meta->key.stream_id;
5142 0 : fd_quic_range_t range = pkt_meta->val.range;
5143 :
5144 : /* find the stream */
5145 0 : fd_quic_stream_t * stream = NULL;
5146 0 : fd_quic_stream_map_t * stream_entry = fd_quic_stream_map_query( conn->stream_map, stream_id, NULL );
5147 0 : if( FD_LIKELY( stream_entry && stream_entry->stream &&
5148 0 : ( stream_entry->stream->stream_flags & FD_QUIC_STREAM_FLAGS_DEAD ) == 0 ) ) {
5149 0 : stream = stream_entry->stream;
5150 :
5151 0 : ulong tx_tail = stream->unacked_low;
5152 0 : ulong tx_sent = stream->tx_sent;
5153 :
5154 : /* ignore bytes which were already acked */
5155 0 : range.offset_lo = fd_ulong_max( range.offset_lo, tx_tail );
5156 :
5157 : /* verify offset_hi */
5158 0 : if( FD_UNLIKELY( range.offset_hi > stream->tx_buf.head ) ) {
5159 : /* offset_hi in the pkt_meta (the highest byte offset in the packet */
5160 : /* should never exceed tx_buf.head - the highest byte offset in the */
5161 : /* stream */
5162 0 : fd_quic_conn_error( conn, FD_QUIC_CONN_REASON_INTERNAL_ERROR, __LINE__ );
5163 0 : return;
5164 0 : }
5165 :
5166 0 : uchar * tx_ack = stream->tx_ack;
5167 : /* did they ack the first unacked byte? */
5168 0 : if( FD_LIKELY( range.offset_lo == tx_tail ) ) {
5169 : /* move tail to first unacked byte */
5170 0 : tx_tail = fd_ulong_max( range.offset_hi, tx_tail );
5171 0 : while( tx_tail < tx_sent ) {
5172 : /* TODO - optimize this for larger strides */
5173 : /* can we skip a whole byte? */
5174 0 : if( ( tx_tail & 7ul ) == 0ul && tx_tail + 8ul <= tx_sent && tx_ack[tx_tail>>3ul] == 0xffu ) {
5175 0 : tx_tail += 8ul;
5176 0 : } else {
5177 0 : if( tx_ack[tx_tail>>3ul] & ( 1u << ( tx_tail & 7u ) ) ) {
5178 0 : tx_tail++;
5179 0 : } else {
5180 0 : break;
5181 0 : }
5182 0 : }
5183 0 : }
5184 0 : stream->unacked_low = tx_tail;
5185 0 : } else {
5186 : /* mark this range as acked in tx_ack, so we can skip it later */
5187 : /* TODO optimize this for larger strides */
5188 0 : for( ulong k = range.offset_lo; k < range.offset_hi; ) {
5189 0 : if( ( k & 7ul ) == 0ul && k + 8ul <= range.offset_hi ) {
5190 : /* set whole byte */
5191 0 : tx_ack[k>>3ul] = 0xffu;
5192 0 : k += 8ul;
5193 0 : } else {
5194 : /* compiler is not smart enough to know ( 1u << ( k & 7u ) ) fits in a uchar */
5195 0 : tx_ack[k>>3ul] |= (uchar)( 1ul << ( k & 7ul ) );
5196 0 : k++;
5197 0 : }
5198 0 : }
5199 0 : }
5200 :
5201 : /* For convenience */
5202 0 : uint fin_state_mask = FD_QUIC_STREAM_STATE_TX_FIN | FD_QUIC_STREAM_STATE_RX_FIN;
5203 :
5204 : /* Free stream if it's done:
5205 : 1. peer has acked every data byte user has tried to send
5206 : 2. peer has acked the fin --> user has fin'd */
5207 0 : if( tx_tail == stream->tx_buf.head &&
5208 0 : ( stream->state & fin_state_mask ) == fin_state_mask ) {
5209 : /* fd_quic_tx_stream_free also notifies the user */
5210 0 : fd_quic_tx_stream_free( conn->quic, conn, stream, FD_QUIC_STREAM_NOTIFY_END );
5211 0 : }
5212 0 : }
5213 0 : } while(0);
5214 0 : break;
5215 0 : }
5216 0 : }
5217 :
5218 : /* process ack range
5219 : applies to pkt_number in [largest_ack - ack_range, largest_ack] */
5220 : ulong
5221 : fd_quic_process_ack_range( fd_quic_conn_t * conn,
5222 : fd_quic_frame_ctx_t * context,
5223 : uint enc_level,
5224 : ulong largest_ack,
5225 : ulong ack_range,
5226 : int is_largest,
5227 : long now,
5228 0 : ulong ack_delay ) {
5229 0 : fd_quic_pkt_t * pkt = context->pkt;
5230 :
5231 : /* inclusive range */
5232 0 : ulong hi = largest_ack;
5233 0 : ulong lo = largest_ack - ack_range;
5234 0 : FD_DTRACE_PROBE_4( quic_process_ack_range, conn->our_conn_id, enc_level, lo, hi );
5235 :
5236 0 : fd_quic_pkt_meta_tracker_t * tracker = &conn->pkt_meta_tracker;
5237 0 : fd_quic_pkt_meta_t * pool = tracker->pool;
5238 0 : fd_quic_pkt_meta_ds_t * sent = &tracker->sent_pkt_metas[enc_level];
5239 :
5240 : /* start at oldest sent */
5241 0 : for( fd_quic_pkt_meta_ds_fwd_iter_t iter = fd_quic_pkt_meta_ds_idx_ge( sent, lo, pool );
5242 0 : !fd_quic_pkt_meta_ds_fwd_iter_done( iter );
5243 0 : iter = fd_quic_pkt_meta_ds_fwd_iter_next( iter, pool ) ) {
5244 0 : fd_quic_pkt_meta_t * e = fd_quic_pkt_meta_ds_fwd_iter_ele( iter, pool );
5245 0 : if( FD_UNLIKELY( e->key.pkt_num > hi ) ) break;
5246 0 : if( is_largest && e->key.pkt_num == hi && hi >= pkt->rtt_pkt_number ) {
5247 0 : pkt->rtt_pkt_number = hi;
5248 0 : pkt->rtt_ack_time = now - e->tx_time; /* in ns */
5249 0 : pkt->rtt_ack_delay = ack_delay; /* in peer units */
5250 0 : }
5251 0 : fd_quic_reclaim_pkt_meta( conn, e, enc_level );
5252 0 : }
5253 :
5254 0 : ulong acked = fd_quic_pkt_meta_remove_range( sent, pool, lo, hi );
5255 0 : conn->used_pkt_meta -= acked;
5256 :
5257 0 : if( enc_level==fd_quic_enc_level_appdata_id ) fd_quic_cb_ack_range( conn->quic, conn, lo, hi );
5258 0 : return acked;
5259 0 : }
5260 :
5261 : static ulong
5262 : fd_quic_handle_ack_frame( fd_quic_frame_ctx_t * context,
5263 : fd_quic_ack_frame_t * data,
5264 : uchar const * p,
5265 0 : ulong p_sz ) {
5266 0 : fd_quic_conn_t * conn = context->conn;
5267 0 : uint enc_level = context->pkt->enc_level;
5268 0 : uint pn_space = fd_quic_enc_level_to_pn_space( enc_level );
5269 0 : ulong const largest_ack = data->largest_ack;
5270 :
5271 0 : if( FD_UNLIKELY( data->first_ack_range > largest_ack ) ) {
5272 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
5273 0 : return FD_QUIC_PARSE_FAIL;
5274 0 : }
5275 :
5276 0 : if( FD_UNLIKELY( largest_ack >= conn->pkt_number[pn_space] ) ) {
5277 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
5278 0 : return FD_QUIC_PARSE_FAIL;
5279 0 : }
5280 :
5281 : /* update highest_acked */
5282 0 : conn->highest_acked[pn_space] = fd_ulong_max( conn->highest_acked[pn_space], largest_ack );
5283 :
5284 0 : fd_quic_state_t * state = fd_quic_get_state( conn->quic );
5285 0 : long const now = state->now;
5286 0 : /******/ conn->last_ack = now;
5287 :
5288 : /* RFC 9002, Section 6.1: We declare a packet p lost if either:
5289 : 1. It was 'skipped': at least three packets were sent after p but before
5290 : the highest ack'ed packet in this ack frame, e.g. (p x y z highest_acked).
5291 : 2. It 'expired': p was sent at least time-threshold time ago.
5292 : Time-threshold is computed in calc_expiry, and typically differs from
5293 : pkt_meta->expiry.
5294 :
5295 : Let skip_ceil be the smallest pkt_num such that any unacked pkt_meta
5296 : with pkt_num < skip_ceil is 'skipped' as defined above. We compute this
5297 : before removing acked packets from the tracker.
5298 :
5299 : We unfortunately can't just use 'largest_ack-3' because 1) largest_ack'd
5300 : may have been previously acknowledged and 2) we may have skipped pkt_nums.
5301 : */
5302 0 : ulong skip_ceil = 0UL;
5303 0 : if( FD_LIKELY( largest_ack > 0UL ) ) {
5304 0 : #define FD_QUIC_K_PACKET_THRESHOLD 3
5305 0 : fd_quic_pkt_meta_tracker_t * tracker = &conn->pkt_meta_tracker;
5306 0 : fd_quic_pkt_meta_t * pool = tracker->pool;
5307 0 : fd_quic_pkt_meta_ds_t * sent = &tracker->sent_pkt_metas[enc_level];
5308 :
5309 0 : uint pkt_num_changes = 0;
5310 0 : ulong prev_pkt_num = ~0UL;
5311 0 : fd_quic_pkt_meta_ds_fwd_iter_t start = fd_quic_pkt_meta_ds_idx_le( sent, pool, largest_ack-1 );
5312 0 : for( fd_quic_pkt_meta_ds_rev_iter_t iter = start; /* rev<>fwd iter */
5313 0 : !!(pkt_num_changes<FD_QUIC_K_PACKET_THRESHOLD) & !fd_quic_pkt_meta_ds_rev_iter_done(iter);
5314 0 : iter=fd_quic_pkt_meta_ds_rev_iter_next( iter, pool ) ) {
5315 0 : fd_quic_pkt_meta_t * e = fd_quic_pkt_meta_ds_rev_iter_ele( iter, pool );
5316 0 : pkt_num_changes += !(e->key.pkt_num==prev_pkt_num);
5317 0 : prev_pkt_num = e->key.pkt_num;
5318 0 : }
5319 0 : skip_ceil = fd_ulong_if( pkt_num_changes==FD_QUIC_K_PACKET_THRESHOLD, prev_pkt_num, 0UL );
5320 0 : }
5321 :
5322 : /* track lowest packet acked */
5323 0 : ulong low_ack_pkt_number = largest_ack - data->first_ack_range;
5324 :
5325 : /* process ack range
5326 : applies to pkt_number in [largest_ack - first_ack_range, largest_ack] */
5327 0 : ulong acked = fd_quic_process_ack_range( conn,
5328 0 : context,
5329 0 : enc_level,
5330 0 : largest_ack,
5331 0 : data->first_ack_range,
5332 0 : 1 /* is_largest */,
5333 0 : now,
5334 0 : data->ack_delay );
5335 :
5336 0 : uchar const * p_str = p;
5337 0 : uchar const * p_end = p + p_sz;
5338 :
5339 0 : ulong ack_range_count = data->ack_range_count;
5340 :
5341 : /* cur_pkt_number holds the packet number of the lowest processed
5342 : and acknowledged packet
5343 : This should always be a valid packet number >= 0 */
5344 0 : ulong cur_pkt_number = largest_ack - data->first_ack_range;
5345 :
5346 : /* walk thru ack ranges */
5347 0 : for( ulong j = 0UL; j < ack_range_count; ++j ) {
5348 0 : if( FD_UNLIKELY( p_end <= p ) ) {
5349 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_FRAME_ENCODING_ERROR, __LINE__ );
5350 0 : return FD_QUIC_PARSE_FAIL;
5351 0 : }
5352 :
5353 0 : fd_quic_ack_range_frag_t ack_range[1];
5354 0 : ulong rc = fd_quic_decode_ack_range_frag( ack_range, p, (ulong)( p_end - p ) );
5355 0 : if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) {
5356 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_FRAME_ENCODING_ERROR, __LINE__ );
5357 0 : return FD_QUIC_PARSE_FAIL;
5358 0 : }
5359 :
5360 : /* ensure we have ulong local vars, regardless of ack_range definition */
5361 0 : ulong gap = (ulong)ack_range->gap;
5362 0 : ulong length = (ulong)ack_range->length;
5363 :
5364 : /* sanity check before unsigned arithmetic */
5365 0 : if( FD_UNLIKELY( ( gap > ( ~0x3UL ) ) |
5366 0 : ( length > ( ~0x3UL ) ) ) ) {
5367 : /* This is an unreasonably large value, so fail with protocol violation
5368 : It's also likely impossible due to the encoding method */
5369 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
5370 0 : return FD_QUIC_PARSE_FAIL;
5371 0 : }
5372 :
5373 : /* The number of packet numbers to skip (they are not being acked) is
5374 : ack_range->gap + 2
5375 : This is +1 to get from the lowest acked packet to the highest unacked packet
5376 : and +1 because the count of packets in the gap is (ack_range->gap+1) */
5377 0 : ulong skip = gap + 2UL;
5378 :
5379 : /* verify the skip and length values are valid */
5380 0 : if( FD_UNLIKELY( skip + length > cur_pkt_number ) ) {
5381 : /* this is a protocol violation, so inform the peer */
5382 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
5383 0 : return FD_QUIC_PARSE_FAIL;
5384 0 : }
5385 :
5386 : /* track lowest */
5387 0 : ulong lo_pkt_number = cur_pkt_number - skip - length;
5388 0 : low_ack_pkt_number = fd_ulong_min( low_ack_pkt_number, lo_pkt_number );
5389 :
5390 : /* process ack range */
5391 0 : acked += fd_quic_process_ack_range( conn,
5392 0 : context,
5393 0 : enc_level,
5394 0 : cur_pkt_number - skip,
5395 0 : length,
5396 0 : 0 /* is_largest */,
5397 0 : now,
5398 0 : 0 /* ack_delay not used here */ );
5399 :
5400 : /* Find the next lowest processed and acknowledged packet number
5401 : This should get us to the next lowest processed and acknowledged packet
5402 : number */
5403 0 : cur_pkt_number -= skip + length;
5404 :
5405 0 : p += rc;
5406 0 : }
5407 :
5408 0 : if( acked ) {
5409 0 : conn->pto_count = 0U;
5410 0 : conn->pto_expiry = 0L;
5411 0 : }
5412 :
5413 : /* Process packets declared lost for either:
5414 : 1. 'skipped': see skip_ceil computation above
5415 : 2. 'expired': checked inside pkt_meta_retry */
5416 0 : fd_quic_pkt_meta_retry( conn->quic, conn, skip_ceil, enc_level );
5417 :
5418 : /* ECN counts
5419 : we currently ignore them, but we must process them to get to the following bytes */
5420 0 : if( data->type & 1U ) {
5421 0 : if( FD_UNLIKELY( p_end <= p ) ) {
5422 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_FRAME_ENCODING_ERROR, __LINE__ );
5423 0 : return FD_QUIC_PARSE_FAIL;
5424 0 : }
5425 :
5426 0 : fd_quic_ecn_counts_frag_t ecn_counts[1];
5427 0 : ulong rc = fd_quic_decode_ecn_counts_frag( ecn_counts, p, (ulong)( p_end - p ) );
5428 0 : if( rc == FD_QUIC_PARSE_FAIL ) {
5429 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_FRAME_ENCODING_ERROR, __LINE__ );
5430 0 : return FD_QUIC_PARSE_FAIL;
5431 0 : }
5432 :
5433 0 : p += rc;
5434 0 : }
5435 :
5436 0 : return (ulong)( p - p_str );
5437 0 : }
5438 :
5439 : static ulong
5440 : fd_quic_handle_reset_stream_frame(
5441 : fd_quic_frame_ctx_t * context,
5442 : fd_quic_reset_stream_frame_t * data,
5443 : uchar const * p FD_PARAM_UNUSED,
5444 0 : ulong p_sz FD_PARAM_UNUSED ) {
5445 : /* TODO implement */
5446 0 : FD_DTRACE_PROBE_4( quic_handle_reset_stream_frame, context->conn->our_conn_id, data->stream_id, data->app_proto_err_code, data->final_size );
5447 0 : return 0UL;
5448 0 : }
5449 :
5450 : static ulong
5451 : fd_quic_handle_stop_sending_frame(
5452 : fd_quic_frame_ctx_t * context,
5453 : fd_quic_stop_sending_frame_t * data,
5454 : uchar const * p FD_PARAM_UNUSED,
5455 0 : ulong p_sz FD_PARAM_UNUSED ) {
5456 0 : FD_DTRACE_PROBE_3( quic_handle_stop_sending_frame, context->conn->our_conn_id, data->stream_id, data->app_proto_err_code );
5457 0 : return 0UL;
5458 0 : }
5459 :
5460 : static ulong
5461 : fd_quic_handle_new_token_frame(
5462 : fd_quic_frame_ctx_t * context,
5463 : fd_quic_new_token_frame_t * data,
5464 : uchar const * p FD_PARAM_UNUSED,
5465 0 : ulong p_sz FD_PARAM_UNUSED ) {
5466 0 : (void)data;
5467 0 : FD_DTRACE_PROBE_1( quic_handle_new_token_frame, context->conn->our_conn_id );
5468 : /* A server MUST treat receipt of a NEW_TOKEN frame as a connection
5469 : error of type PROTOCOL_VIOLATION. (RFC 9000 Section 19.7) */
5470 0 : if( FD_UNLIKELY( context->conn->server ) ) {
5471 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
5472 0 : return FD_QUIC_PARSE_FAIL;
5473 0 : }
5474 0 : return 0UL;
5475 0 : }
5476 :
5477 : void
5478 : fd_quic_tx_stream_free( fd_quic_t * quic,
5479 : fd_quic_conn_t * conn,
5480 : fd_quic_stream_t * stream,
5481 0 : int code ) {
5482 :
5483 : /* TODO rename FD_QUIC_NOTIFY_END to FD_QUIC_STREAM_NOTIFY_END et al */
5484 0 : if( FD_LIKELY( stream->state != FD_QUIC_STREAM_STATE_DEAD ) ) {
5485 0 : fd_quic_cb_stream_notify( quic, stream, stream->context, code );
5486 0 : stream->state = FD_QUIC_STREAM_STATE_DEAD;
5487 0 : }
5488 :
5489 0 : ulong stream_id = stream->stream_id;
5490 :
5491 : /* remove from stream map */
5492 0 : fd_quic_stream_map_t * stream_map = conn->stream_map;
5493 0 : fd_quic_stream_map_t * stream_entry = fd_quic_stream_map_query( stream_map, stream_id, NULL );
5494 0 : if( FD_LIKELY( stream_entry ) ) {
5495 0 : if( FD_LIKELY( stream_entry->stream ) ) {
5496 0 : stream_entry->stream->stream_flags = FD_QUIC_STREAM_FLAGS_DEAD;
5497 0 : }
5498 0 : fd_quic_stream_map_remove( stream_map, stream_entry );
5499 0 : }
5500 :
5501 : /* remove from list - idempotent */
5502 0 : FD_QUIC_STREAM_LIST_REMOVE( stream );
5503 0 : stream->stream_flags = FD_QUIC_STREAM_FLAGS_DEAD;
5504 0 : stream->stream_id = ~0UL;
5505 :
5506 : /* add to stream_pool */
5507 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
5508 0 : fd_quic_stream_pool_free( state->stream_pool, stream );
5509 :
5510 0 : }
5511 :
5512 :
5513 : static inline __attribute__((always_inline)) ulong
5514 : fd_quic_handle_stream_frame(
5515 : fd_quic_frame_ctx_t * context,
5516 : uchar const * p,
5517 : ulong p_sz,
5518 : ulong stream_id,
5519 : ulong offset,
5520 : ulong data_sz,
5521 0 : int fin ) {
5522 0 : fd_quic_t * quic = context->quic;
5523 0 : fd_quic_conn_t * conn = context->conn;
5524 0 : fd_quic_pkt_t * pkt = context->pkt;
5525 :
5526 0 : FD_DTRACE_PROBE_5( quic_handle_stream_frame, conn->our_conn_id, stream_id, offset, data_sz, fin );
5527 :
5528 : /* stream_id type check */
5529 0 : ulong stream_type = stream_id & 3UL;
5530 0 : if( FD_UNLIKELY( stream_type != ( conn->server ? FD_QUIC_STREAM_TYPE_UNI_CLIENT : FD_QUIC_STREAM_TYPE_UNI_SERVER ) ) ) {
5531 0 : FD_DEBUG( FD_LOG_DEBUG(( "Received forbidden stream type" )); )
5532 : /* Technically should switch between STREAM_LIMIT_ERROR and STREAM_STATE_ERROR here */
5533 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_STREAM_LIMIT_ERROR, __LINE__ );
5534 0 : return FD_QUIC_PARSE_FAIL;
5535 0 : }
5536 :
5537 : /* length check */
5538 0 : if( FD_UNLIKELY( data_sz > p_sz ) ) {
5539 0 : FD_DEBUG( FD_LOG_DEBUG(( "Stream header indicates %lu bytes length, but only have %lu", data_sz, p_sz )); )
5540 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_FRAME_ENCODING_ERROR, __LINE__ );
5541 0 : return FD_QUIC_PARSE_FAIL;
5542 0 : }
5543 :
5544 0 : conn->unacked_sz += data_sz;
5545 :
5546 : /* stream_id outside allowed range - protocol error */
5547 0 : if( FD_UNLIKELY( stream_id >= conn->srx->rx_sup_stream_id ) ) {
5548 0 : FD_DEBUG( FD_LOG_DEBUG(( "Stream ID violation detected" )); )
5549 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_STREAM_LIMIT_ERROR, __LINE__ );
5550 0 : return FD_QUIC_PARSE_FAIL;
5551 0 : }
5552 :
5553 : /* A receiver MUST close the connection with an error of type FLOW_CONTROL_ERROR if the sender
5554 : violates the advertised connection or stream data limits */
5555 0 : if( FD_UNLIKELY( quic->config.initial_rx_max_stream_data < offset + data_sz ) ) {
5556 0 : FD_DEBUG( FD_LOG_DEBUG(( "Stream data limit exceeded" )); )
5557 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_FLOW_CONTROL_ERROR, __LINE__ );
5558 0 : return FD_QUIC_PARSE_FAIL;
5559 0 : }
5560 :
5561 0 : int rx_res = fd_quic_cb_stream_rx( quic, conn, stream_id, offset, p, data_sz, fin );
5562 0 : pkt->ack_flag |= rx_res==FD_QUIC_SUCCESS ? 0 : ACK_FLAG_CANCEL;
5563 :
5564 : /* packet bytes consumed */
5565 0 : return data_sz;
5566 0 : }
5567 :
5568 : static ulong
5569 : fd_quic_handle_stream_8_frame(
5570 : fd_quic_frame_ctx_t * context,
5571 : fd_quic_stream_8_frame_t * data,
5572 : uchar const * p,
5573 0 : ulong p_sz ) {
5574 0 : return fd_quic_handle_stream_frame( context, p, p_sz, data->stream_id, 0UL, p_sz, data->type&1 );
5575 0 : }
5576 :
5577 : static ulong
5578 : fd_quic_handle_stream_a_frame(
5579 : fd_quic_frame_ctx_t * context,
5580 : fd_quic_stream_a_frame_t * data,
5581 : uchar const * p,
5582 0 : ulong p_sz ) {
5583 0 : return fd_quic_handle_stream_frame( context, p, p_sz, data->stream_id, 0UL, data->length, data->type&1 );
5584 0 : }
5585 :
5586 : static ulong
5587 : fd_quic_handle_stream_c_frame(
5588 : fd_quic_frame_ctx_t * context,
5589 : fd_quic_stream_c_frame_t * data,
5590 : uchar const * p,
5591 0 : ulong p_sz ) {
5592 0 : return fd_quic_handle_stream_frame( context, p, p_sz, data->stream_id, data->offset, p_sz, data->type&1 );
5593 0 : }
5594 :
5595 : static ulong
5596 : fd_quic_handle_stream_e_frame(
5597 : fd_quic_frame_ctx_t * context,
5598 : fd_quic_stream_e_frame_t * data,
5599 : uchar const * p,
5600 0 : ulong p_sz ) {
5601 0 : return fd_quic_handle_stream_frame( context, p, p_sz, data->stream_id, data->offset, data->length, data->type&1 );
5602 0 : }
5603 :
5604 : static ulong
5605 : fd_quic_handle_max_data_frame(
5606 : fd_quic_frame_ctx_t * context,
5607 : fd_quic_max_data_frame_t * data,
5608 : uchar const * p FD_PARAM_UNUSED,
5609 0 : ulong p_sz FD_PARAM_UNUSED ) {
5610 0 : fd_quic_conn_t * conn = context->conn;
5611 :
5612 0 : ulong max_data_old = conn->tx_max_data;
5613 0 : ulong max_data_new = data->max_data;
5614 0 : FD_DTRACE_PROBE_3( quic_handle_max_data_frame, conn->our_conn_id, max_data_new, max_data_old );
5615 :
5616 : /* max data is only allowed to increase the limit. Transgressing frames
5617 : are silently ignored */
5618 0 : conn->tx_max_data = fd_ulong_max( max_data_old, max_data_new );
5619 0 : return 0; /* no additional bytes consumed from buffer */
5620 0 : }
5621 :
5622 : static ulong
5623 : fd_quic_handle_max_stream_data_frame(
5624 : fd_quic_frame_ctx_t * context,
5625 : fd_quic_max_stream_data_frame_t * data,
5626 : uchar const * p FD_PARAM_UNUSED,
5627 0 : ulong p_sz FD_PARAM_UNUSED ) {
5628 : /* FIXME unsupported for now */
5629 0 : FD_DTRACE_PROBE_3( quic_handle_max_stream_data_frame, context->conn->our_conn_id, data->stream_id, data->max_stream_data );
5630 0 : return 0;
5631 0 : }
5632 :
5633 : static ulong
5634 : fd_quic_handle_max_streams_frame(
5635 : fd_quic_frame_ctx_t * context,
5636 : fd_quic_max_streams_frame_t * data,
5637 : uchar const * p FD_PARAM_UNUSED,
5638 0 : ulong p_sz FD_PARAM_UNUSED ) {
5639 0 : fd_quic_conn_t * conn = context->conn;
5640 0 : FD_DTRACE_PROBE_3( quic_handle_max_streams_frame, conn->our_conn_id, data->type, data->max_streams );
5641 :
5642 0 : if( data->type == 0x13 ) {
5643 : /* Only handle unidirectional streams */
5644 0 : ulong type = (ulong)conn->server | 2UL;
5645 : /* Receipt of a frame that permits opening of a stream larger than this limit (2^60)
5646 : MUST be treated as a connection error of type FRAME_ENCODING_ERROR. */
5647 0 : if( FD_UNLIKELY( data->max_streams > FD_QUIC_STREAM_COUNT_MAX ) ) {
5648 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_FRAME_ENCODING_ERROR, __LINE__ );
5649 0 : return FD_QUIC_PARSE_FAIL;
5650 0 : }
5651 0 : ulong peer_sup_stream_id = data->max_streams * 4UL + type;
5652 0 : conn->tx_sup_stream_id = fd_ulong_max( peer_sup_stream_id, conn->tx_sup_stream_id );
5653 0 : }
5654 :
5655 0 : return 0;
5656 0 : }
5657 :
5658 : static ulong
5659 : fd_quic_handle_data_blocked_frame(
5660 : fd_quic_frame_ctx_t * context,
5661 : fd_quic_data_blocked_frame_t * data,
5662 : uchar const * p FD_PARAM_UNUSED,
5663 0 : ulong p_sz FD_PARAM_UNUSED ) {
5664 0 : FD_DTRACE_PROBE_2( quic_handle_data_blocked, context->conn->our_conn_id, data->max_data );
5665 :
5666 : /* Since we do not do runtime allocations, we will not attempt
5667 : to find more memory in the case of DATA_BLOCKED. */
5668 0 : return 0;
5669 0 : }
5670 :
5671 : static ulong
5672 : fd_quic_handle_stream_data_blocked_frame(
5673 : fd_quic_frame_ctx_t * context,
5674 : fd_quic_stream_data_blocked_frame_t * data,
5675 : uchar const * p FD_PARAM_UNUSED,
5676 0 : ulong p_sz FD_PARAM_UNUSED ) {
5677 0 : FD_DTRACE_PROBE_3( quic_handle_stream_data_blocked, context->conn->our_conn_id, data->stream_id, data->max_stream_data );
5678 :
5679 : /* Since we do not do runtime allocations, we will not attempt
5680 : to find more memory in the case of STREAM_DATA_BLOCKED.*/
5681 0 : (void)data;
5682 0 : return 0;
5683 0 : }
5684 :
5685 : static ulong
5686 : fd_quic_handle_streams_blocked_frame(
5687 : fd_quic_frame_ctx_t * context,
5688 : fd_quic_streams_blocked_frame_t * data,
5689 : uchar const * p FD_PARAM_UNUSED,
5690 0 : ulong p_sz FD_PARAM_UNUSED ) {
5691 0 : FD_DTRACE_PROBE_2( quic_handle_streams_blocked_frame, context->conn->our_conn_id, data->max_streams );
5692 :
5693 : /* STREAMS_BLOCKED should be sent by client when it wants
5694 : to use a new stream, but is unable to due to the max_streams
5695 : value
5696 : We can support this in the future, but as of 2024-Dec, the
5697 : Agave TPU client does not currently use it */
5698 0 : return 0;
5699 0 : }
5700 :
5701 : static ulong
5702 : fd_quic_handle_new_conn_id_frame(
5703 : fd_quic_frame_ctx_t * context,
5704 : fd_quic_new_conn_id_frame_t * data,
5705 : uchar const * p FD_PARAM_UNUSED,
5706 0 : ulong p_sz FD_PARAM_UNUSED ) {
5707 : /* FIXME This is a mandatory feature but we don't support it yet */
5708 0 : FD_DTRACE_PROBE_1( quic_handle_new_conn_id_frame, context->conn->our_conn_id );
5709 0 : (void)data;
5710 0 : return 0;
5711 0 : }
5712 :
5713 : static ulong
5714 : fd_quic_handle_retire_conn_id_frame(
5715 : fd_quic_frame_ctx_t * context,
5716 : fd_quic_retire_conn_id_frame_t * data,
5717 : uchar const * p FD_PARAM_UNUSED,
5718 0 : ulong p_sz FD_PARAM_UNUSED ) {
5719 : /* FIXME This is a mandatory feature but we don't support it yet */
5720 0 : FD_DTRACE_PROBE_1( quic_handle_retire_conn_id_frame, context->conn->our_conn_id );
5721 0 : (void)data;
5722 0 : FD_DEBUG( FD_LOG_DEBUG(( "retire_conn_id requested" )); )
5723 0 : return 0;
5724 0 : }
5725 :
5726 : static ulong
5727 : fd_quic_handle_path_challenge_frame(
5728 : fd_quic_frame_ctx_t * context,
5729 : fd_quic_path_challenge_frame_t * data,
5730 : uchar const * p FD_PARAM_UNUSED,
5731 0 : ulong p_sz FD_PARAM_UNUSED ) {
5732 : /* FIXME The recipient of this frame MUST generate a PATH_RESPONSE frame (Section 19.18) containing the same Data value. */
5733 0 : FD_DTRACE_PROBE_1( quic_handle_path_challenge_frame, context->conn->our_conn_id );
5734 0 : (void)data;
5735 0 : return 0UL;
5736 0 : }
5737 :
5738 : static ulong
5739 : fd_quic_handle_path_response_frame(
5740 : fd_quic_frame_ctx_t * context,
5741 : fd_quic_path_response_frame_t * data,
5742 : uchar const * p FD_PARAM_UNUSED,
5743 0 : ulong p_sz FD_PARAM_UNUSED ) {
5744 : /* We don't generate PATH_CHALLENGE frames, so this frame should never arrive */
5745 0 : FD_DTRACE_PROBE_1( quic_handle_path_response_frame, context->conn->our_conn_id );
5746 0 : (void)data;
5747 0 : return 0UL;
5748 0 : }
5749 :
5750 : static void
5751 0 : fd_quic_handle_conn_close_frame( fd_quic_conn_t * conn ) {
5752 : /* frame type 0x1c means no error, or only error at quic level
5753 : frame type 0x1d means error at application layer
5754 : TODO provide APP with this info */
5755 0 : FD_DEBUG( FD_LOG_DEBUG(( "peer requested close" )) );
5756 :
5757 0 : switch( conn->state ) {
5758 0 : case FD_QUIC_CONN_STATE_PEER_CLOSE:
5759 0 : case FD_QUIC_CONN_STATE_ABORT:
5760 0 : case FD_QUIC_CONN_STATE_CLOSE_PENDING:
5761 0 : return;
5762 :
5763 0 : default:
5764 0 : fd_quic_set_conn_state( conn, FD_QUIC_CONN_STATE_PEER_CLOSE );
5765 0 : }
5766 :
5767 0 : conn->upd_pkt_number = FD_QUIC_PKT_NUM_PENDING;
5768 0 : fd_quic_svc_prep_schedule_now( conn );
5769 0 : }
5770 :
5771 : static ulong
5772 : fd_quic_handle_conn_close_0_frame(
5773 : fd_quic_frame_ctx_t * context,
5774 : fd_quic_conn_close_0_frame_t * data,
5775 : uchar const * p,
5776 0 : ulong p_sz ) {
5777 0 : (void)p;
5778 :
5779 0 : ulong reason_phrase_length = data->reason_phrase_length;
5780 0 : if( FD_UNLIKELY( reason_phrase_length > p_sz ) ) {
5781 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_FRAME_ENCODING_ERROR, __LINE__ );
5782 0 : return FD_QUIC_PARSE_FAIL;
5783 0 : }
5784 :
5785 : /* the information here can be invaluable for debugging */
5786 0 : FD_DEBUG(
5787 0 : char reason_buf[256] = {0};
5788 0 : ulong reason_len = fd_ulong_min( sizeof(reason_buf)-1, reason_phrase_length );
5789 0 : memcpy( reason_buf, p, reason_len );
5790 :
5791 0 : FD_LOG_WARNING(( "fd_quic_handle_conn_close_frame - "
5792 0 : "error_code: %lu "
5793 0 : "frame_type: %lx "
5794 0 : "reason: %s "
5795 0 : "peer " FD_IP4_ADDR_FMT ":%u "
5796 0 : "conn_id %lu",
5797 0 : data->error_code,
5798 0 : data->frame_type,
5799 0 : reason_buf,
5800 0 : FD_IP4_ADDR_FMT_ARGS(context->conn->peer->ip_addr),
5801 0 : context->conn->peer->udp_port,
5802 0 : context->conn->our_conn_id ));
5803 0 : );
5804 :
5805 0 : fd_quic_handle_conn_close_frame( context->conn );
5806 :
5807 0 : return reason_phrase_length;
5808 0 : }
5809 :
5810 : static ulong
5811 : fd_quic_handle_conn_close_1_frame(
5812 : fd_quic_frame_ctx_t * context,
5813 : fd_quic_conn_close_1_frame_t * data,
5814 : uchar const * p,
5815 0 : ulong p_sz ) {
5816 0 : (void)p;
5817 :
5818 0 : ulong reason_phrase_length = data->reason_phrase_length;
5819 0 : if( FD_UNLIKELY( reason_phrase_length > p_sz ) ) {
5820 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_FRAME_ENCODING_ERROR, __LINE__ );
5821 0 : return FD_QUIC_PARSE_FAIL;
5822 0 : }
5823 :
5824 : /* the information here can be invaluable for debugging */
5825 0 : FD_DEBUG(
5826 0 : char reason_buf[256] = {0};
5827 0 : ulong reason_len = fd_ulong_min( sizeof(reason_buf)-1, reason_phrase_length );
5828 0 : memcpy( reason_buf, p, reason_len );
5829 :
5830 0 : FD_LOG_WARNING(( "fd_quic_handle_conn_close_frame - "
5831 0 : "error_code: %lu "
5832 0 : "reason: %s "
5833 0 : "peer " FD_IP4_ADDR_FMT ":%u "
5834 0 : "conn_id %lu",
5835 0 : data->error_code,
5836 0 : reason_buf,
5837 0 : FD_IP4_ADDR_FMT_ARGS(context->conn->peer->ip_addr),
5838 0 : context->conn->peer->udp_port,
5839 0 : context->conn->our_conn_id ));
5840 0 : );
5841 :
5842 0 : fd_quic_handle_conn_close_frame( context->conn );
5843 :
5844 0 : return reason_phrase_length;
5845 0 : }
5846 :
5847 : static ulong
5848 : fd_quic_handle_handshake_done_frame(
5849 : fd_quic_frame_ctx_t * context,
5850 : fd_quic_handshake_done_frame_t * data,
5851 : uchar const * p FD_PARAM_UNUSED,
5852 0 : ulong p_sz FD_PARAM_UNUSED ) {
5853 0 : fd_quic_conn_t * conn = context->conn;
5854 0 : (void)data;
5855 :
5856 : /* servers must treat receipt of HANDSHAKE_DONE as a protocol violation */
5857 0 : if( FD_UNLIKELY( conn->server ) ) {
5858 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
5859 0 : return FD_QUIC_PARSE_FAIL;
5860 0 : }
5861 :
5862 0 : if( conn->state == FD_QUIC_CONN_STATE_HANDSHAKE ) {
5863 : /* still handshaking... assume packet was reordered */
5864 0 : context->pkt->ack_flag |= ACK_FLAG_CANCEL;
5865 0 : return 0UL;
5866 0 : } else if( conn->state != FD_QUIC_CONN_STATE_HANDSHAKE_COMPLETE ) {
5867 : /* duplicate frame or conn closing? */
5868 0 : return 0UL;
5869 0 : }
5870 :
5871 : /* Instantly acknowledge the first HANDSHAKE_DONE frame */
5872 0 : fd_quic_svc_prep_schedule_now( conn );
5873 :
5874 : /* RFC 9001 4.9.2. Discarding Handshake Keys
5875 : > An endpoint MUST discard its Handshake keys when the
5876 : > TLS handshake is confirmed
5877 : RFC 9001 4.1.2. Handshake Confirmed
5878 : > At the client, the handshake is considered confirmed when a
5879 : > HANDSHAKE_DONE frame is received. */
5880 0 : fd_quic_abandon_enc_level( conn, fd_quic_enc_level_handshake_id );
5881 :
5882 0 : if( FD_UNLIKELY( !conn->tls_hs ) ) {
5883 : /* sanity check */
5884 0 : return 0;
5885 0 : }
5886 :
5887 : /* eliminate any remaining hs_data at application level */
5888 0 : fd_quic_tls_clear_hs_data( conn->tls_hs, fd_quic_enc_level_appdata_id );
5889 :
5890 : /* we shouldn't be receiving this unless handshake is complete */
5891 0 : fd_quic_set_conn_state( conn, FD_QUIC_CONN_STATE_ACTIVE );
5892 :
5893 : /* user callback */
5894 0 : fd_quic_cb_conn_hs_complete( conn->quic, conn );
5895 :
5896 : /* Deallocate tls_hs once completed */
5897 0 : if( FD_LIKELY( conn->tls_hs ) ) {
5898 0 : fd_quic_state_t * state = fd_quic_get_state( conn->quic );
5899 0 : fd_quic_tls_hs_delete( conn->tls_hs );
5900 0 : fd_quic_tls_hs_cache_ele_remove( &state->hs_cache, conn->tls_hs, state->hs_pool );
5901 0 : fd_quic_tls_hs_pool_ele_release( state->hs_pool, conn->tls_hs );
5902 0 : conn->tls_hs = NULL;
5903 0 : }
5904 :
5905 0 : return 0;
5906 0 : }
5907 :
5908 : static ulong
5909 : fd_quic_handle_datagram( fd_quic_frame_ctx_t * context,
5910 : uchar const * data,
5911 : ulong data_sz,
5912 0 : ulong header_sz ) {
5913 0 : fd_quic_t * quic = context->quic;
5914 0 : ulong max_frame_sz = quic->config.max_datagram_frame_size;
5915 0 : if( FD_UNLIKELY( !max_frame_sz || !quic->cb.datagram_rx ) ) {
5916 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
5917 0 : return FD_QUIC_PARSE_FAIL;
5918 0 : }
5919 0 : if( FD_UNLIKELY( header_sz+data_sz > max_frame_sz ) ) {
5920 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION, __LINE__ );
5921 0 : return FD_QUIC_PARSE_FAIL;
5922 0 : }
5923 0 : quic->cb.datagram_rx( context->conn, data, data_sz, quic->cb.quic_ctx );
5924 0 : return data_sz;
5925 0 : }
5926 :
5927 : static ulong
5928 : fd_quic_handle_datagram_0_frame(
5929 : fd_quic_frame_ctx_t * context,
5930 : fd_quic_datagram_0_frame_t * frame FD_PARAM_UNUSED,
5931 : uchar const * p,
5932 0 : ulong p_sz ) {
5933 0 : return fd_quic_handle_datagram( context, p, p_sz, context->frame_sz );
5934 0 : }
5935 :
5936 : static ulong
5937 : fd_quic_handle_datagram_1_frame(
5938 : fd_quic_frame_ctx_t * context,
5939 : fd_quic_datagram_1_frame_t * frame,
5940 : uchar const * p,
5941 0 : ulong p_sz ) {
5942 0 : if( FD_UNLIKELY( frame->length>p_sz ) ) {
5943 0 : fd_quic_frame_error( context, FD_QUIC_CONN_REASON_FRAME_ENCODING_ERROR, __LINE__ );
5944 0 : return FD_QUIC_PARSE_FAIL;
5945 0 : }
5946 0 : return fd_quic_handle_datagram( context, p, frame->length, context->frame_sz );
5947 0 : }
5948 :
5949 : /* initiate the shutdown of a connection
5950 : may select a reason code */
5951 : void
5952 : fd_quic_conn_close( fd_quic_conn_t * conn,
5953 0 : uint app_reason ) {
5954 0 : if( FD_UNLIKELY( !conn ) ) return;
5955 :
5956 0 : switch( conn->state ) {
5957 0 : case FD_QUIC_CONN_STATE_INVALID:
5958 0 : case FD_QUIC_CONN_STATE_DEAD:
5959 0 : case FD_QUIC_CONN_STATE_ABORT:
5960 0 : return; /* close has no effect in these states */
5961 :
5962 0 : default:
5963 0 : {
5964 0 : fd_quic_set_conn_state( conn, FD_QUIC_CONN_STATE_CLOSE_PENDING );
5965 0 : conn->app_reason = app_reason;
5966 0 : }
5967 0 : }
5968 :
5969 : /* set connection to be serviced ASAP */
5970 0 : fd_quic_svc_prep_schedule_now( conn );
5971 0 : fd_quic_svc_schedule1( conn );
5972 0 : }
5973 :
5974 : void
5975 : fd_quic_conn_let_die( fd_quic_conn_t * conn,
5976 : long keep_alive_duration,
5977 0 : long now ) {
5978 0 : fd_quic_get_state( conn->quic )->now = now;
5979 0 : conn->let_die_time_ns = fd_long_sat_add( now, keep_alive_duration );
5980 : /* If we've missed the keep-alive time, schedule for immediate servicing */
5981 0 : if( FD_UNLIKELY( conn->last_activity+conn->idle_timeout_ns/2L < now ) ) {
5982 0 : fd_quic_svc_prep_schedule( conn, now );
5983 0 : fd_quic_svc_schedule1( conn );
5984 0 : }
5985 0 : }
|