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