LCOV - code coverage report
Current view: top level - waltz/quic - fd_quic.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 2533 3306 76.6 %
Date: 2025-08-05 05:04:49 Functions: 94 116 81.0 %

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

Generated by: LCOV version 1.14