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