LCOV - code coverage report
Current view: top level - waltz/quic - fd_quic.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 2480 3295 75.3 %
Date: 2026-09-17 04:28:31 Functions: 97 118 82.2 %

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

Generated by: LCOV version 1.14