LCOV - code coverage report
Current view: top level - waltz/quic - fd_quic.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 2455 3135 78.3 %
Date: 2025-11-08 04:42:58 Functions: 92 112 82.1 %

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

Generated by: LCOV version 1.14