LCOV - code coverage report
Current view: top level - plugins/quic - quic.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 946 1438 65.8 %
Date: 2023-07-05 22:20:52 Functions: 90 119 75.6 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2021 Cisco and/or its affiliates.
       3             :  * Licensed under the Apache License, Version 2.0 (the "License");
       4             :  * you may not use this file except in compliance with the License.
       5             :  * You may obtain a copy of the License at:
       6             :  *
       7             :  *     http://www.apache.org/licenses/LICENSE-2.0
       8             :  *
       9             :  * Unless required by applicable law or agreed to in writing, software
      10             :  * distributed under the License is distributed on an "AS IS" BASIS,
      11             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             :  * See the License for the specific language governing permissions and
      13             :  * limitations under the License.
      14             :  */
      15             : 
      16             : #include <sys/socket.h>
      17             : 
      18             : #include <vnet/session/application.h>
      19             : #include <vnet/session/transport.h>
      20             : #include <vnet/session/session.h>
      21             : #include <vlib/unix/plugin.h>
      22             : #include <vpp/app/version.h>
      23             : 
      24             : #include <vppinfra/lock.h>
      25             : 
      26             : #include <quic/quic.h>
      27             : #include <quic/certs.h>
      28             : #include <quic/error.h>
      29             : 
      30             : #include <quicly/constants.h>
      31             : #include <quicly/defaults.h>
      32             : #include <picotls.h>
      33             : 
      34             : #include <quic/quic_crypto.h>
      35             : 
      36             : extern quicly_crypto_engine_t quic_crypto_engine;
      37             : 
      38             : static char *quic_error_strings[] = {
      39             : #define quic_error(n,s) s,
      40             : #include <quic/quic_error.def>
      41             : #undef quic_error
      42             : };
      43             : 
      44             : #define DEFAULT_MAX_PACKETS_PER_KEY 16777216
      45             : 
      46             : quic_main_t quic_main;
      47             : static void quic_update_timer (quic_ctx_t * ctx);
      48             : static void quic_check_quic_session_connected (quic_ctx_t * ctx);
      49             : static int quic_reset_connection (u64 udp_session_handle,
      50             :                                   quic_rx_packet_ctx_t * pctx);
      51             : static void quic_proto_on_close (u32 ctx_index, u32 thread_index);
      52             : 
      53             : static quicly_stream_open_t on_stream_open;
      54             : static quicly_closed_by_remote_t on_closed_by_remote;
      55             : static quicly_now_t quicly_vpp_now_cb;
      56             : 
      57             : /* Crypto contexts */
      58             : 
      59             : static inline void
      60          41 : quic_crypto_context_make_key_from_ctx (clib_bihash_kv_24_8_t * kv,
      61             :                                        quic_ctx_t * ctx)
      62             : {
      63          41 :   application_t *app = application_get (ctx->parent_app_id);
      64          41 :   kv->key[0] = ((u64) ctx->ckpair_index) << 32 | (u64) ctx->crypto_engine;
      65          41 :   kv->key[1] = app->sm_properties.rx_fifo_size - 1;
      66          41 :   kv->key[2] = app->sm_properties.tx_fifo_size - 1;
      67          41 : }
      68             : 
      69             : static inline void
      70           9 : quic_crypto_context_make_key_from_crctx (clib_bihash_kv_24_8_t * kv,
      71             :                                          crypto_context_t * crctx)
      72             : {
      73           9 :   quic_crypto_context_data_t *data =
      74             :     (quic_crypto_context_data_t *) crctx->data;
      75           9 :   kv->key[0] = ((u64) crctx->ckpair_index) << 32 | (u64) crctx->crypto_engine;
      76           9 :   kv->key[1] = data->quicly_ctx.transport_params.max_stream_data.bidi_local;
      77           9 :   kv->key[2] = data->quicly_ctx.transport_params.max_stream_data.bidi_remote;
      78           9 : }
      79             : 
      80             : static void
      81          38 : quic_crypto_context_free_if_needed (crypto_context_t * crctx, u8 thread_index)
      82             : {
      83          38 :   quic_main_t *qm = &quic_main;
      84             :   clib_bihash_kv_24_8_t kv;
      85          38 :   if (crctx->n_subscribers)
      86          29 :     return;
      87           9 :   quic_crypto_context_make_key_from_crctx (&kv, crctx);
      88           9 :   clib_bihash_add_del_24_8 (&qm->wrk_ctx[thread_index].crypto_context_hash,
      89             :                             &kv, 0 /* is_add */ );
      90           9 :   clib_mem_free (crctx->data);
      91           9 :   pool_put (qm->wrk_ctx[thread_index].crypto_ctx_pool, crctx);
      92             : }
      93             : 
      94             : static int
      95           0 : quic_app_cert_key_pair_delete_callback (app_cert_key_pair_t * ckpair)
      96             : {
      97           0 :   quic_main_t *qm = &quic_main;
      98             :   crypto_context_t *crctx;
      99             :   clib_bihash_kv_24_8_t kv;
     100           0 :   vlib_thread_main_t *vtm = vlib_get_thread_main ();
     101           0 :   int num_threads = 1 /* main thread */  + vtm->n_threads;
     102             :   int i;
     103             : 
     104           0 :   for (i = 0; i < num_threads; i++)
     105             :     {
     106             :       /* *INDENT-OFF* */
     107           0 :       pool_foreach (crctx, qm->wrk_ctx[i].crypto_ctx_pool)  {
     108           0 :         if (crctx->ckpair_index == ckpair->cert_key_index)
     109             :           {
     110           0 :             quic_crypto_context_make_key_from_crctx (&kv, crctx);
     111           0 :             clib_bihash_add_del_24_8 (&qm->wrk_ctx[i].crypto_context_hash, &kv, 0 /* is_add */ );
     112             :           }
     113             :       }
     114             :       /* *INDENT-ON* */
     115             :     }
     116           0 :   return 0;
     117             : }
     118             : 
     119             : static crypto_context_t *
     120          12 : quic_crypto_context_alloc (u8 thread_index)
     121             : {
     122          12 :   quic_main_t *qm = &quic_main;
     123             :   crypto_context_t *crctx;
     124             :   u32 idx;
     125             : 
     126          12 :   pool_get (qm->wrk_ctx[thread_index].crypto_ctx_pool, crctx);
     127          12 :   clib_memset (crctx, 0, sizeof (*crctx));
     128          12 :   idx = (crctx - qm->wrk_ctx[thread_index].crypto_ctx_pool);
     129          12 :   crctx->ctx_index = ((u32) thread_index) << 24 | idx;
     130             : 
     131          12 :   return crctx;
     132             : }
     133             : 
     134             : static crypto_context_t *
     135       62801 : quic_crypto_context_get (u32 cr_index, u32 thread_index)
     136             : {
     137       62801 :   quic_main_t *qm = &quic_main;
     138       62801 :   ASSERT (cr_index >> 24 == thread_index);
     139       62801 :   return pool_elt_at_index (qm->wrk_ctx[thread_index].crypto_ctx_pool,
     140             :                             cr_index & 0x00ffffff);
     141             : }
     142             : 
     143             : static clib_error_t *
     144           0 : quic_list_crypto_context_command_fn (vlib_main_t * vm,
     145             :                                      unformat_input_t * input,
     146             :                                      vlib_cli_command_t * cmd)
     147             : {
     148           0 :   quic_main_t *qm = &quic_main;
     149             :   crypto_context_t *crctx;
     150           0 :   vlib_thread_main_t *vtm = vlib_get_thread_main ();
     151           0 :   int i, num_threads = 1 /* main thread */  + vtm->n_threads;
     152           0 :   for (i = 0; i < num_threads; i++)
     153             :     {
     154             :       /* *INDENT-OFF* */
     155           0 :       pool_foreach (crctx, qm->wrk_ctx[i].crypto_ctx_pool)  {
     156           0 :         vlib_cli_output (vm, "[%d][Q]%U", i, format_crypto_context, crctx);
     157             :       }
     158             :       /* *INDENT-ON* */
     159             :     }
     160           0 :   return 0;
     161             : }
     162             : 
     163             : static clib_error_t *
     164           0 : quic_set_max_packets_per_key_fn (vlib_main_t * vm,
     165             :                                  unformat_input_t * input,
     166             :                                  vlib_cli_command_t * cmd)
     167             : {
     168           0 :   quic_main_t *qm = &quic_main;
     169           0 :   unformat_input_t _line_input, *line_input = &_line_input;
     170             :   u64 tmp;
     171             : 
     172           0 :   if (!unformat_user (input, unformat_line_input, line_input))
     173           0 :     return 0;
     174             : 
     175           0 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
     176             :     {
     177           0 :       if (unformat (line_input, "%U", unformat_memory_size, &tmp))
     178             :         {
     179           0 :           qm->max_packets_per_key = tmp;
     180             :         }
     181             :       else
     182           0 :         return clib_error_return (0, "unknown input '%U'",
     183             :                                   format_unformat_error, line_input);
     184             :     }
     185             : 
     186           0 :   return 0;
     187             : }
     188             : 
     189             : static clib_error_t *
     190           0 : quic_set_cc_fn (vlib_main_t *vm, unformat_input_t *input,
     191             :                 vlib_cli_command_t *cmd)
     192             : {
     193           0 :   unformat_input_t _line_input, *line_input = &_line_input;
     194           0 :   quic_main_t *qm = &quic_main;
     195           0 :   clib_error_t *e = 0;
     196             : 
     197           0 :   if (!unformat_user (input, unformat_line_input, line_input))
     198           0 :     return 0;
     199             : 
     200           0 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
     201             :     {
     202           0 :       if (unformat (line_input, "reno"))
     203           0 :         qm->default_quic_cc = QUIC_CC_RENO;
     204           0 :       else if (unformat (line_input, "cubic"))
     205           0 :         qm->default_quic_cc = QUIC_CC_CUBIC;
     206             :       else
     207             :         {
     208           0 :           e = clib_error_return (0, "unknown input '%U'",
     209             :                                  format_unformat_error, line_input);
     210           0 :           goto done;
     211             :         }
     212             :     }
     213           0 : done:
     214           0 :   unformat_free (line_input);
     215           0 :   return e;
     216             : }
     217             : 
     218             : static void
     219          38 : quic_release_crypto_context (u32 crypto_context_index, u8 thread_index)
     220             : {
     221             :   crypto_context_t *crctx;
     222          38 :   crctx = quic_crypto_context_get (crypto_context_index, thread_index);
     223          38 :   crctx->n_subscribers--;
     224          38 :   quic_crypto_context_free_if_needed (crctx, thread_index);
     225          38 : }
     226             : 
     227             : static int
     228          12 : quic_init_crypto_context (crypto_context_t * crctx, quic_ctx_t * ctx)
     229             : {
     230          12 :   quic_main_t *qm = &quic_main;
     231             :   quicly_context_t *quicly_ctx;
     232             :   ptls_iovec_t key_vec;
     233             :   app_cert_key_pair_t *ckpair;
     234             :   application_t *app;
     235             :   quic_crypto_context_data_t *data;
     236             :   ptls_context_t *ptls_ctx;
     237             : 
     238             :   QUIC_DBG (2, "Init quic crctx %d thread %d", crctx->ctx_index,
     239             :             ctx->c_thread_index);
     240             : 
     241          12 :   data = clib_mem_alloc (sizeof (*data));
     242             :   /* picotls depends on data being zeroed */
     243          12 :   clib_memset (data, 0, sizeof (*data));
     244          12 :   crctx->data = (void *) data;
     245          12 :   quicly_ctx = &data->quicly_ctx;
     246          12 :   ptls_ctx = &data->ptls_ctx;
     247             : 
     248          12 :   ptls_ctx->random_bytes = ptls_openssl_random_bytes;
     249          12 :   ptls_ctx->get_time = &ptls_get_time;
     250          12 :   ptls_ctx->key_exchanges = ptls_openssl_key_exchanges;
     251          12 :   ptls_ctx->cipher_suites = qm->quic_ciphers[ctx->crypto_engine];
     252          12 :   ptls_ctx->certificates.list = NULL;
     253          12 :   ptls_ctx->certificates.count = 0;
     254          12 :   ptls_ctx->esni = NULL;
     255          12 :   ptls_ctx->on_client_hello = NULL;
     256          12 :   ptls_ctx->emit_certificate = NULL;
     257          12 :   ptls_ctx->sign_certificate = NULL;
     258          12 :   ptls_ctx->verify_certificate = NULL;
     259          12 :   ptls_ctx->ticket_lifetime = 86400;
     260          12 :   ptls_ctx->max_early_data_size = 8192;
     261          12 :   ptls_ctx->hkdf_label_prefix__obsolete = NULL;
     262          12 :   ptls_ctx->require_dhe_on_psk = 1;
     263          12 :   ptls_ctx->encrypt_ticket = &qm->session_cache.super;
     264          12 :   clib_memcpy (quicly_ctx, &quicly_spec_context, sizeof (quicly_context_t));
     265             : 
     266          12 :   quicly_ctx->max_packets_per_key = qm->max_packets_per_key;
     267          12 :   quicly_ctx->tls = ptls_ctx;
     268          12 :   quicly_ctx->stream_open = &on_stream_open;
     269          12 :   quicly_ctx->closed_by_remote = &on_closed_by_remote;
     270          12 :   quicly_ctx->now = &quicly_vpp_now_cb;
     271          12 :   quicly_amend_ptls_context (quicly_ctx->tls);
     272             : 
     273          12 :   if (qm->vnet_crypto_enabled &&
     274          12 :       qm->default_crypto_engine == CRYPTO_ENGINE_VPP)
     275          12 :     quicly_ctx->crypto_engine = &quic_crypto_engine;
     276             :   else
     277           0 :     quicly_ctx->crypto_engine = &quicly_default_crypto_engine;
     278             : 
     279          12 :   quicly_ctx->transport_params.max_data = QUIC_INT_MAX;
     280          12 :   quicly_ctx->transport_params.max_streams_uni = (uint64_t) 1 << 60;
     281          12 :   quicly_ctx->transport_params.max_streams_bidi = (uint64_t) 1 << 60;
     282          12 :   quicly_ctx->transport_params.max_idle_timeout = qm->connection_timeout;
     283             : 
     284          12 :   if (qm->default_quic_cc == QUIC_CC_CUBIC)
     285           0 :     quicly_ctx->init_cc = &quicly_cc_cubic_init;
     286          12 :   else if (qm->default_quic_cc == QUIC_CC_RENO)
     287          12 :     quicly_ctx->init_cc = &quicly_cc_reno_init;
     288             : 
     289          12 :   app = application_get (ctx->parent_app_id);
     290          12 :   quicly_ctx->transport_params.max_stream_data.bidi_local =
     291          12 :     app->sm_properties.rx_fifo_size - 1;
     292          12 :   quicly_ctx->transport_params.max_stream_data.bidi_remote =
     293          12 :     app->sm_properties.tx_fifo_size - 1;
     294          12 :   quicly_ctx->transport_params.max_stream_data.uni = QUIC_INT_MAX;
     295             : 
     296          12 :   quicly_ctx->transport_params.max_udp_payload_size = QUIC_MAX_PACKET_SIZE;
     297          12 :   if (!app->quic_iv_set)
     298             :     {
     299          10 :       ptls_openssl_random_bytes (app->quic_iv, QUIC_IV_LEN - 1);
     300          10 :       app->quic_iv[QUIC_IV_LEN - 1] = 0;
     301          10 :       app->quic_iv_set = 1;
     302             :     }
     303             : 
     304          12 :   clib_memcpy (data->cid_key, app->quic_iv, QUIC_IV_LEN);
     305          12 :   key_vec = ptls_iovec_init (data->cid_key, QUIC_IV_LEN);
     306          12 :   quicly_ctx->cid_encryptor =
     307          12 :     quicly_new_default_cid_encryptor (&ptls_openssl_bfecb,
     308             :                                       &ptls_openssl_aes128ecb,
     309             :                                       &ptls_openssl_sha256, key_vec);
     310             : 
     311          12 :   ckpair = app_cert_key_pair_get_if_valid (crctx->ckpair_index);
     312          12 :   if (!ckpair || !ckpair->key || !ckpair->cert)
     313             :     {
     314             :       QUIC_DBG (1, "Wrong ckpair id %d\n", crctx->ckpair_index);
     315           0 :       return -1;
     316             :     }
     317          12 :   if (load_bio_private_key (quicly_ctx->tls, (char *) ckpair->key))
     318             :     {
     319             :       QUIC_DBG (1, "failed to read private key from app configuration\n");
     320           0 :       return -1;
     321             :     }
     322          12 :   if (load_bio_certificate_chain (quicly_ctx->tls, (char *) ckpair->cert))
     323             :     {
     324             :       QUIC_DBG (1, "failed to load certificate\n");
     325           0 :       return -1;
     326             :     }
     327          12 :   return 0;
     328             : 
     329             : }
     330             : 
     331             : static int
     332          41 : quic_acquire_crypto_context (quic_ctx_t * ctx)
     333             : {
     334          41 :   quic_main_t *qm = &quic_main;
     335             :   crypto_context_t *crctx;
     336             :   clib_bihash_kv_24_8_t kv;
     337             : 
     338          41 :   if (ctx->crypto_engine == CRYPTO_ENGINE_NONE)
     339             :     {
     340             :       QUIC_DBG (2, "No crypto engine specified, using %d",
     341             :                 qm->default_crypto_engine);
     342          23 :       ctx->crypto_engine = qm->default_crypto_engine;
     343             :     }
     344          41 :   if (!clib_bitmap_get (qm->available_crypto_engines, ctx->crypto_engine))
     345             :     {
     346             :       QUIC_DBG (1, "Quic does not support crypto engine %d",
     347             :                 ctx->crypto_engine);
     348           0 :       return SESSION_E_NOCRYPTOENG;
     349             :     }
     350             : 
     351             :   /* Check for exisiting crypto ctx */
     352          41 :   quic_crypto_context_make_key_from_ctx (&kv, ctx);
     353          41 :   if (clib_bihash_search_24_8
     354          41 :       (&qm->wrk_ctx[ctx->c_thread_index].crypto_context_hash, &kv, &kv) == 0)
     355             :     {
     356          29 :       crctx = quic_crypto_context_get (kv.value, ctx->c_thread_index);
     357             :       QUIC_DBG (2, "Found exisiting crypto context %d", kv.value);
     358          29 :       ctx->crypto_context_index = kv.value;
     359          29 :       crctx->n_subscribers++;
     360          29 :       return 0;
     361             :     }
     362             : 
     363          12 :   crctx = quic_crypto_context_alloc (ctx->c_thread_index);
     364          12 :   ctx->crypto_context_index = crctx->ctx_index;
     365          12 :   kv.value = crctx->ctx_index;
     366          12 :   crctx->crypto_engine = ctx->crypto_engine;
     367          12 :   crctx->ckpair_index = ctx->ckpair_index;
     368          12 :   if (quic_init_crypto_context (crctx, ctx))
     369           0 :     goto error;
     370          12 :   if (vnet_app_add_cert_key_interest (ctx->ckpair_index, qm->app_index))
     371           0 :     goto error;
     372          12 :   crctx->n_subscribers++;
     373          12 :   clib_bihash_add_del_24_8 (&qm->
     374          12 :                             wrk_ctx[ctx->c_thread_index].crypto_context_hash,
     375             :                             &kv, 1 /* is_add */ );
     376          12 :   return 0;
     377             : 
     378           0 : error:
     379           0 :   quic_crypto_context_free_if_needed (crctx, ctx->c_thread_index);
     380           0 :   return SESSION_E_NOCRYPTOCKP;
     381             : }
     382             : 
     383             : /*  Helper functions */
     384             : 
     385             : static u32
     386          77 : quic_ctx_alloc (u32 thread_index)
     387             : {
     388          77 :   quic_main_t *qm = &quic_main;
     389             :   quic_ctx_t *ctx;
     390             : 
     391          77 :   pool_get_aligned_safe (qm->ctx_pool[thread_index], ctx,
     392             :                          CLIB_CACHE_LINE_BYTES);
     393             : 
     394          77 :   clib_memset (ctx, 0, sizeof (quic_ctx_t));
     395          77 :   ctx->c_thread_index = thread_index;
     396          77 :   ctx->timer_handle = QUIC_TIMER_HANDLE_INVALID;
     397             :   QUIC_DBG (3, "Allocated quic_ctx %u on thread %u",
     398             :             ctx - qm->ctx_pool[thread_index], thread_index);
     399          77 :   return ctx - qm->ctx_pool[thread_index];
     400             : }
     401             : 
     402             : static void
     403          74 : quic_ctx_free (quic_ctx_t * ctx)
     404             : {
     405             :   QUIC_DBG (2, "Free ctx %u %x", ctx->c_thread_index, ctx->c_c_index);
     406          74 :   u32 thread_index = ctx->c_thread_index;
     407          74 :   QUIC_ASSERT (ctx->timer_handle == QUIC_TIMER_HANDLE_INVALID);
     408             :   if (CLIB_DEBUG)
     409          74 :     clib_memset (ctx, 0xfb, sizeof (*ctx));
     410          74 :   pool_put (quic_main.ctx_pool[thread_index], ctx);
     411          74 : }
     412             : 
     413             : static quic_ctx_t *
     414      747556 : quic_ctx_get (u32 ctx_index, u32 thread_index)
     415             : {
     416      747556 :   return pool_elt_at_index (quic_main.ctx_pool[thread_index], ctx_index);
     417             : }
     418             : 
     419             : static quic_ctx_t *
     420          54 : quic_ctx_get_if_valid (u32 ctx_index, u32 thread_index)
     421             : {
     422          54 :   if (pool_is_free_index (quic_main.ctx_pool[thread_index], ctx_index))
     423           0 :     return 0;
     424          54 :   return pool_elt_at_index (quic_main.ctx_pool[thread_index], ctx_index);
     425             : }
     426             : 
     427             : quic_ctx_t *
     428          90 : quic_get_conn_ctx (quicly_conn_t * conn)
     429             : {
     430             :   u64 conn_data;
     431          90 :   conn_data = (u64) * quicly_get_data (conn);
     432          90 :   return quic_ctx_get (conn_data & UINT32_MAX, conn_data >> 32);
     433             : }
     434             : 
     435             : static void
     436          36 : quic_store_conn_ctx (quicly_conn_t * conn, quic_ctx_t * ctx)
     437             : {
     438          72 :   *quicly_get_data (conn) =
     439          36 :     (void *) (((u64) ctx->c_thread_index) << 32 | (u64) ctx->c_c_index);
     440          36 : }
     441             : 
     442             : static inline int
     443      205967 : quic_ctx_is_stream (quic_ctx_t * ctx)
     444             : {
     445      205967 :   return (ctx->flags & QUIC_F_IS_STREAM);
     446             : }
     447             : 
     448             : static inline int
     449           8 : quic_ctx_is_listener (quic_ctx_t * ctx)
     450             : {
     451           8 :   return (ctx->flags & QUIC_F_IS_LISTENER);
     452             : }
     453             : 
     454             : static inline int
     455           0 : quic_ctx_is_conn (quic_ctx_t * ctx)
     456             : {
     457           0 :   return !(quic_ctx_is_listener (ctx) || quic_ctx_is_stream (ctx));
     458             : }
     459             : 
     460             : static inline session_t *
     461       50672 : get_stream_session_and_ctx_from_stream (quicly_stream_t * stream,
     462             :                                         quic_ctx_t ** ctx)
     463             : {
     464             :   quic_stream_data_t *stream_data;
     465             : 
     466       50672 :   stream_data = (quic_stream_data_t *) stream->data;
     467       50672 :   *ctx = quic_ctx_get (stream_data->ctx_id, stream_data->thread_index);
     468       50672 :   return session_get ((*ctx)->c_s_index, stream_data->thread_index);
     469             : }
     470             : 
     471             : static inline void
     472       49675 : quic_make_connection_key (clib_bihash_kv_16_8_t * kv,
     473             :                           const quicly_cid_plaintext_t * id)
     474             : {
     475       49675 :   kv->key[0] = ((u64) id->master_id) << 32 | (u64) id->thread_id;
     476       49675 :   kv->key[1] = id->node_id;
     477       49675 : }
     478             : 
     479             : static int
     480       14140 : quic_sendable_packet_count (session_t * udp_session)
     481             : {
     482             :   u32 max_enqueue;
     483       14140 :   u32 packet_size = QUIC_MAX_PACKET_SIZE + SESSION_CONN_HDR_LEN;
     484       14140 :   max_enqueue = svm_fifo_max_enqueue (udp_session->tx_fifo);
     485       14140 :   return clib_min (max_enqueue / packet_size, QUIC_SEND_PACKET_VEC_SIZE);
     486             : }
     487             : 
     488             : static quicly_context_t *
     489       62734 : quic_get_quicly_ctx_from_ctx (quic_ctx_t * ctx)
     490             : {
     491             :   crypto_context_t *crctx =
     492       62734 :     quic_crypto_context_get (ctx->crypto_context_index, ctx->c_thread_index);
     493       62734 :   quic_crypto_context_data_t *data =
     494             :     (quic_crypto_context_data_t *) crctx->data;
     495       62734 :   return &data->quicly_ctx;
     496             : }
     497             : 
     498             : static quicly_context_t *
     499       49603 : quic_get_quicly_ctx_from_udp (u64 udp_session_handle)
     500             : {
     501       49603 :   session_t *udp_session = session_get_from_handle (udp_session_handle);
     502             :   quic_ctx_t *ctx =
     503       49603 :     quic_ctx_get (udp_session->opaque, udp_session->thread_index);
     504       49603 :   return quic_get_quicly_ctx_from_ctx (ctx);
     505             : }
     506             : 
     507             : static inline void
     508       13059 : quic_set_udp_tx_evt (session_t * udp_session)
     509             : {
     510       13059 :   int rv = 0;
     511       13059 :   if (svm_fifo_set_event (udp_session->tx_fifo))
     512        3763 :     rv = session_send_io_evt_to_thread (udp_session->tx_fifo,
     513             :                                         SESSION_IO_EVT_TX);
     514       13059 :   if (PREDICT_FALSE (rv))
     515           0 :     clib_warning ("Event enqueue errored %d", rv);
     516       13059 : }
     517             : 
     518             : static inline void
     519          72 : quic_stop_ctx_timer (quic_ctx_t * ctx)
     520             : {
     521             :   tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
     522          72 :   if (ctx->timer_handle == QUIC_TIMER_HANDLE_INVALID)
     523          72 :     return;
     524           0 :   tw = &quic_main.wrk_ctx[ctx->c_thread_index].timer_wheel;
     525           0 :   tw_timer_stop_1t_3w_1024sl_ov (tw, ctx->timer_handle);
     526           0 :   ctx->timer_handle = QUIC_TIMER_HANDLE_INVALID;
     527             :   QUIC_DBG (4, "Stopping timer for ctx %u", ctx->c_c_index);
     528             : }
     529             : 
     530             : /* QUIC protocol actions */
     531             : 
     532             : static void
     533      113161 : quic_ack_rx_data (session_t * stream_session)
     534             : {
     535             :   u32 max_deq;
     536             :   quic_ctx_t *sctx;
     537             :   svm_fifo_t *f;
     538             :   quicly_stream_t *stream;
     539             :   quic_stream_data_t *stream_data;
     540             : 
     541      113161 :   sctx = quic_ctx_get (stream_session->connection_index,
     542      113161 :                        stream_session->thread_index);
     543      113161 :   QUIC_ASSERT (quic_ctx_is_stream (sctx));
     544      113161 :   stream = sctx->stream;
     545      113161 :   stream_data = (quic_stream_data_t *) stream->data;
     546             : 
     547      113161 :   f = stream_session->rx_fifo;
     548      113161 :   max_deq = svm_fifo_max_dequeue (f);
     549             : 
     550      113161 :   QUIC_ASSERT (stream_data->app_rx_data_len >= max_deq);
     551      113161 :   quicly_stream_sync_recvbuf (stream, stream_data->app_rx_data_len - max_deq);
     552             :   QUIC_DBG (3, "Acking %u bytes", stream_data->app_rx_data_len - max_deq);
     553      113161 :   stream_data->app_rx_data_len = max_deq;
     554      113161 : }
     555             : 
     556             : static void
     557          36 : quic_disconnect_transport (quic_ctx_t * ctx)
     558             : {
     559             :   QUIC_DBG (2, "Disconnecting transport 0x%lx", ctx->udp_session_handle);
     560          36 :   vnet_disconnect_args_t a = {
     561          36 :     .handle = ctx->udp_session_handle,
     562          36 :     .app_index = quic_main.app_index,
     563             :   };
     564             : 
     565          36 :   if (vnet_disconnect_session (&a))
     566           0 :     clib_warning ("UDP session 0x%lx disconnect errored",
     567             :                   ctx->udp_session_handle);
     568          36 : }
     569             : 
     570             : static void
     571          36 : quic_connection_delete (quic_ctx_t * ctx)
     572             : {
     573             :   clib_bihash_kv_16_8_t kv;
     574             :   quicly_conn_t *conn;
     575             : 
     576          36 :   if (ctx->conn == NULL)
     577             :     {
     578             :       QUIC_DBG (2, "Skipping redundant delete of connection %u",
     579             :                 ctx->c_c_index);
     580           0 :       return;
     581             :     }
     582             :   QUIC_DBG (2, "Deleting connection %u", ctx->c_c_index);
     583             : 
     584          36 :   QUIC_ASSERT (!quic_ctx_is_stream (ctx));
     585          36 :   quic_stop_ctx_timer (ctx);
     586             : 
     587             :   /*  Delete the connection from the connection map */
     588          36 :   conn = ctx->conn;
     589          36 :   ctx->conn = NULL;
     590          36 :   quic_make_connection_key (&kv, quicly_get_master_id (conn));
     591             :   QUIC_DBG (2, "Deleting conn with id %lu %lu from map", kv.key[0],
     592             :             kv.key[1]);
     593          36 :   clib_bihash_add_del_16_8 (&quic_main.connection_hash, &kv, 0 /* is_add */ );
     594             : 
     595          36 :   quic_disconnect_transport (ctx);
     596             : 
     597          36 :   if (conn)
     598          36 :     quicly_free (conn);
     599          36 :   session_transport_delete_notify (&ctx->connection);
     600             : }
     601             : 
     602             : void
     603       99278 : quic_increment_counter (u8 evt, u8 val)
     604             : {
     605       99278 :   vlib_main_t *vm = vlib_get_main ();
     606       99278 :   vlib_node_increment_counter (vm, quic_input_node.index, evt, val);
     607       99278 : }
     608             : 
     609             : /**
     610             :  * Called when quicly return an error
     611             :  * This function interacts tightly with quic_proto_on_close
     612             :  */
     613             : static void
     614          36 : quic_connection_closed (quic_ctx_t * ctx)
     615             : {
     616             :   QUIC_DBG (2, "QUIC connection %u/%u closed", ctx->c_thread_index,
     617             :             ctx->c_c_index);
     618             : 
     619             :   /* TODO if connection is not established, just delete the session? */
     620             :   /* Actually should send connect or accept error */
     621             : 
     622          36 :   switch (ctx->conn_state)
     623             :     {
     624           0 :     case QUIC_CONN_STATE_READY:
     625             :       /* Error on an opened connection (timeout...)
     626             :          This puts the session in closing state, we should receive a notification
     627             :          when the app has closed its session */
     628           0 :       session_transport_reset_notify (&ctx->connection);
     629             :       /* This ensures we delete the connection when the app confirms the close */
     630           0 :       ctx->conn_state = QUIC_CONN_STATE_PASSIVE_CLOSING_QUIC_CLOSED;
     631           0 :       break;
     632           0 :     case QUIC_CONN_STATE_PASSIVE_CLOSING:
     633           0 :       ctx->conn_state = QUIC_CONN_STATE_PASSIVE_CLOSING_QUIC_CLOSED;
     634             :       /* quic_proto_on_close will eventually be called when the app confirms the close
     635             :          , we delete the connection at that point */
     636           0 :       break;
     637          18 :     case QUIC_CONN_STATE_PASSIVE_CLOSING_APP_CLOSED:
     638             :       /* App already confirmed close, we can delete the connection */
     639          18 :       quic_connection_delete (ctx);
     640          18 :       break;
     641          18 :     case QUIC_CONN_STATE_OPENED:
     642             :     case QUIC_CONN_STATE_HANDSHAKE:
     643             :     case QUIC_CONN_STATE_ACTIVE_CLOSING:
     644          18 :       quic_connection_delete (ctx);
     645          18 :       break;
     646           0 :     default:
     647             :       QUIC_DBG (0, "BUG %d", ctx->conn_state);
     648           0 :       break;
     649             :     }
     650          36 : }
     651             : 
     652             : static int
     653       49603 : quic_send_datagram (session_t *udp_session, struct iovec *packet,
     654             :                     quicly_address_t *dest, quicly_address_t *src)
     655             : {
     656             :   u32 max_enqueue, len;
     657             :   session_dgram_hdr_t hdr;
     658             :   svm_fifo_t *f;
     659             :   transport_connection_t *tc;
     660             :   int ret;
     661             : 
     662       49603 :   len = packet->iov_len;
     663       49603 :   f = udp_session->tx_fifo;
     664       49603 :   tc = session_get_transport (udp_session);
     665       49603 :   max_enqueue = svm_fifo_max_enqueue (f);
     666       49603 :   if (max_enqueue < SESSION_CONN_HDR_LEN + len)
     667             :     {
     668           0 :       QUIC_ERR ("Too much data to send, max_enqueue %u, len %u",
     669             :                 max_enqueue, len + SESSION_CONN_HDR_LEN);
     670           0 :       return QUIC_ERROR_FULL_FIFO;
     671             :     }
     672             : 
     673             :   /*  Build packet header for fifo */
     674       49603 :   hdr.data_length = len;
     675       49603 :   hdr.data_offset = 0;
     676       49603 :   hdr.is_ip4 = tc->is_ip4;
     677       49603 :   clib_memcpy (&hdr.lcl_ip, &tc->lcl_ip, sizeof (ip46_address_t));
     678       49603 :   hdr.lcl_port = tc->lcl_port;
     679       49603 :   hdr.gso_size = 0;
     680             : 
     681             :   /*  Read dest address from quicly-provided sockaddr */
     682       49603 :   if (hdr.is_ip4)
     683             :     {
     684       49603 :       QUIC_ASSERT (dest->sa.sa_family == AF_INET);
     685       49603 :       struct sockaddr_in *sa4 = (struct sockaddr_in *) &dest->sa;
     686       49603 :       hdr.rmt_port = sa4->sin_port;
     687       49603 :       hdr.rmt_ip.ip4.as_u32 = sa4->sin_addr.s_addr;
     688             :     }
     689             :   else
     690             :     {
     691           0 :       QUIC_ASSERT (dest->sa.sa_family == AF_INET6);
     692           0 :       struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &dest->sa;
     693           0 :       hdr.rmt_port = sa6->sin6_port;
     694           0 :       clib_memcpy_fast (&hdr.rmt_ip.ip6, &sa6->sin6_addr, 16);
     695             :     }
     696             : 
     697       49603 :   svm_fifo_seg_t segs[2] = { { (u8 *) &hdr, sizeof (hdr) },
     698       49603 :                              { packet->iov_base, len } };
     699             : 
     700       49603 :   ret = svm_fifo_enqueue_segments (f, segs, 2, 0 /* allow partial */);
     701       49603 :   if (PREDICT_FALSE (ret < 0))
     702             :     {
     703           0 :       QUIC_ERR ("Not enough space to enqueue dgram");
     704           0 :       return QUIC_ERROR_FULL_FIFO;
     705             :     }
     706             : 
     707       49603 :   quic_increment_counter (QUIC_ERROR_TX_PACKETS, 1);
     708             : 
     709       49603 :   return 0;
     710             : }
     711             : 
     712             : static int
     713       13095 : quic_send_packets (quic_ctx_t * ctx)
     714       13095 : {
     715             :   struct iovec packets[QUIC_SEND_PACKET_VEC_SIZE];
     716             :   uint8_t
     717       13095 :     buf[QUIC_SEND_PACKET_VEC_SIZE * quic_get_quicly_ctx_from_ctx (ctx)
     718       13095 :                                       ->transport_params.max_udp_payload_size];
     719             :   session_t *udp_session;
     720             :   quicly_conn_t *conn;
     721             :   size_t num_packets, i, max_packets;
     722             :   quicly_address_t dest, src;
     723       13095 :   u32 n_sent = 0;
     724       13095 :   int err = 0;
     725             : 
     726             :   /* We have sctx, get qctx */
     727       13095 :   if (quic_ctx_is_stream (ctx))
     728        4811 :     ctx = quic_ctx_get (ctx->quic_connection_ctx_id, ctx->c_thread_index);
     729             : 
     730       13095 :   QUIC_ASSERT (!quic_ctx_is_stream (ctx));
     731             : 
     732       13095 :   udp_session = session_get_from_handle_if_valid (ctx->udp_session_handle);
     733       13095 :   if (!udp_session)
     734           0 :     goto quicly_error;
     735             : 
     736       13095 :   conn = ctx->conn;
     737       13095 :   if (!conn)
     738           0 :     return 0;
     739             : 
     740             :   do
     741             :     {
     742             :       /* TODO : quicly can assert it can send min_packets up to 2 */
     743       14140 :       max_packets = quic_sendable_packet_count (udp_session);
     744       14140 :       if (max_packets < 2)
     745         141 :         break;
     746             : 
     747       13999 :       num_packets = max_packets;
     748       13999 :       if ((err = quicly_send (conn, &dest, &src, packets, &num_packets, buf,
     749             :                               sizeof (buf))))
     750          36 :         goto quicly_error;
     751             : 
     752       63566 :       for (i = 0; i != num_packets; ++i)
     753             :         {
     754             : 
     755       49603 :           if ((err =
     756       49603 :                  quic_send_datagram (udp_session, &packets[i], &dest, &src)))
     757           0 :             goto quicly_error;
     758             : 
     759             :         }
     760       13963 :       n_sent += num_packets;
     761             :     }
     762       13963 :   while (num_packets > 0 && num_packets == max_packets);
     763             : 
     764       13059 :   quic_set_udp_tx_evt (udp_session);
     765             : 
     766             :   QUIC_DBG (3, "%u[TX] %u[RX]", svm_fifo_max_dequeue (udp_session->tx_fifo),
     767             :             svm_fifo_max_dequeue (udp_session->rx_fifo));
     768       13059 :   quic_update_timer (ctx);
     769       13059 :   return n_sent;
     770             : 
     771          36 : quicly_error:
     772          36 :   if (err && err != QUICLY_ERROR_PACKET_IGNORED
     773          36 :       && err != QUICLY_ERROR_FREE_CONNECTION)
     774           0 :     clib_warning ("Quic error '%U'.", quic_format_err, err);
     775          36 :   quic_connection_closed (ctx);
     776          36 :   return 0;
     777             : }
     778             : 
     779             : /* Quicly callbacks */
     780             : 
     781             : static void
     782          36 : quic_on_stream_destroy (quicly_stream_t * stream, int err)
     783             : {
     784          36 :   quic_stream_data_t *stream_data = (quic_stream_data_t *) stream->data;
     785          36 :   quic_ctx_t *sctx = quic_ctx_get (stream_data->ctx_id,
     786             :                                    stream_data->thread_index);
     787          36 :   session_t *stream_session = session_get (sctx->c_s_index,
     788             :                                            sctx->c_thread_index);
     789             :   QUIC_DBG (2, "DESTROYED_STREAM: session 0x%lx (%U)",
     790             :             session_handle (stream_session), quic_format_err, err);
     791             : 
     792          36 :   stream_session->session_state = SESSION_STATE_CLOSED;
     793          36 :   session_transport_delete_notify (&sctx->connection);
     794             : 
     795          36 :   quic_increment_counter (QUIC_ERROR_CLOSED_STREAM, 1);
     796          36 :   quic_ctx_free (sctx);
     797          36 :   clib_mem_free (stream->data);
     798          36 : }
     799             : 
     800             : static void
     801           0 : quic_on_stop_sending (quicly_stream_t * stream, int err)
     802             : {
     803             : #if QUIC_DEBUG >= 2
     804             :   quic_stream_data_t *stream_data = (quic_stream_data_t *) stream->data;
     805             :   quic_ctx_t *sctx = quic_ctx_get (stream_data->ctx_id,
     806             :                                    stream_data->thread_index);
     807             :   session_t *stream_session = session_get (sctx->c_s_index,
     808             :                                            sctx->c_thread_index);
     809             :   clib_warning ("(NOT IMPLEMENTD) STOP_SENDING: session 0x%lx (%U)",
     810             :                 session_handle (stream_session), quic_format_err, err);
     811             : #endif
     812             :   /* TODO : handle STOP_SENDING */
     813           0 : }
     814             : 
     815             : static void
     816           0 : quic_on_receive_reset (quicly_stream_t * stream, int err)
     817             : {
     818           0 :   quic_stream_data_t *stream_data = (quic_stream_data_t *) stream->data;
     819           0 :   quic_ctx_t *sctx = quic_ctx_get (stream_data->ctx_id,
     820             :                                    stream_data->thread_index);
     821             : #if QUIC_DEBUG >= 2
     822             :   session_t *stream_session = session_get (sctx->c_s_index,
     823             :                                            sctx->c_thread_index);
     824             :   clib_warning ("RESET_STREAM: session 0x%lx (%U)",
     825             :                 session_handle (stream_session), quic_format_err, err);
     826             : #endif
     827           0 :   session_transport_closing_notify (&sctx->connection);
     828           0 : }
     829             : 
     830             : static void
     831       46112 : quic_on_receive (quicly_stream_t * stream, size_t off, const void *src,
     832             :                  size_t len)
     833             : {
     834             :   QUIC_DBG (3, "received data: %lu bytes, offset %lu", len, off);
     835             :   u32 max_enq, rv;
     836             :   quic_ctx_t *sctx;
     837             :   session_t *stream_session;
     838             :   app_worker_t *app_wrk;
     839             :   svm_fifo_t *f;
     840             :   quic_stream_data_t *stream_data;
     841             :   int rlen;
     842             : 
     843       46112 :   if (!len)
     844          18 :     return;
     845             : 
     846       46094 :   stream_data = (quic_stream_data_t *) stream->data;
     847       46094 :   sctx = quic_ctx_get (stream_data->ctx_id, stream_data->thread_index);
     848       46094 :   stream_session = session_get (sctx->c_s_index, stream_data->thread_index);
     849       46094 :   f = stream_session->rx_fifo;
     850             : 
     851       46094 :   max_enq = svm_fifo_max_enqueue_prod (f);
     852             :   QUIC_DBG (3, "Enqueuing %u at off %u in %u space", len, off, max_enq);
     853             :   /* Handle duplicate packet/chunk from quicly */
     854       46094 :   if (off < stream_data->app_rx_data_len)
     855             :     {
     856             :       QUIC_DBG (3, "Session [idx %u, app_wrk %u, thread %u, rx-fifo 0x%llx]: "
     857             :                 "DUPLICATE PACKET (max_enq %u, len %u, "
     858             :                 "app_rx_data_len %u, off %u, ToBeNQ %u)",
     859             :                 stream_session->session_index,
     860             :                 stream_session->app_wrk_index,
     861             :                 stream_session->thread_index, f,
     862             :                 max_enq, len, stream_data->app_rx_data_len, off,
     863             :                 off - stream_data->app_rx_data_len + len);
     864           0 :       return;
     865             :     }
     866       46094 :   if (PREDICT_FALSE ((off - stream_data->app_rx_data_len + len) > max_enq))
     867             :     {
     868           0 :       QUIC_ERR ("Session [idx %u, app_wrk %u, thread %u, rx-fifo 0x%llx]: "
     869             :                 "RX FIFO IS FULL (max_enq %u, len %u, "
     870             :                 "app_rx_data_len %u, off %u, ToBeNQ %u)",
     871             :                 stream_session->session_index,
     872             :                 stream_session->app_wrk_index,
     873             :                 stream_session->thread_index, f,
     874             :                 max_enq, len, stream_data->app_rx_data_len, off,
     875             :                 off - stream_data->app_rx_data_len + len);
     876           0 :       return;                   /* This shouldn't happen */
     877             :     }
     878       46094 :   if (off == stream_data->app_rx_data_len)
     879             :     {
     880             :       /* Streams live on the same thread so (f, stream_data) should stay consistent */
     881       46086 :       rlen = svm_fifo_enqueue (f, len, (u8 *) src);
     882       46086 :       if (PREDICT_FALSE (rlen < 0))
     883             :         {
     884             :           /*
     885             :            * drop, fifo full
     886             :            * drop, fifo grow
     887             :            */
     888           0 :           return;
     889             :         }
     890             :       QUIC_DBG (3, "Session [idx %u, app_wrk %u, ti %u, rx-fifo 0x%llx]: "
     891             :                 "Enqueuing %u (rlen %u) at off %u in %u space, ",
     892             :                 stream_session->session_index,
     893             :                 stream_session->app_wrk_index,
     894             :                 stream_session->thread_index, f, len, rlen, off, max_enq);
     895       46086 :       stream_data->app_rx_data_len += rlen;
     896       46086 :       QUIC_ASSERT (rlen >= len);
     897       46086 :       app_wrk = app_worker_get_if_valid (stream_session->app_wrk_index);
     898       46086 :       if (PREDICT_TRUE (app_wrk != 0))
     899             :         {
     900       46086 :           rv = app_worker_lock_and_send_event (app_wrk, stream_session,
     901             :                                                SESSION_IO_EVT_RX);
     902       46086 :           if (rv)
     903           0 :             QUIC_ERR ("Failed to ping app for RX");
     904             :         }
     905       46086 :       quic_ack_rx_data (stream_session);
     906             :     }
     907             :   else
     908             :     {
     909           8 :       rlen = svm_fifo_enqueue_with_offset (f,
     910           8 :                                            off - stream_data->app_rx_data_len,
     911             :                                            len, (u8 *) src);
     912           8 :       if (PREDICT_FALSE (rlen < 0))
     913             :         {
     914             :           /*
     915             :            * drop, fifo full
     916             :            * drop, fifo grow
     917             :            */
     918           0 :           return;
     919             :         }
     920           8 :       QUIC_ASSERT (rlen == 0);
     921             :     }
     922       46094 :   return;
     923             : }
     924             : 
     925             : void
     926        4577 : quic_fifo_egress_shift (quicly_stream_t * stream, size_t delta)
     927             : {
     928             :   quic_stream_data_t *stream_data;
     929             :   session_t *stream_session;
     930             :   quic_ctx_t *ctx;
     931             :   svm_fifo_t *f;
     932             :   u32 rv;
     933             : 
     934        4577 :   stream_data = (quic_stream_data_t *) stream->data;
     935        4577 :   stream_session = get_stream_session_and_ctx_from_stream (stream, &ctx);
     936        4577 :   f = stream_session->tx_fifo;
     937             : 
     938        4577 :   QUIC_ASSERT (stream_data->app_tx_data_len >= delta);
     939        4577 :   stream_data->app_tx_data_len -= delta;
     940        4577 :   ctx->bytes_written += delta;
     941        4577 :   rv = svm_fifo_dequeue_drop (f, delta);
     942        4577 :   QUIC_ASSERT (rv == delta);
     943             : 
     944        4577 :   rv = quicly_stream_sync_sendbuf (stream, 0);
     945        4577 :   QUIC_ASSERT (!rv);
     946        4577 : }
     947             : 
     948             : void
     949       46095 : quic_fifo_egress_emit (quicly_stream_t * stream, size_t off, void *dst,
     950             :                        size_t * len, int *wrote_all)
     951             : {
     952             :   quic_stream_data_t *stream_data;
     953             :   quic_ctx_t *ctx;
     954             :   session_t *stream_session;
     955             :   svm_fifo_t *f;
     956             :   u32 deq_max;
     957             : 
     958       46095 :   stream_data = (quic_stream_data_t *) stream->data;
     959       46095 :   stream_session = get_stream_session_and_ctx_from_stream (stream, &ctx);
     960       46095 :   f = stream_session->tx_fifo;
     961             : 
     962             :   QUIC_DBG (3, "Emitting %u, offset %u", *len, off);
     963             : 
     964       46095 :   deq_max = svm_fifo_max_dequeue_cons (f);
     965       46095 :   QUIC_ASSERT (off <= deq_max);
     966       46095 :   if (off + *len < deq_max)
     967             :     {
     968       45597 :       *wrote_all = 0;
     969             :     }
     970             :   else
     971             :     {
     972         498 :       *wrote_all = 1;
     973         498 :       *len = deq_max - off;
     974             :     }
     975       46095 :   QUIC_ASSERT (*len > 0);
     976             : 
     977       46095 :   if (off + *len > stream_data->app_tx_data_len)
     978        1638 :     stream_data->app_tx_data_len = off + *len;
     979             : 
     980       46095 :   svm_fifo_peek (f, off, *len, dst);
     981       46095 : }
     982             : 
     983             : static const quicly_stream_callbacks_t quic_stream_callbacks = {
     984             :   .on_destroy = quic_on_stream_destroy,
     985             :   .on_send_shift = quic_fifo_egress_shift,
     986             :   .on_send_emit = quic_fifo_egress_emit,
     987             :   .on_send_stop = quic_on_stop_sending,
     988             :   .on_receive = quic_on_receive,
     989             :   .on_receive_reset = quic_on_receive_reset
     990             : };
     991             : 
     992             : static int
     993          36 : quic_on_stream_open (quicly_stream_open_t * self, quicly_stream_t * stream)
     994             : {
     995             :   /* Return code for this function ends either
     996             :    * - in quicly_receive : if not QUICLY_ERROR_PACKET_IGNORED, will close connection
     997             :    * - in quicly_open_stream, returned directly
     998             :    */
     999             : 
    1000             :   session_t *stream_session, *quic_session;
    1001             :   quic_stream_data_t *stream_data;
    1002             :   app_worker_t *app_wrk;
    1003             :   quic_ctx_t *qctx, *sctx;
    1004             :   u32 sctx_id;
    1005             :   int rv;
    1006             : 
    1007             :   QUIC_DBG (2, "on_stream_open called");
    1008          36 :   stream->data = clib_mem_alloc (sizeof (quic_stream_data_t));
    1009          36 :   stream->callbacks = &quic_stream_callbacks;
    1010             :   /* Notify accept on parent qsession, but only if this is not a locally
    1011             :    * initiated stream */
    1012          36 :   if (quicly_stream_is_self_initiated (stream))
    1013          18 :     return 0;
    1014             : 
    1015          18 :   sctx_id = quic_ctx_alloc (vlib_get_thread_index ());
    1016          18 :   qctx = quic_get_conn_ctx (stream->conn);
    1017             : 
    1018             :   /* Might need to signal that the connection is ready if the first thing the
    1019             :    * server does is open a stream */
    1020          18 :   quic_check_quic_session_connected (qctx);
    1021             :   /* ctx might be invalidated */
    1022          18 :   qctx = quic_get_conn_ctx (stream->conn);
    1023             : 
    1024          18 :   stream_session = session_alloc (qctx->c_thread_index);
    1025             :   QUIC_DBG (2, "ACCEPTED stream_session 0x%lx ctx %u",
    1026             :             session_handle (stream_session), sctx_id);
    1027          18 :   sctx = quic_ctx_get (sctx_id, qctx->c_thread_index);
    1028          18 :   sctx->parent_app_wrk_id = qctx->parent_app_wrk_id;
    1029          18 :   sctx->parent_app_id = qctx->parent_app_id;
    1030          18 :   sctx->quic_connection_ctx_id = qctx->c_c_index;
    1031          18 :   sctx->c_c_index = sctx_id;
    1032          18 :   sctx->c_s_index = stream_session->session_index;
    1033          18 :   sctx->stream = stream;
    1034          18 :   sctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
    1035          18 :   sctx->flags |= QUIC_F_IS_STREAM;
    1036          18 :   sctx->crypto_context_index = qctx->crypto_context_index;
    1037          18 :   if (quicly_stream_is_unidirectional (stream->stream_id))
    1038           0 :     stream_session->flags |= SESSION_F_UNIDIRECTIONAL;
    1039             : 
    1040          18 :   stream_data = (quic_stream_data_t *) stream->data;
    1041          18 :   stream_data->ctx_id = sctx_id;
    1042          18 :   stream_data->thread_index = sctx->c_thread_index;
    1043          18 :   stream_data->app_rx_data_len = 0;
    1044          18 :   stream_data->app_tx_data_len = 0;
    1045             : 
    1046          18 :   sctx->c_s_index = stream_session->session_index;
    1047          18 :   stream_session->session_state = SESSION_STATE_CREATED;
    1048          18 :   stream_session->app_wrk_index = sctx->parent_app_wrk_id;
    1049          18 :   stream_session->connection_index = sctx->c_c_index;
    1050          18 :   stream_session->session_type =
    1051          18 :     session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC, qctx->udp_is_ip4);
    1052          18 :   quic_session = session_get (qctx->c_s_index, qctx->c_thread_index);
    1053          18 :   stream_session->listener_handle = listen_session_get_handle (quic_session);
    1054             : 
    1055          18 :   app_wrk = app_worker_get (stream_session->app_wrk_index);
    1056          18 :   if ((rv = app_worker_init_connected (app_wrk, stream_session)))
    1057             :     {
    1058           0 :       QUIC_ERR ("failed to allocate fifos");
    1059           0 :       quicly_reset_stream (stream, QUIC_APP_ALLOCATION_ERROR);
    1060           0 :       return 0;                 /* Frame is still valid */
    1061             :     }
    1062          18 :   svm_fifo_add_want_deq_ntf (stream_session->rx_fifo,
    1063             :                              SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL |
    1064             :                              SVM_FIFO_WANT_DEQ_NOTIF_IF_EMPTY);
    1065             : 
    1066          18 :   if ((rv = app_worker_accept_notify (app_wrk, stream_session)))
    1067             :     {
    1068           0 :       QUIC_ERR ("failed to notify accept worker app");
    1069           0 :       quicly_reset_stream (stream, QUIC_APP_ACCEPT_NOTIFY_ERROR);
    1070           0 :       return 0;                 /* Frame is still valid */
    1071             :     }
    1072             : 
    1073          18 :   return 0;
    1074             : }
    1075             : 
    1076             : static void
    1077          18 : quic_on_closed_by_remote (quicly_closed_by_remote_t *self, quicly_conn_t *conn,
    1078             :                           int code, uint64_t frame_type, const char *reason,
    1079             :                           size_t reason_len)
    1080             : {
    1081          18 :   quic_ctx_t *ctx = quic_get_conn_ctx (conn);
    1082             : #if QUIC_DEBUG >= 2
    1083             :   session_t *quic_session = session_get (ctx->c_s_index, ctx->c_thread_index);
    1084             :   clib_warning ("Session 0x%lx closed by peer (%U) %.*s ",
    1085             :                 session_handle (quic_session), quic_format_err, code,
    1086             :                 reason_len, reason);
    1087             : #endif
    1088          18 :   ctx->conn_state = QUIC_CONN_STATE_PASSIVE_CLOSING;
    1089          18 :   session_transport_closing_notify (&ctx->connection);
    1090          18 : }
    1091             : 
    1092             : /* Timer handling */
    1093             : 
    1094             : static int64_t
    1095       76733 : quic_get_thread_time (u8 thread_index)
    1096             : {
    1097       76733 :   return quic_main.wrk_ctx[thread_index].time_now;
    1098             : }
    1099             : 
    1100             : static int64_t
    1101       76733 : quic_get_time (quicly_now_t * self)
    1102             : {
    1103       76733 :   u8 thread_index = vlib_get_thread_index ();
    1104       76733 :   return quic_get_thread_time (thread_index);
    1105             : }
    1106             : 
    1107             : static u32
    1108     8899900 : quic_set_time_now (u32 thread_index)
    1109             : {
    1110     8899900 :   vlib_main_t *vlib_main = vlib_get_main ();
    1111     8889680 :   f64 time = vlib_time_now (vlib_main);
    1112     8962320 :   quic_main.wrk_ctx[thread_index].time_now = (int64_t) (time * 1000.f);
    1113     8962320 :   return quic_main.wrk_ctx[thread_index].time_now;
    1114             : }
    1115             : 
    1116             : /* Transport proto callback */
    1117             : static void
    1118     8900820 : quic_update_time (f64 now, u8 thread_index)
    1119             : {
    1120             :   tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
    1121             : 
    1122     8900820 :   tw = &quic_main.wrk_ctx[thread_index].timer_wheel;
    1123     8900820 :   quic_set_time_now (thread_index);
    1124     8962380 :   tw_timer_expire_timers_1t_3w_1024sl_ov (tw, now);
    1125     8958670 : }
    1126             : 
    1127             : static void
    1128          66 : quic_timer_expired (u32 conn_index)
    1129             : {
    1130             :   quic_ctx_t *ctx;
    1131             :   QUIC_DBG (4, "Timer expired for conn %u at %ld", conn_index,
    1132             :             quic_get_time (NULL));
    1133          66 :   ctx = quic_ctx_get (conn_index, vlib_get_thread_index ());
    1134          66 :   ctx->timer_handle = QUIC_TIMER_HANDLE_INVALID;
    1135          66 :   quic_send_packets (ctx);
    1136          66 : }
    1137             : 
    1138             : static void
    1139       13059 : quic_update_timer (quic_ctx_t * ctx)
    1140             : {
    1141             :   tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
    1142             :   int64_t next_timeout, next_interval;
    1143             :   session_t *quic_session;
    1144             :   int rv;
    1145             : 
    1146             :   /*  This timeout is in ms which is the unit of our timer */
    1147       13059 :   next_timeout = quicly_get_first_timeout (ctx->conn);
    1148       13059 :   next_interval = next_timeout - quic_get_time (NULL);
    1149             : 
    1150       13059 :   if (next_timeout == 0 || next_interval <= 0)
    1151             :     {
    1152         114 :       if (ctx->c_s_index == QUIC_SESSION_INVALID)
    1153             :         {
    1154           0 :           next_interval = 1;
    1155             :         }
    1156             :       else
    1157             :         {
    1158         114 :           quic_session = session_get (ctx->c_s_index, ctx->c_thread_index);
    1159         114 :           if (svm_fifo_set_event (quic_session->tx_fifo))
    1160             :             {
    1161           9 :               rv = session_send_io_evt_to_thread_custom (
    1162           9 :                 quic_session, quic_session->thread_index, SESSION_IO_EVT_TX);
    1163           9 :               if (PREDICT_FALSE (rv))
    1164           0 :                 QUIC_ERR ("Failed to enqueue builtin_tx %d", rv);
    1165             :             }
    1166         114 :           return;
    1167             :         }
    1168             :     }
    1169             : 
    1170       12945 :   ASSERT (vlib_get_thread_index () == ctx->c_thread_index ||
    1171             :           vlib_get_thread_index () == 0);
    1172       12945 :   tw = &quic_main.wrk_ctx[ctx->c_thread_index].timer_wheel;
    1173             : 
    1174             :   QUIC_DBG (4, "Timer set to %ld (int %ld) for ctx %u", next_timeout,
    1175             :             next_interval, ctx->c_c_index);
    1176             : 
    1177       12945 :   if (ctx->timer_handle == QUIC_TIMER_HANDLE_INVALID)
    1178             :     {
    1179          66 :       if (next_timeout == INT64_MAX)
    1180             :         {
    1181             :           QUIC_DBG (4, "timer for ctx %u already stopped", ctx->c_c_index);
    1182           0 :           return;
    1183             :         }
    1184          66 :       ctx->timer_handle = tw_timer_start_1t_3w_1024sl_ov (tw, ctx->c_c_index,
    1185             :                                                           0, next_interval);
    1186             :     }
    1187             :   else
    1188             :     {
    1189       12879 :       if (next_timeout == INT64_MAX)
    1190             :         {
    1191           0 :           quic_stop_ctx_timer (ctx);
    1192             :         }
    1193             :       else
    1194       12879 :         tw_timer_update_1t_3w_1024sl_ov (tw, ctx->timer_handle,
    1195             :                                          next_interval);
    1196             :     }
    1197       12945 :   return;
    1198             : }
    1199             : 
    1200             : static void
    1201          44 : quic_expired_timers_dispatch (u32 * expired_timers)
    1202             : {
    1203             :   int i;
    1204             : 
    1205         110 :   for (i = 0; i < vec_len (expired_timers); i++)
    1206             :     {
    1207          66 :       quic_timer_expired (expired_timers[i]);
    1208             :     }
    1209          44 : }
    1210             : 
    1211             : /* Transport proto functions */
    1212             : static int
    1213          18 : quic_connect_stream (session_t * quic_session, session_endpoint_cfg_t * sep)
    1214             : {
    1215             :   uint64_t quic_session_handle;
    1216             :   session_t *stream_session;
    1217             :   quic_stream_data_t *stream_data;
    1218             :   quicly_stream_t *stream;
    1219             :   quicly_conn_t *conn;
    1220             :   app_worker_t *app_wrk;
    1221             :   quic_ctx_t *qctx, *sctx;
    1222             :   u32 sctx_index;
    1223             :   u8 is_unidir;
    1224             :   int rv;
    1225             : 
    1226             :   /*  Find base session to which the user want to attach a stream */
    1227          18 :   quic_session_handle = session_handle (quic_session);
    1228             :   QUIC_DBG (2, "Opening new stream (qsession %u)", quic_session_handle);
    1229             : 
    1230          18 :   if (session_type_transport_proto (quic_session->session_type) !=
    1231             :       TRANSPORT_PROTO_QUIC)
    1232             :     {
    1233           0 :       QUIC_ERR ("received incompatible session");
    1234           0 :       return -1;
    1235             :     }
    1236             : 
    1237          18 :   app_wrk = app_worker_get_if_valid (quic_session->app_wrk_index);
    1238          18 :   if (!app_wrk)
    1239             :     {
    1240           0 :       QUIC_ERR ("Invalid app worker :(");
    1241           0 :       return -1;
    1242             :     }
    1243             : 
    1244          18 :   sctx_index = quic_ctx_alloc (quic_session->thread_index);  /*  Allocate before we get pointers */
    1245          18 :   sctx = quic_ctx_get (sctx_index, quic_session->thread_index);
    1246          18 :   qctx = quic_ctx_get (quic_session->connection_index,
    1247          18 :                        quic_session->thread_index);
    1248          18 :   if (quic_ctx_is_stream (qctx))
    1249             :     {
    1250           0 :       QUIC_ERR ("session is a stream");
    1251           0 :       quic_ctx_free (sctx);
    1252           0 :       return -1;
    1253             :     }
    1254             : 
    1255          18 :   sctx->parent_app_wrk_id = qctx->parent_app_wrk_id;
    1256          18 :   sctx->parent_app_id = qctx->parent_app_id;
    1257          18 :   sctx->quic_connection_ctx_id = qctx->c_c_index;
    1258          18 :   sctx->c_c_index = sctx_index;
    1259          18 :   sctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
    1260          18 :   sctx->flags |= QUIC_F_IS_STREAM;
    1261             : 
    1262          18 :   conn = qctx->conn;
    1263             : 
    1264          18 :   if (!conn || !quicly_connection_is_ready (conn))
    1265           0 :     return -1;
    1266             : 
    1267          18 :   is_unidir = sep->transport_flags & TRANSPORT_CFG_F_UNIDIRECTIONAL;
    1268          18 :   if ((rv = quicly_open_stream (conn, &stream, is_unidir)))
    1269             :     {
    1270             :       QUIC_DBG (2, "Stream open failed with %d", rv);
    1271           0 :       return -1;
    1272             :     }
    1273          18 :   quic_increment_counter (QUIC_ERROR_OPENED_STREAM, 1);
    1274             : 
    1275          18 :   sctx->stream = stream;
    1276          18 :   sctx->crypto_context_index = qctx->crypto_context_index;
    1277             : 
    1278             :   QUIC_DBG (2, "Opened stream %d, creating session", stream->stream_id);
    1279             : 
    1280          18 :   stream_session = session_alloc (qctx->c_thread_index);
    1281             :   QUIC_DBG (2, "Allocated stream_session 0x%lx ctx %u",
    1282             :             session_handle (stream_session), sctx_index);
    1283          18 :   stream_session->app_wrk_index = app_wrk->wrk_index;
    1284          18 :   stream_session->connection_index = sctx_index;
    1285          18 :   stream_session->listener_handle = quic_session_handle;
    1286          18 :   stream_session->session_type =
    1287          18 :     session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC, qctx->udp_is_ip4);
    1288          18 :   if (is_unidir)
    1289           0 :     stream_session->flags |= SESSION_F_UNIDIRECTIONAL;
    1290             : 
    1291          18 :   sctx->c_s_index = stream_session->session_index;
    1292          18 :   stream_data = (quic_stream_data_t *) stream->data;
    1293          18 :   stream_data->ctx_id = sctx->c_c_index;
    1294          18 :   stream_data->thread_index = sctx->c_thread_index;
    1295          18 :   stream_data->app_rx_data_len = 0;
    1296          18 :   stream_data->app_tx_data_len = 0;
    1297          18 :   stream_session->session_state = SESSION_STATE_READY;
    1298             : 
    1299             :   /* For now we only reset streams. Cleanup will be triggered by timers */
    1300          18 :   if ((rv = app_worker_init_connected (app_wrk, stream_session)))
    1301             :     {
    1302           0 :       QUIC_ERR ("failed to app_worker_init_connected");
    1303           0 :       quicly_reset_stream (stream, QUIC_APP_CONNECT_NOTIFY_ERROR);
    1304           0 :       return app_worker_connect_notify (app_wrk, NULL, rv, sep->opaque);
    1305             :     }
    1306             : 
    1307          18 :   svm_fifo_add_want_deq_ntf (stream_session->rx_fifo,
    1308             :                              SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL |
    1309             :                              SVM_FIFO_WANT_DEQ_NOTIF_IF_EMPTY);
    1310             : 
    1311          18 :   if (app_worker_connect_notify (app_wrk, stream_session, SESSION_E_NONE,
    1312             :                                  sep->opaque))
    1313             :     {
    1314           0 :       QUIC_ERR ("failed to notify app");
    1315           0 :       quic_increment_counter (QUIC_ERROR_CLOSED_STREAM, 1);
    1316           0 :       quicly_reset_stream (stream, QUIC_APP_CONNECT_NOTIFY_ERROR);
    1317           0 :       return -1;
    1318             :     }
    1319             : 
    1320          18 :   return 0;
    1321             : }
    1322             : 
    1323             : static int
    1324          18 : quic_connect_connection (session_endpoint_cfg_t * sep)
    1325             : {
    1326          18 :   vnet_connect_args_t _cargs, *cargs = &_cargs;
    1327             :   transport_endpt_crypto_cfg_t *ccfg;
    1328          18 :   quic_main_t *qm = &quic_main;
    1329             :   u32 ctx_index, thread_index;
    1330             :   quic_ctx_t *ctx;
    1331             :   app_worker_t *app_wrk;
    1332             :   application_t *app;
    1333             :   int error;
    1334             : 
    1335          18 :   if (!sep->ext_cfg)
    1336           0 :     return SESSION_E_NOEXTCFG;
    1337             : 
    1338             :   /* Use pool on thread 1 if we have workers because of UDP */
    1339          18 :   thread_index = transport_cl_thread ();
    1340          18 :   ccfg = &sep->ext_cfg->crypto;
    1341             : 
    1342          18 :   clib_memset (cargs, 0, sizeof (*cargs));
    1343          18 :   ctx_index = quic_ctx_alloc (thread_index);
    1344          18 :   ctx = quic_ctx_get (ctx_index, thread_index);
    1345          18 :   ctx->parent_app_wrk_id = sep->app_wrk_index;
    1346          18 :   ctx->c_s_index = QUIC_SESSION_INVALID;
    1347          18 :   ctx->c_c_index = ctx_index;
    1348          18 :   ctx->udp_is_ip4 = sep->is_ip4;
    1349          18 :   ctx->timer_handle = QUIC_TIMER_HANDLE_INVALID;
    1350          18 :   ctx->conn_state = QUIC_CONN_STATE_HANDSHAKE;
    1351          18 :   ctx->client_opaque = sep->opaque;
    1352          18 :   ctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
    1353          18 :   if (ccfg->hostname[0])
    1354           0 :     ctx->srv_hostname = format (0, "%s", ccfg->hostname);
    1355             :   else
    1356             :     /*  needed by quic for crypto + determining client / server */
    1357          18 :     ctx->srv_hostname = format (0, "%U", format_ip46_address,
    1358          18 :                                 &sep->ip, sep->is_ip4);
    1359          18 :   vec_terminate_c_string (ctx->srv_hostname);
    1360             : 
    1361          18 :   clib_memcpy (&cargs->sep_ext, sep, sizeof (session_endpoint_cfg_t));
    1362          18 :   cargs->sep.transport_proto = TRANSPORT_PROTO_UDP;
    1363          18 :   cargs->app_index = qm->app_index;
    1364          18 :   cargs->api_context = ctx_index;
    1365             : 
    1366          18 :   app_wrk = app_worker_get (sep->app_wrk_index);
    1367          18 :   app = application_get (app_wrk->app_index);
    1368          18 :   ctx->parent_app_id = app_wrk->app_index;
    1369          18 :   cargs->sep_ext.ns_index = app->ns_index;
    1370          18 :   cargs->sep_ext.transport_flags = TRANSPORT_CFG_F_CONNECTED;
    1371             : 
    1372          18 :   ctx->crypto_engine = ccfg->crypto_engine;
    1373          18 :   ctx->ckpair_index = ccfg->ckpair_index;
    1374          18 :   if ((error = quic_acquire_crypto_context (ctx)))
    1375           0 :     return error;
    1376             : 
    1377          18 :   if ((error = vnet_connect (cargs)))
    1378           0 :     return error;
    1379             : 
    1380          18 :   return 0;
    1381             : }
    1382             : 
    1383             : static int
    1384          36 : quic_connect (transport_endpoint_cfg_t * tep)
    1385             : {
    1386             :   QUIC_DBG (2, "Called quic_connect");
    1387          36 :   session_endpoint_cfg_t *sep = (session_endpoint_cfg_t *) tep;
    1388             :   session_t *quic_session;
    1389          36 :   sep = (session_endpoint_cfg_t *) tep;
    1390             : 
    1391          36 :   quic_session = session_get_from_handle_if_valid (sep->parent_handle);
    1392          36 :   if (quic_session)
    1393          18 :     return quic_connect_stream (quic_session, sep);
    1394             :   else
    1395          18 :     return quic_connect_connection (sep);
    1396             : }
    1397             : 
    1398             : static void
    1399          54 : quic_proto_on_close (u32 ctx_index, u32 thread_index)
    1400             : {
    1401             :   int err;
    1402          54 :   quic_ctx_t *ctx = quic_ctx_get_if_valid (ctx_index, thread_index);
    1403          54 :   if (!ctx)
    1404           0 :     return;
    1405          54 :   session_t *stream_session = session_get (ctx->c_s_index,
    1406             :                                            ctx->c_thread_index);
    1407             : #if QUIC_DEBUG >= 2
    1408             :   clib_warning ("Closing session 0x%lx", session_handle (stream_session));
    1409             : #endif
    1410          54 :   if (quic_ctx_is_stream (ctx))
    1411             :     {
    1412          18 :       quicly_stream_t *stream = ctx->stream;
    1413          18 :       if (!quicly_stream_has_send_side (quicly_is_client (stream->conn),
    1414             :                                         stream->stream_id))
    1415           0 :         return;
    1416          36 :       quicly_sendstate_shutdown (&stream->sendstate, ctx->bytes_written +
    1417          18 :                                  svm_fifo_max_dequeue
    1418             :                                  (stream_session->tx_fifo));
    1419          18 :       err = quicly_stream_sync_sendbuf (stream, 1);
    1420          18 :       if (err)
    1421             :         {
    1422             :           QUIC_DBG (1, "sendstate_shutdown failed for stream session %lu",
    1423             :                     session_handle (stream_session));
    1424           0 :           quicly_reset_stream (stream, QUIC_APP_ERROR_CLOSE_NOTIFY);
    1425             :         }
    1426          18 :       quic_send_packets (ctx);
    1427          18 :       return;
    1428             :     }
    1429             : 
    1430          36 :   switch (ctx->conn_state)
    1431             :     {
    1432          18 :     case QUIC_CONN_STATE_OPENED:
    1433             :     case QUIC_CONN_STATE_HANDSHAKE:
    1434             :     case QUIC_CONN_STATE_READY:
    1435          18 :       ctx->conn_state = QUIC_CONN_STATE_ACTIVE_CLOSING;
    1436          18 :       quicly_conn_t *conn = ctx->conn;
    1437             :       /* Start connection closing. Keep sending packets until quicly_send
    1438             :          returns QUICLY_ERROR_FREE_CONNECTION */
    1439             : 
    1440          18 :       quic_increment_counter (QUIC_ERROR_CLOSED_CONNECTION, 1);
    1441          18 :       quicly_close (conn, QUIC_APP_ERROR_CLOSE_NOTIFY, "Closed by peer");
    1442             :       /* This also causes all streams to be closed (and the cb called) */
    1443          18 :       quic_send_packets (ctx);
    1444          18 :       break;
    1445          18 :     case QUIC_CONN_STATE_PASSIVE_CLOSING:
    1446          18 :       ctx->conn_state = QUIC_CONN_STATE_PASSIVE_CLOSING_APP_CLOSED;
    1447             :       /* send_packets will eventually return an error, we delete the conn at
    1448             :          that point */
    1449          18 :       break;
    1450           0 :     case QUIC_CONN_STATE_PASSIVE_CLOSING_QUIC_CLOSED:
    1451           0 :       quic_connection_delete (ctx);
    1452           0 :       break;
    1453           0 :     case QUIC_CONN_STATE_ACTIVE_CLOSING:
    1454           0 :       break;
    1455           0 :     default:
    1456           0 :       QUIC_ERR ("Trying to close conn in state %d", ctx->conn_state);
    1457           0 :       break;
    1458             :     }
    1459             : }
    1460             : 
    1461             : static u32
    1462           5 : quic_start_listen (u32 quic_listen_session_index,
    1463             :                    transport_endpoint_cfg_t *tep)
    1464             : {
    1465           5 :   vnet_listen_args_t _bargs, *args = &_bargs;
    1466             :   transport_endpt_crypto_cfg_t *ccfg;
    1467           5 :   quic_main_t *qm = &quic_main;
    1468             :   session_handle_t udp_handle;
    1469             :   session_endpoint_cfg_t *sep;
    1470             :   session_t *udp_listen_session;
    1471             :   app_worker_t *app_wrk;
    1472             :   application_t *app;
    1473             :   quic_ctx_t *lctx;
    1474             :   u32 lctx_index;
    1475             :   app_listener_t *app_listener;
    1476             :   int rv;
    1477             : 
    1478           5 :   sep = (session_endpoint_cfg_t *) tep;
    1479           5 :   if (!sep->ext_cfg)
    1480           0 :     return SESSION_E_NOEXTCFG;
    1481             : 
    1482           5 :   ccfg = &sep->ext_cfg->crypto;
    1483           5 :   app_wrk = app_worker_get (sep->app_wrk_index);
    1484           5 :   app = application_get (app_wrk->app_index);
    1485             :   QUIC_DBG (2, "Called quic_start_listen for app %d", app_wrk->app_index);
    1486             : 
    1487           5 :   clib_memset (args, 0, sizeof (*args));
    1488           5 :   args->app_index = qm->app_index;
    1489           5 :   args->sep_ext = *sep;
    1490           5 :   args->sep_ext.ns_index = app->ns_index;
    1491           5 :   args->sep_ext.transport_proto = TRANSPORT_PROTO_UDP;
    1492           5 :   args->sep_ext.transport_flags = TRANSPORT_CFG_F_CONNECTED;
    1493           5 :   if ((rv = vnet_listen (args)))
    1494           0 :     return rv;
    1495             : 
    1496           5 :   lctx_index = quic_ctx_alloc (0);
    1497           5 :   udp_handle = args->handle;
    1498           5 :   app_listener = app_listener_get_w_handle (udp_handle);
    1499           5 :   udp_listen_session = app_listener_get_session (app_listener);
    1500           5 :   udp_listen_session->opaque = lctx_index;
    1501             : 
    1502           5 :   lctx = quic_ctx_get (lctx_index, 0);
    1503           5 :   lctx->flags |= QUIC_F_IS_LISTENER;
    1504             : 
    1505           5 :   clib_memcpy (&lctx->c_rmt_ip, &args->sep.peer.ip, sizeof (ip46_address_t));
    1506           5 :   clib_memcpy (&lctx->c_lcl_ip, &args->sep.ip, sizeof (ip46_address_t));
    1507           5 :   lctx->c_rmt_port = args->sep.peer.port;
    1508           5 :   lctx->c_lcl_port = args->sep.port;
    1509           5 :   lctx->c_is_ip4 = args->sep.is_ip4;
    1510           5 :   lctx->c_fib_index = args->sep.fib_index;
    1511           5 :   lctx->c_proto = TRANSPORT_PROTO_QUIC;
    1512           5 :   lctx->parent_app_wrk_id = sep->app_wrk_index;
    1513           5 :   lctx->parent_app_id = app_wrk->app_index;
    1514           5 :   lctx->udp_session_handle = udp_handle;
    1515           5 :   lctx->c_s_index = quic_listen_session_index;
    1516           5 :   lctx->crypto_engine = ccfg->crypto_engine;
    1517           5 :   lctx->ckpair_index = ccfg->ckpair_index;
    1518           5 :   if ((rv = quic_acquire_crypto_context (lctx)))
    1519           0 :     return rv;
    1520             : 
    1521             :   QUIC_DBG (2, "Listening UDP session 0x%lx",
    1522             :             session_handle (udp_listen_session));
    1523             :   QUIC_DBG (2, "Listening QUIC session 0x%lx", quic_listen_session_index);
    1524           5 :   return lctx_index;
    1525             : }
    1526             : 
    1527             : static u32
    1528           2 : quic_stop_listen (u32 lctx_index)
    1529             : {
    1530             :   QUIC_DBG (2, "Called quic_stop_listen");
    1531             :   quic_ctx_t *lctx;
    1532           2 :   lctx = quic_ctx_get (lctx_index, 0);
    1533           2 :   QUIC_ASSERT (quic_ctx_is_listener (lctx));
    1534           2 :   vnet_unlisten_args_t a = {
    1535           2 :     .handle = lctx->udp_session_handle,
    1536           2 :     .app_index = quic_main.app_index,
    1537             :     .wrk_map_index = 0          /* default wrk */
    1538             :   };
    1539           2 :   if (vnet_unlisten (&a))
    1540           0 :     clib_warning ("unlisten errored");
    1541             : 
    1542           2 :   quic_release_crypto_context (lctx->crypto_context_index,
    1543             :                                0 /* thread_index */ );
    1544           2 :   quic_ctx_free (lctx);
    1545           2 :   return 0;
    1546             : }
    1547             : 
    1548             : static transport_connection_t *
    1549      167756 : quic_connection_get (u32 ctx_index, u32 thread_index)
    1550             : {
    1551             :   quic_ctx_t *ctx;
    1552      167756 :   ctx = quic_ctx_get (ctx_index, thread_index);
    1553      167756 :   return &ctx->connection;
    1554             : }
    1555             : 
    1556             : static transport_connection_t *
    1557          12 : quic_listener_get (u32 listener_index)
    1558             : {
    1559             :   QUIC_DBG (2, "Called quic_listener_get");
    1560             :   quic_ctx_t *ctx;
    1561          12 :   ctx = quic_ctx_get (listener_index, 0);
    1562          12 :   return &ctx->connection;
    1563             : }
    1564             : 
    1565             : static u8 *
    1566           0 : format_quic_ctx (u8 * s, va_list * args)
    1567             : {
    1568           0 :   quic_ctx_t *ctx = va_arg (*args, quic_ctx_t *);
    1569           0 :   u32 verbose = va_arg (*args, u32);
    1570           0 :   u8 *str = 0;
    1571             : 
    1572           0 :   if (!ctx)
    1573           0 :     return s;
    1574           0 :   str = format (str, "[#%d][Q] ", ctx->c_thread_index);
    1575             : 
    1576           0 :   if (quic_ctx_is_listener (ctx))
    1577           0 :     str = format (str, "Listener, UDP %ld", ctx->udp_session_handle);
    1578           0 :   else if (quic_ctx_is_stream (ctx))
    1579           0 :     str = format (str, "Stream %ld conn %d",
    1580           0 :                   ctx->stream->stream_id, ctx->quic_connection_ctx_id);
    1581             :   else                          /* connection */
    1582           0 :     str = format (str, "Conn %d UDP %d", ctx->c_c_index,
    1583             :                   ctx->udp_session_handle);
    1584             : 
    1585           0 :   str = format (str, " app %d wrk %d", ctx->parent_app_id,
    1586             :                 ctx->parent_app_wrk_id);
    1587             : 
    1588           0 :   if (verbose == 1)
    1589           0 :     s = format (s, "%-" SESSION_CLI_ID_LEN "s%-" SESSION_CLI_STATE_LEN "d",
    1590           0 :                 str, ctx->conn_state);
    1591             :   else
    1592           0 :     s = format (s, "%s\n", str);
    1593           0 :   vec_free (str);
    1594           0 :   return s;
    1595             : }
    1596             : 
    1597             : static u8 *
    1598           0 : format_quic_connection (u8 * s, va_list * args)
    1599             : {
    1600           0 :   u32 qc_index = va_arg (*args, u32);
    1601           0 :   u32 thread_index = va_arg (*args, u32);
    1602           0 :   u32 verbose = va_arg (*args, u32);
    1603           0 :   quic_ctx_t *ctx = quic_ctx_get (qc_index, thread_index);
    1604           0 :   s = format (s, "%U", format_quic_ctx, ctx, verbose);
    1605           0 :   return s;
    1606             : }
    1607             : 
    1608             : static u8 *
    1609           0 : format_quic_half_open (u8 * s, va_list * args)
    1610             : {
    1611           0 :   u32 qc_index = va_arg (*args, u32);
    1612           0 :   u32 thread_index = va_arg (*args, u32);
    1613           0 :   quic_ctx_t *ctx = quic_ctx_get (qc_index, thread_index);
    1614           0 :   s = format (s, "[#%d][Q] half-open app %u", thread_index,
    1615             :               ctx->parent_app_id);
    1616           0 :   return s;
    1617             : }
    1618             : 
    1619             : /*  TODO improve */
    1620             : static u8 *
    1621           0 : format_quic_listener (u8 * s, va_list * args)
    1622             : {
    1623           0 :   u32 tci = va_arg (*args, u32);
    1624           0 :   u32 thread_index = va_arg (*args, u32);
    1625           0 :   u32 verbose = va_arg (*args, u32);
    1626           0 :   quic_ctx_t *ctx = quic_ctx_get (tci, thread_index);
    1627           0 :   s = format (s, "%U", format_quic_ctx, ctx, verbose);
    1628           0 :   return s;
    1629             : }
    1630             : 
    1631             : /* Session layer callbacks */
    1632             : 
    1633             : static inline void
    1634       49621 : quic_build_sockaddr (struct sockaddr *sa, socklen_t * salen,
    1635             :                      ip46_address_t * addr, u16 port, u8 is_ip4)
    1636             : {
    1637       49621 :   if (is_ip4)
    1638             :     {
    1639       49621 :       struct sockaddr_in *sa4 = (struct sockaddr_in *) sa;
    1640       49621 :       sa4->sin_family = AF_INET;
    1641       49621 :       sa4->sin_port = port;
    1642       49621 :       sa4->sin_addr.s_addr = addr->ip4.as_u32;
    1643       49621 :       *salen = sizeof (struct sockaddr_in);
    1644             :     }
    1645             :   else
    1646             :     {
    1647           0 :       struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) sa;
    1648           0 :       sa6->sin6_family = AF_INET6;
    1649           0 :       sa6->sin6_port = port;
    1650           0 :       clib_memcpy (&sa6->sin6_addr, &addr->ip6, 16);
    1651           0 :       *salen = sizeof (struct sockaddr_in6);
    1652             :     }
    1653       49621 : }
    1654             : 
    1655             : static void
    1656          18 : quic_on_quic_session_connected (quic_ctx_t * ctx)
    1657             : {
    1658             :   session_t *quic_session;
    1659             :   app_worker_t *app_wrk;
    1660          18 :   u32 ctx_id = ctx->c_c_index;
    1661          18 :   u32 thread_index = ctx->c_thread_index;
    1662             :   int rv;
    1663             : 
    1664          18 :   quic_session = session_alloc (thread_index);
    1665             : 
    1666             :   QUIC_DBG (2, "Allocated quic session 0x%lx", session_handle (quic_session));
    1667          18 :   ctx->c_s_index = quic_session->session_index;
    1668          18 :   quic_session->app_wrk_index = ctx->parent_app_wrk_id;
    1669          18 :   quic_session->connection_index = ctx->c_c_index;
    1670          18 :   quic_session->listener_handle = SESSION_INVALID_HANDLE;
    1671          18 :   quic_session->session_type =
    1672          18 :     session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC, ctx->udp_is_ip4);
    1673             : 
    1674             :   /* If quic session connected fails, immediatly close connection */
    1675          18 :   app_wrk = app_worker_get (ctx->parent_app_wrk_id);
    1676          18 :   if ((rv = app_worker_init_connected (app_wrk, quic_session)))
    1677             :     {
    1678           0 :       QUIC_ERR ("failed to app_worker_init_connected");
    1679           0 :       quic_proto_on_close (ctx_id, thread_index);
    1680           0 :       app_worker_connect_notify (app_wrk, NULL, rv, ctx->client_opaque);
    1681           0 :       return;
    1682             :     }
    1683             : 
    1684          18 :   quic_session->session_state = SESSION_STATE_CONNECTING;
    1685          18 :   if ((rv = app_worker_connect_notify (app_wrk, quic_session,
    1686             :                                        SESSION_E_NONE, ctx->client_opaque)))
    1687             :     {
    1688           0 :       QUIC_ERR ("failed to notify app %d", rv);
    1689           0 :       quic_proto_on_close (ctx_id, thread_index);
    1690           0 :       return;
    1691             :     }
    1692             : 
    1693             :   /*  If the app opens a stream in its callback it may invalidate ctx */
    1694          18 :   ctx = quic_ctx_get (ctx_id, thread_index);
    1695             :   /*
    1696             :    * app_worker_connect_notify() might have reallocated pool, reload
    1697             :    * quic_session pointer
    1698             :    */
    1699          18 :   quic_session = session_get (ctx->c_s_index, thread_index);
    1700          18 :   quic_session->session_state = SESSION_STATE_LISTENING;
    1701             : }
    1702             : 
    1703             : static void
    1704       49603 : quic_check_quic_session_connected (quic_ctx_t * ctx)
    1705             : {
    1706             :   /* Called when we need to trigger quic session connected
    1707             :    * we may call this function on the server side / at
    1708             :    * stream opening */
    1709             : 
    1710             :   /* Conn may be set to null if the connection is terminated */
    1711       49603 :   if (!ctx->conn || ctx->conn_state != QUIC_CONN_STATE_HANDSHAKE)
    1712       49548 :     return;
    1713          55 :   if (!quicly_connection_is_ready (ctx->conn))
    1714          37 :     return;
    1715          18 :   ctx->conn_state = QUIC_CONN_STATE_READY;
    1716          18 :   if (!quicly_is_client (ctx->conn))
    1717           0 :     return;
    1718          18 :   quic_on_quic_session_connected (ctx);
    1719             : }
    1720             : 
    1721             : static inline void
    1722           0 : quic_update_conn_ctx (quicly_conn_t * conn, quicly_context_t * quicly_context)
    1723             : {
    1724             :   /* we need to update the quicly_conn on migrate
    1725             :    * as it contains a pointer to the crypto context */
    1726             :   ptls_context_t **tls;
    1727             :   quicly_context_t **_quicly_context;
    1728           0 :   _quicly_context = (quicly_context_t **) conn;
    1729           0 :   *_quicly_context = quicly_context;
    1730           0 :   tls = (ptls_context_t **) quicly_get_tls (conn);
    1731           0 :   *tls = quicly_context->tls;
    1732           0 : }
    1733             : 
    1734             : static void
    1735           0 : quic_receive_connection (void *arg)
    1736             : {
    1737           0 :   u32 new_ctx_id, thread_index = vlib_get_thread_index ();
    1738             :   quic_ctx_t *temp_ctx, *new_ctx;
    1739             :   clib_bihash_kv_16_8_t kv;
    1740             :   quicly_conn_t *conn;
    1741             :   quicly_context_t *quicly_context;
    1742             :   session_t *udp_session;
    1743             : 
    1744           0 :   temp_ctx = arg;
    1745           0 :   new_ctx_id = quic_ctx_alloc (thread_index);
    1746           0 :   new_ctx = quic_ctx_get (new_ctx_id, thread_index);
    1747             : 
    1748             :   QUIC_DBG (2, "Received conn %u (now %u)", temp_ctx->c_thread_index,
    1749             :             new_ctx_id);
    1750             : 
    1751           0 :   clib_memcpy (new_ctx, temp_ctx, sizeof (quic_ctx_t));
    1752           0 :   clib_mem_free (temp_ctx);
    1753             : 
    1754           0 :   new_ctx->c_thread_index = thread_index;
    1755           0 :   new_ctx->c_c_index = new_ctx_id;
    1756           0 :   quic_acquire_crypto_context (new_ctx);
    1757             : 
    1758           0 :   conn = new_ctx->conn;
    1759           0 :   quicly_context = quic_get_quicly_ctx_from_ctx (new_ctx);
    1760           0 :   quic_update_conn_ctx (conn, quicly_context);
    1761             : 
    1762           0 :   quic_store_conn_ctx (conn, new_ctx);
    1763           0 :   quic_make_connection_key (&kv, quicly_get_master_id (conn));
    1764           0 :   kv.value = ((u64) thread_index) << 32 | (u64) new_ctx_id;
    1765             :   QUIC_DBG (2, "Registering conn with id %lu %lu", kv.key[0], kv.key[1]);
    1766           0 :   clib_bihash_add_del_16_8 (&quic_main.connection_hash, &kv, 1 /* is_add */ );
    1767           0 :   new_ctx->timer_handle = QUIC_TIMER_HANDLE_INVALID;
    1768           0 :   quic_update_timer (new_ctx);
    1769             : 
    1770             :   /*  Trigger write on this connection if necessary */
    1771           0 :   udp_session = session_get_from_handle (new_ctx->udp_session_handle);
    1772           0 :   udp_session->opaque = new_ctx_id;
    1773           0 :   udp_session->flags &= ~SESSION_F_IS_MIGRATING;
    1774           0 :   if (svm_fifo_max_dequeue (udp_session->tx_fifo))
    1775           0 :     quic_set_udp_tx_evt (udp_session);
    1776           0 : }
    1777             : 
    1778             : static void
    1779           0 : quic_transfer_connection (u32 ctx_index, u32 dest_thread)
    1780             : {
    1781             :   quic_ctx_t *ctx, *temp_ctx;
    1782           0 :   u32 thread_index = vlib_get_thread_index ();
    1783             : 
    1784             :   QUIC_DBG (2, "Transferring conn %u to thread %u", ctx_index, dest_thread);
    1785             : 
    1786           0 :   temp_ctx = clib_mem_alloc (sizeof (quic_ctx_t));
    1787           0 :   QUIC_ASSERT (temp_ctx != NULL);
    1788           0 :   ctx = quic_ctx_get (ctx_index, thread_index);
    1789             : 
    1790           0 :   clib_memcpy (temp_ctx, ctx, sizeof (quic_ctx_t));
    1791             : 
    1792           0 :   quic_stop_ctx_timer (ctx);
    1793           0 :   quic_release_crypto_context (ctx->crypto_context_index, thread_index);
    1794           0 :   quic_ctx_free (ctx);
    1795             : 
    1796             :   /*  Send connection to destination thread */
    1797           0 :   session_send_rpc_evt_to_thread (dest_thread, quic_receive_connection,
    1798             :                                   (void *) temp_ctx);
    1799           0 : }
    1800             : 
    1801             : static int
    1802          18 : quic_udp_session_connected_callback (u32 quic_app_index, u32 ctx_index,
    1803             :                                      session_t * udp_session,
    1804             :                                      session_error_t err)
    1805             : {
    1806             :   QUIC_DBG (2, "UDP Session is now connected (id %u)",
    1807             :             udp_session->session_index);
    1808             :   /* This should always be called before quic_connect returns since UDP always
    1809             :    * connects instantly. */
    1810             :   clib_bihash_kv_16_8_t kv;
    1811             :   struct sockaddr_in6 sa6;
    1812          18 :   struct sockaddr *sa = (struct sockaddr *) &sa6;
    1813             :   socklen_t salen;
    1814             :   transport_connection_t *tc;
    1815             :   app_worker_t *app_wrk;
    1816             :   quicly_conn_t *conn;
    1817             :   quic_ctx_t *ctx;
    1818             :   u32 thread_index;
    1819             :   int ret;
    1820             :   quicly_context_t *quicly_ctx;
    1821             : 
    1822             :   /* Allocate session on whatever thread udp used, i.e., probably first
    1823             :    * worker, although this may be main thread. If it is main, it's done
    1824             :    * with a worker barrier */
    1825          18 :   thread_index = udp_session->thread_index;
    1826          18 :   ASSERT (thread_index == 0 || thread_index == 1);
    1827          18 :   ctx = quic_ctx_get (ctx_index, thread_index);
    1828          18 :   if (err)
    1829             :     {
    1830             :       u32 api_context;
    1831           0 :       app_wrk = app_worker_get_if_valid (ctx->parent_app_wrk_id);
    1832           0 :       if (app_wrk)
    1833             :         {
    1834           0 :           api_context = ctx->c_s_index;
    1835           0 :           app_worker_connect_notify (app_wrk, 0, SESSION_E_NONE, api_context);
    1836             :         }
    1837           0 :       return 0;
    1838             :     }
    1839             : 
    1840             :   QUIC_DBG (2, "New ctx [%u]%x", thread_index, (ctx) ? ctx_index : ~0);
    1841             : 
    1842          18 :   ctx->udp_session_handle = session_handle (udp_session);
    1843          18 :   udp_session->opaque = ctx_index;
    1844             : 
    1845             :   /* Init QUIC lib connection
    1846             :    * Generate required sockaddr & salen */
    1847          18 :   tc = session_get_transport (udp_session);
    1848          18 :   quic_build_sockaddr (sa, &salen, &tc->rmt_ip, tc->rmt_port, tc->is_ip4);
    1849             : 
    1850          18 :   quicly_ctx = quic_get_quicly_ctx_from_ctx (ctx);
    1851          18 :   ret = quicly_connect (&ctx->conn, quicly_ctx, (char *) ctx->srv_hostname,
    1852          18 :                         sa, NULL, &quic_main.wrk_ctx[thread_index].next_cid,
    1853             :                         ptls_iovec_init (NULL, 0), &quic_main.hs_properties,
    1854             :                         NULL);
    1855          18 :   ++quic_main.wrk_ctx[thread_index].next_cid.master_id;
    1856             :   /*  Save context handle in quicly connection */
    1857          18 :   quic_store_conn_ctx (ctx->conn, ctx);
    1858          18 :   assert (ret == 0);
    1859             : 
    1860             :   /*  Register connection in connections map */
    1861          18 :   conn = ctx->conn;
    1862          18 :   quic_make_connection_key (&kv, quicly_get_master_id (conn));
    1863          18 :   kv.value = ((u64) thread_index) << 32 | (u64) ctx_index;
    1864             :   QUIC_DBG (2, "Registering conn with id %lu %lu", kv.key[0], kv.key[1]);
    1865          18 :   clib_bihash_add_del_16_8 (&quic_main.connection_hash, &kv, 1 /* is_add */ );
    1866             : 
    1867          18 :   quic_send_packets (ctx);
    1868             : 
    1869          18 :   return ret;
    1870             : }
    1871             : 
    1872             : static void
    1873           0 : quic_udp_session_disconnect_callback (session_t * s)
    1874             : {
    1875           0 :   clib_warning ("UDP session disconnected???");
    1876           0 : }
    1877             : 
    1878             : static void
    1879          72 : quic_udp_session_cleanup_callback (session_t * udp_session,
    1880             :                                    session_cleanup_ntf_t ntf)
    1881             : {
    1882             :   quic_ctx_t *ctx;
    1883             : 
    1884          72 :   if (ntf != SESSION_CLEANUP_SESSION)
    1885          36 :     return;
    1886             : 
    1887          36 :   ctx = quic_ctx_get (udp_session->opaque, udp_session->thread_index);
    1888          36 :   quic_stop_ctx_timer (ctx);
    1889          36 :   quic_release_crypto_context (ctx->crypto_context_index,
    1890          36 :                                ctx->c_thread_index);
    1891          36 :   quic_ctx_free (ctx);
    1892             : }
    1893             : 
    1894             : static void
    1895           0 : quic_udp_session_reset_callback (session_t * s)
    1896             : {
    1897           0 :   clib_warning ("UDP session reset???");
    1898           0 : }
    1899             : 
    1900             : static void
    1901           0 : quic_udp_session_migrate_callback (session_t * s, session_handle_t new_sh)
    1902             : {
    1903           0 :   u32 new_thread = session_thread_from_handle (new_sh);
    1904             :   quic_ctx_t *ctx;
    1905             : 
    1906             :   QUIC_DBG (2, "Session %x migrated to %lx", s->session_index, new_sh);
    1907           0 :   QUIC_ASSERT (vlib_get_thread_index () == s->thread_index);
    1908           0 :   ctx = quic_ctx_get (s->opaque, s->thread_index);
    1909           0 :   QUIC_ASSERT (ctx->udp_session_handle == session_handle (s));
    1910             : 
    1911           0 :   ctx->udp_session_handle = new_sh;
    1912             : #if QUIC_DEBUG >= 1
    1913             :   s->opaque = 0xfeedface;
    1914             : #endif
    1915           0 :   quic_transfer_connection (ctx->c_c_index, new_thread);
    1916           0 : }
    1917             : 
    1918             : int
    1919          18 : quic_udp_session_accepted_callback (session_t * udp_session)
    1920             : {
    1921             :   /* New UDP connection, try to accept it */
    1922             :   u32 ctx_index;
    1923             :   quic_ctx_t *ctx, *lctx;
    1924             :   session_t *udp_listen_session;
    1925          18 :   u32 thread_index = vlib_get_thread_index ();
    1926             : 
    1927             :   udp_listen_session =
    1928          18 :     listen_session_get_from_handle (udp_session->listener_handle);
    1929             : 
    1930          18 :   ctx_index = quic_ctx_alloc (thread_index);
    1931          18 :   ctx = quic_ctx_get (ctx_index, thread_index);
    1932          18 :   ctx->c_thread_index = udp_session->thread_index;
    1933          18 :   ctx->c_c_index = ctx_index;
    1934          18 :   ctx->c_s_index = QUIC_SESSION_INVALID;
    1935          18 :   ctx->udp_session_handle = session_handle (udp_session);
    1936             :   QUIC_DBG (2, "ACCEPTED UDP 0x%lx", ctx->udp_session_handle);
    1937          18 :   ctx->listener_ctx_id = udp_listen_session->opaque;
    1938          18 :   lctx = quic_ctx_get (udp_listen_session->opaque,
    1939          18 :                        udp_listen_session->thread_index);
    1940          18 :   ctx->udp_is_ip4 = lctx->c_is_ip4;
    1941          18 :   ctx->parent_app_id = lctx->parent_app_id;
    1942          18 :   ctx->parent_app_wrk_id = lctx->parent_app_wrk_id;
    1943          18 :   ctx->timer_handle = QUIC_TIMER_HANDLE_INVALID;
    1944          18 :   ctx->conn_state = QUIC_CONN_STATE_OPENED;
    1945          18 :   ctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
    1946             : 
    1947          18 :   ctx->crypto_engine = lctx->crypto_engine;
    1948          18 :   ctx->ckpair_index = lctx->ckpair_index;
    1949          18 :   quic_acquire_crypto_context (ctx);
    1950          18 :   udp_session->opaque = ctx_index;
    1951          18 :   udp_session->session_state = SESSION_STATE_READY;
    1952             : 
    1953             :   /* TODO timeout to delete these if they never connect */
    1954          18 :   return 0;
    1955             : }
    1956             : 
    1957             : static int
    1958           5 : quic_add_segment_callback (u32 client_index, u64 seg_handle)
    1959             : {
    1960             :   /* No-op for builtin */
    1961           5 :   return 0;
    1962             : }
    1963             : 
    1964             : static int
    1965           0 : quic_del_segment_callback (u32 client_index, u64 seg_handle)
    1966             : {
    1967             :   /* No-op for builtin */
    1968           0 :   return 0;
    1969             : }
    1970             : 
    1971             : static int
    1972         579 : quic_custom_app_rx_callback (transport_connection_t * tc)
    1973             : {
    1974             :   quic_ctx_t *ctx;
    1975         579 :   session_t *stream_session = session_get (tc->s_index, tc->thread_index);
    1976             :   QUIC_DBG (3, "Received app READ notification");
    1977         579 :   quic_ack_rx_data (stream_session);
    1978         579 :   svm_fifo_reset_has_deq_ntf (stream_session->rx_fifo);
    1979             : 
    1980             :   /* Need to send packets (acks may never be sent otherwise) */
    1981         579 :   ctx = quic_ctx_get (stream_session->connection_index,
    1982         579 :                       stream_session->thread_index);
    1983         579 :   quic_send_packets (ctx);
    1984         579 :   return 0;
    1985             : }
    1986             : 
    1987             : static int
    1988       66502 : quic_custom_tx_callback (void *s, transport_send_params_t * sp)
    1989             : {
    1990       66502 :   session_t *stream_session = (session_t *) s;
    1991             :   quic_stream_data_t *stream_data;
    1992             :   quicly_stream_t *stream;
    1993             :   quic_ctx_t *ctx;
    1994             :   u32 max_deq;
    1995             :   int rv;
    1996             : 
    1997       66502 :   if (PREDICT_FALSE
    1998             :       (stream_session->session_state >= SESSION_STATE_TRANSPORT_CLOSING))
    1999           6 :     return 0;
    2000       66496 :   ctx = quic_ctx_get (stream_session->connection_index,
    2001       66496 :                       stream_session->thread_index);
    2002       66496 :   if (PREDICT_FALSE (!quic_ctx_is_stream (ctx)))
    2003             :     {
    2004           0 :       goto tx_end;              /* Most probably a reschedule */
    2005             :     }
    2006             : 
    2007             :   QUIC_DBG (3, "Stream TX event");
    2008       66496 :   quic_ack_rx_data (stream_session);
    2009       66496 :   stream = ctx->stream;
    2010       66496 :   if (!quicly_sendstate_is_open (&stream->sendstate))
    2011             :     {
    2012           0 :       QUIC_ERR ("Warning: tried to send on closed stream");
    2013           0 :       return 0;
    2014             :     }
    2015             : 
    2016       66496 :   stream_data = (quic_stream_data_t *) stream->data;
    2017       66496 :   max_deq = svm_fifo_max_dequeue (stream_session->tx_fifo);
    2018       66496 :   QUIC_ASSERT (max_deq >= stream_data->app_tx_data_len);
    2019       66496 :   if (max_deq == stream_data->app_tx_data_len)
    2020             :     {
    2021             :       QUIC_DBG (3, "TX but no data %d / %d", max_deq,
    2022             :                 stream_data->app_tx_data_len);
    2023       62282 :       return 0;
    2024             :     }
    2025        4214 :   stream_data->app_tx_data_len = max_deq;
    2026        4214 :   rv = quicly_stream_sync_sendbuf (stream, 1);
    2027        4214 :   QUIC_ASSERT (!rv);
    2028             : 
    2029        4214 : tx_end:
    2030        4214 :   return quic_send_packets (ctx);
    2031             : }
    2032             : 
    2033             : /*
    2034             :  * Returns 0 if a matching connection is found and is on the right thread.
    2035             :  * Otherwise returns -1.
    2036             :  * If a connection is found, even on the wrong thread, ctx_thread and ctx_index
    2037             :  * will be set.
    2038             :  */
    2039             : static inline int
    2040       49603 : quic_find_packet_ctx (quic_rx_packet_ctx_t * pctx, u32 caller_thread_index)
    2041             : {
    2042             :   clib_bihash_kv_16_8_t kv;
    2043             :   clib_bihash_16_8_t *h;
    2044             :   quic_ctx_t *ctx;
    2045             :   u32 index, thread_id;
    2046             : 
    2047       49603 :   h = &quic_main.connection_hash;
    2048       49603 :   quic_make_connection_key (&kv, &pctx->packet.cid.dest.plaintext);
    2049             :   QUIC_DBG (3, "Searching conn with id %lu %lu", kv.key[0], kv.key[1]);
    2050             : 
    2051       49603 :   if (clib_bihash_search_16_8 (h, &kv, &kv))
    2052             :     {
    2053             :       QUIC_DBG (3, "connection not found");
    2054          18 :       return QUIC_PACKET_TYPE_NONE;
    2055             :     }
    2056             : 
    2057       49585 :   index = kv.value & UINT32_MAX;
    2058       49585 :   thread_id = kv.value >> 32;
    2059             :   /* Check if this connection belongs to this thread, otherwise
    2060             :    * ask for it to be moved */
    2061       49585 :   if (thread_id != caller_thread_index)
    2062             :     {
    2063             :       QUIC_DBG (2, "Connection is on wrong thread");
    2064             :       /* Cannot make full check with quicly_is_destination... */
    2065           0 :       pctx->ctx_index = index;
    2066           0 :       pctx->thread_index = thread_id;
    2067           0 :       return QUIC_PACKET_TYPE_MIGRATE;
    2068             :     }
    2069       49585 :   ctx = quic_ctx_get (index, vlib_get_thread_index ());
    2070       49585 :   if (!ctx->conn)
    2071             :     {
    2072           0 :       QUIC_ERR ("ctx has no conn");
    2073           0 :       return QUIC_PACKET_TYPE_NONE;
    2074             :     }
    2075       49585 :   if (!quicly_is_destination (ctx->conn, NULL, &pctx->sa, &pctx->packet))
    2076           0 :     return QUIC_PACKET_TYPE_NONE;
    2077             : 
    2078             :   QUIC_DBG (3, "Connection found");
    2079       49585 :   pctx->ctx_index = index;
    2080       49585 :   pctx->thread_index = thread_id;
    2081       49585 :   return QUIC_PACKET_TYPE_RECEIVE;
    2082             : }
    2083             : 
    2084             : static void
    2085          18 : quic_accept_connection (quic_rx_packet_ctx_t * pctx)
    2086             : {
    2087             :   quicly_context_t *quicly_ctx;
    2088             :   session_t *quic_session;
    2089             :   clib_bihash_kv_16_8_t kv;
    2090             :   app_worker_t *app_wrk;
    2091             :   quicly_conn_t *conn;
    2092             :   quic_ctx_t *ctx;
    2093             :   quic_ctx_t *lctx;
    2094             :   int rv;
    2095             : 
    2096             :   /* new connection, accept and create context if packet is valid
    2097             :    * TODO: check if socket is actually listening? */
    2098          18 :   ctx = quic_ctx_get (pctx->ctx_index, pctx->thread_index);
    2099          18 :   if (ctx->c_s_index != QUIC_SESSION_INVALID)
    2100             :     {
    2101             :       QUIC_DBG (2, "already accepted ctx 0x%x", ctx->c_s_index);
    2102           0 :       return;
    2103             :     }
    2104             : 
    2105          18 :   quicly_ctx = quic_get_quicly_ctx_from_ctx (ctx);
    2106          18 :   if ((rv = quicly_accept (&conn, quicly_ctx, NULL, &pctx->sa,
    2107             :                            &pctx->packet, NULL,
    2108          18 :                            &quic_main.wrk_ctx[pctx->thread_index].next_cid,
    2109             :                            NULL)))
    2110             :     {
    2111             :       /* Invalid packet, pass */
    2112           0 :       assert (conn == NULL);
    2113           0 :       QUIC_ERR ("Accept failed with %U", quic_format_err, rv);
    2114             :       /* TODO: cleanup created quic ctx and UDP session */
    2115           0 :       return;
    2116             :     }
    2117          18 :   assert (conn != NULL);
    2118             : 
    2119          18 :   ++quic_main.wrk_ctx[pctx->thread_index].next_cid.master_id;
    2120             :   /* Save ctx handle in quicly connection */
    2121          18 :   quic_store_conn_ctx (conn, ctx);
    2122          18 :   ctx->conn = conn;
    2123             : 
    2124          18 :   quic_session = session_alloc (ctx->c_thread_index);
    2125             :   QUIC_DBG (2, "Allocated quic_session, 0x%lx ctx %u",
    2126             :             session_handle (quic_session), ctx->c_c_index);
    2127          18 :   quic_session->session_state = SESSION_STATE_LISTENING;
    2128          18 :   ctx->c_s_index = quic_session->session_index;
    2129             : 
    2130          18 :   lctx = quic_ctx_get (ctx->listener_ctx_id, 0);
    2131             : 
    2132          18 :   quic_session->app_wrk_index = lctx->parent_app_wrk_id;
    2133          18 :   quic_session->connection_index = ctx->c_c_index;
    2134          18 :   quic_session->session_type =
    2135          18 :     session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC, ctx->udp_is_ip4);
    2136          18 :   quic_session->listener_handle = lctx->c_s_index;
    2137             : 
    2138             :   /* Register connection in connections map */
    2139          18 :   quic_make_connection_key (&kv, quicly_get_master_id (conn));
    2140          18 :   kv.value = ((u64) pctx->thread_index) << 32 | (u64) pctx->ctx_index;
    2141          18 :   clib_bihash_add_del_16_8 (&quic_main.connection_hash, &kv, 1 /* is_add */ );
    2142             :   QUIC_DBG (2, "Registering conn with id %lu %lu", kv.key[0], kv.key[1]);
    2143             : 
    2144             :   /* If notify fails, reset connection immediatly */
    2145          18 :   if ((rv = app_worker_init_accepted (quic_session)))
    2146             :     {
    2147           0 :       QUIC_ERR ("failed to allocate fifos");
    2148           0 :       quic_proto_on_close (pctx->ctx_index, pctx->thread_index);
    2149           0 :       return;
    2150             :     }
    2151             : 
    2152          18 :   app_wrk = app_worker_get (quic_session->app_wrk_index);
    2153          18 :   if ((rv = app_worker_accept_notify (app_wrk, quic_session)))
    2154             :     {
    2155           0 :       QUIC_ERR ("failed to notify accept worker app");
    2156           0 :       quic_proto_on_close (pctx->ctx_index, pctx->thread_index);
    2157           0 :       return;
    2158             :     }
    2159             : 
    2160          18 :   ctx->conn_state = QUIC_CONN_STATE_READY;
    2161             : }
    2162             : 
    2163             : static int
    2164           0 : quic_reset_connection (u64 udp_session_handle, quic_rx_packet_ctx_t * pctx)
    2165           0 : {
    2166             :   /* short header packet; potentially a dead connection. No need to check the
    2167             :    * length of the incoming packet, because loop is prevented by authenticating
    2168             :    * the CID (by checking node_id and thread_id). If the peer is also sending a
    2169             :    * reset, then the next CID is highly likely to contain a non-authenticating
    2170             :    * CID, ... */
    2171             :   QUIC_DBG (2, "Sending stateless reset");
    2172             :   int rv;
    2173             :   session_t *udp_session;
    2174             :   quicly_context_t *quicly_ctx;
    2175           0 :   if (pctx->packet.cid.dest.plaintext.node_id != 0
    2176           0 :       || pctx->packet.cid.dest.plaintext.thread_id != 0)
    2177           0 :     return 0;
    2178           0 :   quicly_ctx = quic_get_quicly_ctx_from_udp (udp_session_handle);
    2179           0 :   quic_ctx_t *qctx = quic_ctx_get (pctx->ctx_index, pctx->thread_index);
    2180             : 
    2181             :   quicly_address_t src;
    2182           0 :   uint8_t payload[quicly_ctx->transport_params.max_udp_payload_size];
    2183             :   size_t payload_len =
    2184           0 :     quicly_send_stateless_reset (quicly_ctx, &src.sa, payload);
    2185           0 :   if (payload_len == 0)
    2186           0 :     return 1;
    2187             : 
    2188             :   struct iovec packet;
    2189           0 :   packet.iov_len = payload_len;
    2190           0 :   packet.iov_base = payload;
    2191             : 
    2192           0 :   struct _st_quicly_conn_public_t *conn =
    2193             :     (struct _st_quicly_conn_public_t *) qctx->conn;
    2194             : 
    2195           0 :   udp_session = session_get_from_handle (udp_session_handle);
    2196           0 :   rv = quic_send_datagram (udp_session, &packet, &conn->remote.address,
    2197             :                            &conn->local.address);
    2198           0 :   quic_set_udp_tx_evt (udp_session);
    2199           0 :   return rv;
    2200             : }
    2201             : 
    2202             : static int
    2203       49603 : quic_process_one_rx_packet (u64 udp_session_handle, svm_fifo_t * f,
    2204             :                             u32 fifo_offset, quic_rx_packet_ctx_t * pctx)
    2205             : {
    2206             :   size_t plen;
    2207             :   u32 full_len, ret;
    2208       49603 :   u32 thread_index = vlib_get_thread_index ();
    2209       49603 :   u32 cur_deq = svm_fifo_max_dequeue (f) - fifo_offset;
    2210             :   quicly_context_t *quicly_ctx;
    2211             :   session_t *udp_session;
    2212             :   int rv;
    2213             : 
    2214       99206 :   ret = svm_fifo_peek (f, fifo_offset,
    2215       49603 :                        SESSION_CONN_HDR_LEN, (u8 *) & pctx->ph);
    2216       49603 :   QUIC_ASSERT (ret == SESSION_CONN_HDR_LEN);
    2217       49603 :   QUIC_ASSERT (pctx->ph.data_offset == 0);
    2218       49603 :   full_len = pctx->ph.data_length + SESSION_CONN_HDR_LEN;
    2219       49603 :   if (full_len > cur_deq)
    2220             :     {
    2221           0 :       QUIC_ERR ("Not enough data in fifo RX");
    2222           0 :       return 1;
    2223             :     }
    2224             : 
    2225             :   /* Quicly can read len bytes from the fifo at offset:
    2226             :    * ph.data_offset + SESSION_CONN_HDR_LEN */
    2227       99206 :   ret = svm_fifo_peek (f, SESSION_CONN_HDR_LEN + fifo_offset,
    2228       49603 :                        pctx->ph.data_length, pctx->data);
    2229       49603 :   if (ret != pctx->ph.data_length)
    2230             :     {
    2231           0 :       QUIC_ERR ("Not enough data peeked in RX");
    2232           0 :       return 1;
    2233             :     }
    2234             : 
    2235       49603 :   quic_increment_counter (QUIC_ERROR_RX_PACKETS, 1);
    2236       49603 :   quic_build_sockaddr (&pctx->sa, &pctx->salen, &pctx->ph.rmt_ip,
    2237       49603 :                        pctx->ph.rmt_port, pctx->ph.is_ip4);
    2238       49603 :   quicly_ctx = quic_get_quicly_ctx_from_udp (udp_session_handle);
    2239             : 
    2240       49603 :   size_t off = 0;
    2241       49603 :   plen = quicly_decode_packet (quicly_ctx, &pctx->packet, pctx->data,
    2242       49603 :                                pctx->ph.data_length, &off);
    2243             : 
    2244       49603 :   if (plen == SIZE_MAX)
    2245             :     {
    2246           0 :       return 1;
    2247             :     }
    2248             : 
    2249       49603 :   rv = quic_find_packet_ctx (pctx, thread_index);
    2250       49603 :   if (rv == QUIC_PACKET_TYPE_RECEIVE)
    2251             :     {
    2252       49585 :       pctx->ptype = QUIC_PACKET_TYPE_RECEIVE;
    2253             : 
    2254       49585 :       if (quic_main.vnet_crypto_enabled &&
    2255       49585 :           quic_main.default_crypto_engine == CRYPTO_ENGINE_VPP)
    2256             :         {
    2257       49585 :           quic_ctx_t *qctx = quic_ctx_get (pctx->ctx_index, thread_index);
    2258       49585 :           quic_crypto_decrypt_packet (qctx, pctx);
    2259             :         }
    2260       49585 :       return 0;
    2261             :     }
    2262          18 :   else if (rv == QUIC_PACKET_TYPE_MIGRATE)
    2263             :     {
    2264           0 :       pctx->ptype = QUIC_PACKET_TYPE_MIGRATE;
    2265             :       /*  Connection found but on wrong thread, ask move */
    2266             :     }
    2267          18 :   else if (QUICLY_PACKET_IS_LONG_HEADER (pctx->packet.octets.base[0]))
    2268             :     {
    2269          18 :       pctx->ptype = QUIC_PACKET_TYPE_ACCEPT;
    2270          18 :       udp_session = session_get_from_handle (udp_session_handle);
    2271          18 :       pctx->ctx_index = udp_session->opaque;
    2272          18 :       pctx->thread_index = thread_index;
    2273             :     }
    2274             :   else
    2275             :     {
    2276           0 :       pctx->ptype = QUIC_PACKET_TYPE_RESET;
    2277             :     }
    2278          18 :   return 1;
    2279             : }
    2280             : 
    2281             : static int
    2282        7758 : quic_udp_session_rx_callback (session_t * udp_session)
    2283             : {
    2284             :   /*  Read data from UDP rx_fifo and pass it to the quicly conn. */
    2285        7758 :   quic_ctx_t *ctx = NULL, *prev_ctx = NULL;
    2286        7758 :   svm_fifo_t *f = udp_session->rx_fifo;
    2287             :   u32 max_deq;
    2288        7758 :   u64 udp_session_handle = session_handle (udp_session);
    2289        7758 :   int rv = 0;
    2290        7758 :   u32 thread_index = vlib_get_thread_index ();
    2291             :   u32 cur_deq, fifo_offset, max_packets, i;
    2292             : 
    2293             :   quic_rx_packet_ctx_t packets_ctx[QUIC_RCV_MAX_PACKETS];
    2294             : 
    2295        7758 :   if (udp_session->flags & SESSION_F_IS_MIGRATING)
    2296             :     {
    2297             :       QUIC_DBG (3, "RX on migrating udp session");
    2298           0 :       return 0;
    2299             :     }
    2300             : 
    2301        7758 : rx_start:
    2302        8182 :   max_deq = svm_fifo_max_dequeue (f);
    2303        8182 :   if (max_deq == 0)
    2304           0 :     return 0;
    2305             : 
    2306        8182 :   fifo_offset = 0;
    2307        8182 :   max_packets = QUIC_RCV_MAX_PACKETS;
    2308             : 
    2309             : #if CLIB_DEBUG > 0
    2310        8182 :   clib_memset (packets_ctx, 0xfa,
    2311             :                QUIC_RCV_MAX_PACKETS * sizeof (quic_rx_packet_ctx_t));
    2312             : #endif
    2313       57767 :   for (i = 0; i < max_packets; i++)
    2314             :     {
    2315       57246 :       packets_ctx[i].thread_index = UINT32_MAX;
    2316       57246 :       packets_ctx[i].ctx_index = UINT32_MAX;
    2317       57246 :       packets_ctx[i].ptype = QUIC_PACKET_TYPE_DROP;
    2318             : 
    2319       57246 :       cur_deq = max_deq - fifo_offset;
    2320       57246 :       if (cur_deq == 0)
    2321             :         {
    2322        7643 :           max_packets = i + 1;
    2323        7643 :           break;
    2324             :         }
    2325       49603 :       if (cur_deq < SESSION_CONN_HDR_LEN)
    2326             :         {
    2327           0 :           fifo_offset = max_deq;
    2328           0 :           max_packets = i + 1;
    2329           0 :           QUIC_ERR ("Fifo %d < header size in RX", cur_deq);
    2330           0 :           break;
    2331             :         }
    2332       49603 :       rv = quic_process_one_rx_packet (udp_session_handle, f,
    2333             :                                        fifo_offset, &packets_ctx[i]);
    2334       49603 :       if (packets_ctx[i].ptype != QUIC_PACKET_TYPE_MIGRATE)
    2335       49603 :         fifo_offset += SESSION_CONN_HDR_LEN + packets_ctx[i].ph.data_length;
    2336       49603 :       if (rv)
    2337             :         {
    2338          18 :           max_packets = i + 1;
    2339          18 :           break;
    2340             :         }
    2341             :     }
    2342             : 
    2343       65428 :   for (i = 0; i < max_packets; i++)
    2344             :     {
    2345       57246 :       switch (packets_ctx[i].ptype)
    2346             :         {
    2347       49585 :         case QUIC_PACKET_TYPE_RECEIVE:
    2348       49585 :           ctx = quic_ctx_get (packets_ctx[i].ctx_index, thread_index);
    2349       49585 :           rv = quicly_receive (ctx->conn, NULL, &packets_ctx[i].sa,
    2350             :                                &packets_ctx[i].packet);
    2351       49585 :           if (rv && rv != QUICLY_ERROR_PACKET_IGNORED)
    2352             :             {
    2353           0 :               QUIC_ERR ("quicly_receive return error %U",
    2354             :                         quic_format_err, rv);
    2355             :             }
    2356       49585 :           break;
    2357          18 :         case QUIC_PACKET_TYPE_ACCEPT:
    2358          18 :           quic_accept_connection (&packets_ctx[i]);
    2359          18 :           break;
    2360           0 :         case QUIC_PACKET_TYPE_RESET:
    2361           0 :           quic_reset_connection (udp_session_handle, &packets_ctx[i]);
    2362           0 :           break;
    2363             :         }
    2364       57246 :     }
    2365        8182 :   ctx = prev_ctx = NULL;
    2366       65428 :   for (i = 0; i < max_packets; i++)
    2367             :     {
    2368       57246 :       prev_ctx = ctx;
    2369       57246 :       switch (packets_ctx[i].ptype)
    2370             :         {
    2371       49585 :         case QUIC_PACKET_TYPE_RECEIVE:
    2372       49585 :           ctx = quic_ctx_get (packets_ctx[i].ctx_index,
    2373             :                               packets_ctx[i].thread_index);
    2374       49585 :           quic_check_quic_session_connected (ctx);
    2375       49585 :           ctx = quic_ctx_get (packets_ctx[i].ctx_index,
    2376             :                               packets_ctx[i].thread_index);
    2377       49585 :           break;
    2378          18 :         case QUIC_PACKET_TYPE_ACCEPT:
    2379          18 :           ctx = quic_ctx_get (packets_ctx[i].ctx_index,
    2380             :                               packets_ctx[i].thread_index);
    2381          18 :           break;
    2382        7643 :         default:
    2383        7643 :           continue;             /* this exits the for loop since other packet types are
    2384             :                                    necessarily the last in the batch */
    2385             :         }
    2386       49603 :       if (ctx != prev_ctx)
    2387        8182 :         quic_send_packets (ctx);
    2388             :     }
    2389             : 
    2390        8182 :   udp_session = session_get_from_handle (udp_session_handle);   /*  session alloc might have happened */
    2391        8182 :   f = udp_session->rx_fifo;
    2392        8182 :   svm_fifo_dequeue_drop (f, fifo_offset);
    2393             : 
    2394        8182 :   if (svm_fifo_max_dequeue (f))
    2395         424 :     goto rx_start;
    2396             : 
    2397        7758 :   return 0;
    2398             : }
    2399             : 
    2400             : always_inline void
    2401          12 : quic_common_get_transport_endpoint (quic_ctx_t * ctx,
    2402             :                                     transport_endpoint_t * tep, u8 is_lcl)
    2403             : {
    2404             :   session_t *udp_session;
    2405          12 :   if (!quic_ctx_is_stream (ctx))
    2406             :     {
    2407           6 :       udp_session = session_get_from_handle (ctx->udp_session_handle);
    2408           6 :       session_get_endpoint (udp_session, tep, is_lcl);
    2409             :     }
    2410          12 : }
    2411             : 
    2412             : static void
    2413           6 : quic_get_transport_listener_endpoint (u32 listener_index,
    2414             :                                       transport_endpoint_t * tep, u8 is_lcl)
    2415             : {
    2416             :   quic_ctx_t *ctx;
    2417             :   app_listener_t *app_listener;
    2418             :   session_t *udp_listen_session;
    2419           6 :   ctx = quic_ctx_get (listener_index, vlib_get_thread_index ());
    2420           6 :   if (quic_ctx_is_listener (ctx))
    2421             :     {
    2422           2 :       app_listener = app_listener_get_w_handle (ctx->udp_session_handle);
    2423           2 :       udp_listen_session = app_listener_get_session (app_listener);
    2424           2 :       return session_get_endpoint (udp_listen_session, tep, is_lcl);
    2425             :     }
    2426           4 :   quic_common_get_transport_endpoint (ctx, tep, is_lcl);
    2427             : }
    2428             : 
    2429             : static void
    2430           8 : quic_get_transport_endpoint (u32 ctx_index, u32 thread_index,
    2431             :                              transport_endpoint_t * tep, u8 is_lcl)
    2432             : {
    2433             :   quic_ctx_t *ctx;
    2434           8 :   ctx = quic_ctx_get (ctx_index, thread_index);
    2435           8 :   quic_common_get_transport_endpoint (ctx, tep, is_lcl);
    2436           8 : }
    2437             : 
    2438             : /* *INDENT-OFF* */
    2439             : static session_cb_vft_t quic_app_cb_vft = {
    2440             :   .session_accept_callback = quic_udp_session_accepted_callback,
    2441             :   .session_disconnect_callback = quic_udp_session_disconnect_callback,
    2442             :   .session_connected_callback = quic_udp_session_connected_callback,
    2443             :   .session_reset_callback = quic_udp_session_reset_callback,
    2444             :   .session_migrate_callback = quic_udp_session_migrate_callback,
    2445             :   .add_segment_callback = quic_add_segment_callback,
    2446             :   .del_segment_callback = quic_del_segment_callback,
    2447             :   .builtin_app_rx_callback = quic_udp_session_rx_callback,
    2448             :   .session_cleanup_callback = quic_udp_session_cleanup_callback,
    2449             :   .app_cert_key_pair_delete_callback = quic_app_cert_key_pair_delete_callback,
    2450             : };
    2451             : 
    2452             : static const transport_proto_vft_t quic_proto = {
    2453             :   .connect = quic_connect,
    2454             :   .close = quic_proto_on_close,
    2455             :   .start_listen = quic_start_listen,
    2456             :   .stop_listen = quic_stop_listen,
    2457             :   .get_connection = quic_connection_get,
    2458             :   .get_listener = quic_listener_get,
    2459             :   .update_time = quic_update_time,
    2460             :   .app_rx_evt = quic_custom_app_rx_callback,
    2461             :   .custom_tx = quic_custom_tx_callback,
    2462             :   .format_connection = format_quic_connection,
    2463             :   .format_half_open = format_quic_half_open,
    2464             :   .format_listener = format_quic_listener,
    2465             :   .get_transport_endpoint = quic_get_transport_endpoint,
    2466             :   .get_transport_listener_endpoint = quic_get_transport_listener_endpoint,
    2467             :   .transport_options = {
    2468             :     .name = "quic",
    2469             :     .short_name = "Q",
    2470             :     .tx_type = TRANSPORT_TX_INTERNAL,
    2471             :     .service_type = TRANSPORT_SERVICE_APP,
    2472             :   },
    2473             : };
    2474             : /* *INDENT-ON* */
    2475             : 
    2476             : static quicly_stream_open_t on_stream_open = { quic_on_stream_open };
    2477             : static quicly_closed_by_remote_t on_closed_by_remote = {
    2478             :   quic_on_closed_by_remote
    2479             : };
    2480             : static quicly_now_t quicly_vpp_now_cb = { quic_get_time };
    2481             : 
    2482             : static void
    2483          50 : quic_register_cipher_suite (crypto_engine_type_t type,
    2484             :                             ptls_cipher_suite_t ** ciphers)
    2485             : {
    2486          50 :   quic_main_t *qm = &quic_main;
    2487          50 :   vec_validate (qm->quic_ciphers, type);
    2488          50 :   clib_bitmap_set (qm->available_crypto_engines, type, 1);
    2489          50 :   qm->quic_ciphers[type] = ciphers;
    2490          50 : }
    2491             : 
    2492             : static void
    2493           2 : quic_update_fifo_size ()
    2494             : {
    2495           2 :   quic_main_t *qm = &quic_main;
    2496             :   segment_manager_props_t *seg_mgr_props =
    2497           2 :     application_get_segment_manager_properties (qm->app_index);
    2498             : 
    2499           2 :   if (!seg_mgr_props)
    2500             :     {
    2501           0 :       clib_warning
    2502             :         ("error while getting segment_manager_props_t, can't update fifo-size");
    2503           0 :       return;
    2504             :     }
    2505             : 
    2506           2 :   seg_mgr_props->tx_fifo_size = qm->udp_fifo_size;
    2507           2 :   seg_mgr_props->rx_fifo_size = qm->udp_fifo_size;
    2508             : }
    2509             : 
    2510             : static clib_error_t *
    2511          25 : quic_init (vlib_main_t * vm)
    2512             : {
    2513          25 :   u32 segment_size = 256 << 20;
    2514          25 :   vlib_thread_main_t *vtm = vlib_get_thread_main ();
    2515             :   tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
    2516          25 :   vnet_app_attach_args_t _a, *a = &_a;
    2517             :   u64 options[APP_OPTIONS_N_OPTIONS];
    2518          25 :   quic_main_t *qm = &quic_main;
    2519             :   u32 num_threads, i;
    2520             : 
    2521          25 :   num_threads = 1 /* main thread */  + vtm->n_threads;
    2522             : 
    2523          25 :   clib_memset (a, 0, sizeof (*a));
    2524          25 :   clib_memset (options, 0, sizeof (options));
    2525             : 
    2526          25 :   a->session_cb_vft = &quic_app_cb_vft;
    2527          25 :   a->api_client_index = APP_INVALID_INDEX;
    2528          25 :   a->options = options;
    2529          25 :   a->name = format (0, "quic");
    2530          25 :   a->options[APP_OPTIONS_SEGMENT_SIZE] = segment_size;
    2531          25 :   a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = segment_size;
    2532          25 :   a->options[APP_OPTIONS_RX_FIFO_SIZE] = qm->udp_fifo_size;
    2533          25 :   a->options[APP_OPTIONS_TX_FIFO_SIZE] = qm->udp_fifo_size;
    2534          25 :   a->options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] = qm->udp_fifo_prealloc;
    2535          25 :   a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
    2536          25 :   a->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
    2537          25 :   a->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_IS_TRANSPORT_APP;
    2538             : 
    2539          25 :   if (vnet_application_attach (a))
    2540             :     {
    2541           0 :       clib_warning ("failed to attach quic app");
    2542           0 :       return clib_error_return (0, "failed to attach quic app");
    2543             :     }
    2544             : 
    2545          25 :   vec_validate (qm->ctx_pool, num_threads - 1);
    2546          25 :   vec_validate (qm->wrk_ctx, num_threads - 1);
    2547             : 
    2548          71 :   for (i = 0; i < num_threads; i++)
    2549             :     {
    2550          46 :       qm->wrk_ctx[i].next_cid.thread_id = i;
    2551          46 :       tw = &qm->wrk_ctx[i].timer_wheel;
    2552          46 :       tw_timer_wheel_init_1t_3w_1024sl_ov (tw, quic_expired_timers_dispatch,
    2553             :                                            1e-3 /* timer period 1ms */ , ~0);
    2554          46 :       tw->last_run_time = vlib_time_now (vlib_get_main ());
    2555          46 :       clib_bihash_init_24_8 (&qm->wrk_ctx[i].crypto_context_hash,
    2556             :                              "quic crypto contexts", 64, 128 << 10);
    2557             :     }
    2558             : 
    2559          25 :   clib_bihash_init_16_8 (&qm->connection_hash, "quic connections", 1024,
    2560             :                          4 << 20);
    2561             : 
    2562          25 :   qm->app_index = a->app_index;
    2563          25 :   qm->tstamp_ticks_per_clock = vm->clib_time.seconds_per_clock
    2564          25 :     / QUIC_TSTAMP_RESOLUTION;
    2565          25 :   qm->session_cache.super.cb = quic_encrypt_ticket_cb;
    2566             : 
    2567          25 :   transport_register_protocol (TRANSPORT_PROTO_QUIC, &quic_proto,
    2568             :                                FIB_PROTOCOL_IP4, ~0);
    2569          25 :   transport_register_protocol (TRANSPORT_PROTO_QUIC, &quic_proto,
    2570             :                                FIB_PROTOCOL_IP6, ~0);
    2571             : 
    2572             :   quic_load_openssl3_legacy_provider ();
    2573          25 :   clib_bitmap_alloc (qm->available_crypto_engines,
    2574             :                      app_crypto_engine_n_types ());
    2575          25 :   quic_register_cipher_suite (CRYPTO_ENGINE_PICOTLS,
    2576             :                               ptls_openssl_cipher_suites);
    2577          25 :   qm->default_crypto_engine = CRYPTO_ENGINE_PICOTLS;
    2578             : 
    2579          25 :   vnet_crypto_main_t *cm = &crypto_main;
    2580          25 :   if (vec_len (cm->engines) == 0)
    2581           0 :     qm->vnet_crypto_enabled = 0;
    2582             :   else
    2583          25 :     qm->vnet_crypto_enabled = 1;
    2584          25 :   if (qm->vnet_crypto_enabled == 1)
    2585             :     {
    2586          25 :       u8 empty_key[32] = {};
    2587          25 :       quic_register_cipher_suite (CRYPTO_ENGINE_VPP,
    2588             :                                   quic_crypto_cipher_suites);
    2589          25 :       qm->default_crypto_engine = CRYPTO_ENGINE_VPP;
    2590          25 :       vec_validate (qm->per_thread_crypto_key_indices, num_threads);
    2591          71 :       for (i = 0; i < num_threads; i++)
    2592             :         {
    2593          46 :           qm->per_thread_crypto_key_indices[i] = vnet_crypto_key_add (
    2594             :             vm, VNET_CRYPTO_ALG_AES_256_CTR, empty_key, 32);
    2595             :         }
    2596             :     }
    2597             : 
    2598          25 :   qm->max_packets_per_key = DEFAULT_MAX_PACKETS_PER_KEY;
    2599          25 :   qm->default_quic_cc = QUIC_CC_RENO;
    2600             : 
    2601          25 :   vec_free (a->name);
    2602          25 :   return 0;
    2603             : }
    2604             : 
    2605          50 : VLIB_INIT_FUNCTION (quic_init);
    2606             : 
    2607             : static clib_error_t *
    2608           0 : quic_plugin_crypto_command_fn (vlib_main_t * vm,
    2609             :                                unformat_input_t * input,
    2610             :                                vlib_cli_command_t * cmd)
    2611             : {
    2612           0 :   unformat_input_t _line_input, *line_input = &_line_input;
    2613           0 :   quic_main_t *qm = &quic_main;
    2614           0 :   clib_error_t *e = 0;
    2615             : 
    2616           0 :   if (!unformat_user (input, unformat_line_input, line_input))
    2617           0 :     return 0;
    2618             : 
    2619           0 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    2620             :     {
    2621           0 :       if (unformat (line_input, "vpp"))
    2622           0 :         qm->default_crypto_engine = CRYPTO_ENGINE_VPP;
    2623           0 :       else if (unformat (line_input, "picotls"))
    2624           0 :         qm->default_crypto_engine = CRYPTO_ENGINE_PICOTLS;
    2625             :       else
    2626             :         {
    2627           0 :           e = clib_error_return (0, "unknown input '%U'",
    2628             :                                  format_unformat_error, line_input);
    2629           0 :           goto done;
    2630             :         }
    2631             :     }
    2632           0 : done:
    2633           0 :   unformat_free (line_input);
    2634           0 :   return e;
    2635             : }
    2636             : 
    2637             : u64 quic_fifosize = 0;
    2638             : static clib_error_t *
    2639           2 : quic_plugin_set_fifo_size_command_fn (vlib_main_t * vm,
    2640             :                                       unformat_input_t * input,
    2641             :                                       vlib_cli_command_t * cmd)
    2642             : {
    2643           2 :   quic_main_t *qm = &quic_main;
    2644           2 :   unformat_input_t _line_input, *line_input = &_line_input;
    2645             :   uword tmp;
    2646             : 
    2647           2 :   if (!unformat_user (input, unformat_line_input, line_input))
    2648           0 :     return 0;
    2649             : 
    2650           4 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    2651             :     {
    2652           2 :       if (unformat (line_input, "%U", unformat_memory_size, &tmp))
    2653             :         {
    2654           2 :           if (tmp >= 0x100000000ULL)
    2655             :             {
    2656           0 :               return clib_error_return
    2657             :                 (0, "fifo-size %llu (0x%llx) too large", tmp, tmp);
    2658             :             }
    2659           2 :           qm->udp_fifo_size = tmp;
    2660           2 :           quic_update_fifo_size ();
    2661             :         }
    2662             :       else
    2663           0 :         return clib_error_return (0, "unknown input '%U'",
    2664             :                                   format_unformat_error, line_input);
    2665             :     }
    2666             : 
    2667           2 :   return 0;
    2668             : }
    2669             : 
    2670             : static inline u64
    2671           0 : quic_get_counter_value (u32 event_code)
    2672             : {
    2673             :   vlib_node_t *n;
    2674             :   vlib_main_t *vm;
    2675             :   vlib_error_main_t *em;
    2676             : 
    2677             :   u32 code, i;
    2678           0 :   u64 c, sum = 0;
    2679             : 
    2680           0 :   vm = vlib_get_main ();
    2681           0 :   em = &vm->error_main;
    2682           0 :   n = vlib_get_node (vm, quic_input_node.index);
    2683           0 :   code = event_code;
    2684           0 :   foreach_vlib_main ()
    2685             :     {
    2686           0 :       em = &this_vlib_main->error_main;
    2687           0 :       i = n->error_heap_index + code;
    2688           0 :       c = em->counters[i];
    2689             : 
    2690           0 :       if (i < vec_len (em->counters_last_clear))
    2691           0 :         c -= em->counters_last_clear[i];
    2692           0 :       sum += c;
    2693             :     }
    2694           0 :   return sum;
    2695             : }
    2696             : 
    2697             : static void
    2698           0 : quic_show_aggregated_stats (vlib_main_t * vm)
    2699             : {
    2700           0 :   u32 num_workers = vlib_num_workers ();
    2701           0 :   quic_main_t *qm = &quic_main;
    2702           0 :   quic_ctx_t *ctx = NULL;
    2703             :   quicly_stats_t st, agg_stats;
    2704           0 :   u32 i, nconn = 0, nstream = 0;
    2705             : 
    2706           0 :   clib_memset (&agg_stats, 0, sizeof (agg_stats));
    2707           0 :   for (i = 0; i < num_workers + 1; i++)
    2708             :     {
    2709             :       /* *INDENT-OFF* */
    2710           0 :       pool_foreach (ctx, qm->ctx_pool[i])
    2711             :        {
    2712           0 :         if (quic_ctx_is_conn (ctx) && ctx->conn)
    2713             :           {
    2714           0 :             quicly_get_stats (ctx->conn, &st);
    2715           0 :             agg_stats.rtt.smoothed += st.rtt.smoothed;
    2716           0 :             agg_stats.rtt.minimum += st.rtt.minimum;
    2717           0 :             agg_stats.rtt.variance += st.rtt.variance;
    2718           0 :             agg_stats.num_packets.received += st.num_packets.received;
    2719           0 :             agg_stats.num_packets.sent += st.num_packets.sent;
    2720           0 :             agg_stats.num_packets.lost += st.num_packets.lost;
    2721           0 :             agg_stats.num_packets.ack_received += st.num_packets.ack_received;
    2722           0 :             agg_stats.num_bytes.received += st.num_bytes.received;
    2723           0 :             agg_stats.num_bytes.sent += st.num_bytes.sent;
    2724           0 :             nconn++;
    2725             :           }
    2726           0 :         else if (quic_ctx_is_stream (ctx))
    2727           0 :           nstream++;
    2728             :       }
    2729             :       /* *INDENT-ON* */
    2730             :     }
    2731           0 :   vlib_cli_output (vm, "-------- Connections --------");
    2732           0 :   vlib_cli_output (vm, "Current:         %u", nconn);
    2733           0 :   vlib_cli_output (vm, "Opened:          %d",
    2734             :                    quic_get_counter_value (QUIC_ERROR_OPENED_CONNECTION));
    2735           0 :   vlib_cli_output (vm, "Closed:          %d",
    2736             :                    quic_get_counter_value (QUIC_ERROR_CLOSED_CONNECTION));
    2737           0 :   vlib_cli_output (vm, "---------- Streams ----------");
    2738           0 :   vlib_cli_output (vm, "Current:         %u", nstream);
    2739           0 :   vlib_cli_output (vm, "Opened:          %d",
    2740             :                    quic_get_counter_value (QUIC_ERROR_OPENED_STREAM));
    2741           0 :   vlib_cli_output (vm, "Closed:          %d",
    2742             :                    quic_get_counter_value (QUIC_ERROR_CLOSED_STREAM));
    2743           0 :   vlib_cli_output (vm, "---------- Packets ----------");
    2744           0 :   vlib_cli_output (vm, "RX Total:        %d",
    2745             :                    quic_get_counter_value (QUIC_ERROR_RX_PACKETS));
    2746           0 :   vlib_cli_output (vm, "RX 0RTT:         %d",
    2747             :                    quic_get_counter_value (QUIC_ERROR_ZERO_RTT_RX_PACKETS));
    2748           0 :   vlib_cli_output (vm, "RX 1RTT:         %d",
    2749             :                    quic_get_counter_value (QUIC_ERROR_ONE_RTT_RX_PACKETS));
    2750           0 :   vlib_cli_output (vm, "TX Total:        %d",
    2751             :                    quic_get_counter_value (QUIC_ERROR_TX_PACKETS));
    2752           0 :   vlib_cli_output (vm, "----------- Stats -----------");
    2753           0 :   vlib_cli_output (vm, "Min      RTT     %f",
    2754           0 :                    nconn > 0 ? agg_stats.rtt.minimum / nconn : 0);
    2755           0 :   vlib_cli_output (vm, "Smoothed RTT     %f",
    2756           0 :                    nconn > 0 ? agg_stats.rtt.smoothed / nconn : 0);
    2757           0 :   vlib_cli_output (vm, "Variance on RTT  %f",
    2758           0 :                    nconn > 0 ? agg_stats.rtt.variance / nconn : 0);
    2759           0 :   vlib_cli_output (vm, "Packets Received %lu",
    2760             :                    agg_stats.num_packets.received);
    2761           0 :   vlib_cli_output (vm, "Packets Sent     %lu", agg_stats.num_packets.sent);
    2762           0 :   vlib_cli_output (vm, "Packets Lost     %lu", agg_stats.num_packets.lost);
    2763           0 :   vlib_cli_output (vm, "Packets Acks     %lu",
    2764             :                    agg_stats.num_packets.ack_received);
    2765           0 :   vlib_cli_output (vm, "RX bytes         %lu", agg_stats.num_bytes.received);
    2766           0 :   vlib_cli_output (vm, "TX bytes         %lu", agg_stats.num_bytes.sent);
    2767           0 : }
    2768             : 
    2769             : static u8 *
    2770           0 : quic_format_quicly_conn_id (u8 * s, va_list * args)
    2771             : {
    2772           0 :   quicly_cid_plaintext_t *mid = va_arg (*args, quicly_cid_plaintext_t *);
    2773           0 :   s = format (s, "C%x_%x", mid->master_id, mid->thread_id);
    2774           0 :   return s;
    2775             : }
    2776             : 
    2777             : static u8 *
    2778           0 : quic_format_quicly_stream_id (u8 * s, va_list * args)
    2779             : {
    2780           0 :   quicly_stream_t *stream = va_arg (*args, quicly_stream_t *);
    2781             :   s =
    2782           0 :     format (s, "%U S%lx", quic_format_quicly_conn_id,
    2783             :             quicly_get_master_id (stream->conn), stream->stream_id);
    2784           0 :   return s;
    2785             : }
    2786             : 
    2787             : static u8 *
    2788           0 : quic_format_listener_ctx (u8 * s, va_list * args)
    2789             : {
    2790           0 :   quic_ctx_t *ctx = va_arg (*args, quic_ctx_t *);
    2791           0 :   s = format (s, "[#%d][%x][Listener]", ctx->c_thread_index, ctx->c_c_index);
    2792           0 :   return s;
    2793             : }
    2794             : 
    2795             : static u8 *
    2796           0 : quic_format_connection_ctx (u8 * s, va_list * args)
    2797             : {
    2798           0 :   quic_ctx_t *ctx = va_arg (*args, quic_ctx_t *);
    2799             :   quicly_stats_t quicly_stats;
    2800             : 
    2801           0 :   s = format (s, "[#%d][%x]", ctx->c_thread_index, ctx->c_c_index);
    2802             : 
    2803           0 :   if (!ctx->conn)
    2804             :     {
    2805           0 :       s = format (s, "- no conn -\n");
    2806           0 :       return s;
    2807             :     }
    2808           0 :   s = format (s, "[%U]",
    2809             :               quic_format_quicly_conn_id, quicly_get_master_id (ctx->conn));
    2810           0 :   quicly_get_stats (ctx->conn, &quicly_stats);
    2811             : 
    2812           0 :   s = format (s, "[RTT >%3d, ~%3d, V%3d, last %3d]",
    2813             :               quicly_stats.rtt.minimum, quicly_stats.rtt.smoothed,
    2814             :               quicly_stats.rtt.variance, quicly_stats.rtt.latest);
    2815           0 :   s = format (s, " TX:%d RX:%d loss:%d ack:%d",
    2816             :               quicly_stats.num_packets.sent,
    2817             :               quicly_stats.num_packets.received,
    2818             :               quicly_stats.num_packets.lost,
    2819             :               quicly_stats.num_packets.ack_received);
    2820             :   s =
    2821           0 :     format (s, "\ncwnd:%u ssthresh:%u recovery_end:%lu", quicly_stats.cc.cwnd,
    2822             :             quicly_stats.cc.ssthresh, quicly_stats.cc.recovery_end);
    2823             : 
    2824           0 :   quicly_context_t *quicly_ctx = quic_get_quicly_ctx_from_ctx (ctx);
    2825           0 :   if (quicly_ctx->init_cc == &quicly_cc_cubic_init)
    2826             :     {
    2827           0 :       s = format (
    2828             :         s,
    2829             :         "\nk:%d w_max:%u w_last_max:%u avoidance_start:%ld last_sent_time:%ld",
    2830             :         quicly_stats.cc.state.cubic.k, quicly_stats.cc.state.cubic.w_max,
    2831             :         quicly_stats.cc.state.cubic.w_last_max,
    2832             :         quicly_stats.cc.state.cubic.avoidance_start,
    2833             :         quicly_stats.cc.state.cubic.last_sent_time);
    2834             :     }
    2835           0 :   else if (quicly_ctx->init_cc == &quicly_cc_reno_init)
    2836             :     {
    2837           0 :       s = format (s, " stash:%u", quicly_stats.cc.state.reno.stash);
    2838             :     }
    2839             : 
    2840           0 :   return s;
    2841             : }
    2842             : 
    2843             : static u8 *
    2844           0 : quic_format_stream_ctx (u8 * s, va_list * args)
    2845             : {
    2846           0 :   quic_ctx_t *ctx = va_arg (*args, quic_ctx_t *);
    2847             :   session_t *stream_session;
    2848           0 :   quicly_stream_t *stream = ctx->stream;
    2849             :   u32 txs, rxs;
    2850             : 
    2851           0 :   s = format (s, "[#%d][%x]", ctx->c_thread_index, ctx->c_c_index);
    2852           0 :   s = format (s, "[%U]", quic_format_quicly_stream_id, stream);
    2853             : 
    2854           0 :   stream_session = session_get_if_valid (ctx->c_s_index, ctx->c_thread_index);
    2855           0 :   if (!stream_session)
    2856             :     {
    2857           0 :       s = format (s, "- no session -\n");
    2858           0 :       return s;
    2859             :     }
    2860           0 :   txs = svm_fifo_max_dequeue (stream_session->tx_fifo);
    2861           0 :   rxs = svm_fifo_max_dequeue (stream_session->rx_fifo);
    2862           0 :   s = format (s, "[rx %d tx %d]\n", rxs, txs);
    2863           0 :   return s;
    2864             : }
    2865             : 
    2866             : static clib_error_t *
    2867           0 : quic_show_connections_command_fn (vlib_main_t * vm,
    2868             :                                   unformat_input_t * input,
    2869             :                                   vlib_cli_command_t * cmd)
    2870             : {
    2871           0 :   unformat_input_t _line_input, *line_input = &_line_input;
    2872           0 :   u8 show_listeners = 0, show_conn = 0, show_stream = 0;
    2873           0 :   u32 num_workers = vlib_num_workers ();
    2874           0 :   quic_main_t *qm = &quic_main;
    2875           0 :   clib_error_t *error = 0;
    2876           0 :   quic_ctx_t *ctx = NULL;
    2877             : 
    2878           0 :   session_cli_return_if_not_enabled ();
    2879             : 
    2880           0 :   if (!unformat_user (input, unformat_line_input, line_input))
    2881             :     {
    2882           0 :       quic_show_aggregated_stats (vm);
    2883           0 :       return 0;
    2884             :     }
    2885             : 
    2886           0 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    2887             :     {
    2888           0 :       if (unformat (line_input, "listener"))
    2889           0 :         show_listeners = 1;
    2890           0 :       else if (unformat (line_input, "conn"))
    2891           0 :         show_conn = 1;
    2892           0 :       else if (unformat (line_input, "stream"))
    2893           0 :         show_stream = 1;
    2894             :       else
    2895             :         {
    2896           0 :           error = clib_error_return (0, "unknown input `%U'",
    2897             :                                      format_unformat_error, line_input);
    2898           0 :           goto done;
    2899             :         }
    2900             :     }
    2901             : 
    2902           0 :   for (int i = 0; i < num_workers + 1; i++)
    2903             :     {
    2904             :       /* *INDENT-OFF* */
    2905           0 :       pool_foreach (ctx, qm->ctx_pool[i])
    2906             :        {
    2907           0 :         if (quic_ctx_is_stream (ctx) && show_stream)
    2908           0 :           vlib_cli_output (vm, "%U", quic_format_stream_ctx, ctx);
    2909           0 :         else if (quic_ctx_is_listener (ctx) && show_listeners)
    2910           0 :           vlib_cli_output (vm, "%U", quic_format_listener_ctx, ctx);
    2911           0 :         else if (quic_ctx_is_conn (ctx) && show_conn)
    2912           0 :           vlib_cli_output (vm, "%U", quic_format_connection_ctx, ctx);
    2913             :       }
    2914             :       /* *INDENT-ON* */
    2915             :     }
    2916             : 
    2917           0 : done:
    2918           0 :   unformat_free (line_input);
    2919           0 :   return error;
    2920             : }
    2921             : 
    2922             : /* *INDENT-OFF* */
    2923        2000 : VLIB_CLI_COMMAND (quic_plugin_crypto_command, static) = {
    2924             :   .path = "quic set crypto api",
    2925             :   .short_help = "quic set crypto api [picotls|vpp]",
    2926             :   .function = quic_plugin_crypto_command_fn,
    2927             : };
    2928        2000 : VLIB_CLI_COMMAND(quic_plugin_set_fifo_size_command, static)=
    2929             : {
    2930             :   .path = "quic set fifo-size",
    2931             :   .short_help = "quic set fifo-size N[K|M|G] (default 64K)",
    2932             :   .function = quic_plugin_set_fifo_size_command_fn,
    2933             : };
    2934        2000 : VLIB_CLI_COMMAND(quic_show_ctx_command, static)=
    2935             : {
    2936             :   .path = "show quic",
    2937             :   .short_help = "show quic",
    2938             :   .function = quic_show_connections_command_fn,
    2939             : };
    2940        2000 : VLIB_CLI_COMMAND (quic_list_crypto_context_command, static) =
    2941             : {
    2942             :   .path = "show quic crypto context",
    2943             :   .short_help = "list quic crypto contextes",
    2944             :   .function = quic_list_crypto_context_command_fn,
    2945             : };
    2946        2000 : VLIB_CLI_COMMAND (quic_set_max_packets_per_key, static) =
    2947             : {
    2948             :   .path = "set quic max_packets_per_key",
    2949             :   .short_help = "set quic max_packets_per_key 16777216",
    2950             :   .function = quic_set_max_packets_per_key_fn,
    2951             : };
    2952        2000 : VLIB_CLI_COMMAND (quic_set_cc, static) = {
    2953             :   .path = "set quic cc",
    2954             :   .short_help = "set quic cc [reno|cubic]",
    2955             :   .function = quic_set_cc_fn,
    2956             : };
    2957             : VLIB_PLUGIN_REGISTER () =
    2958             : {
    2959             :   .version = VPP_BUILD_VER,
    2960             :   .description = "Quic transport protocol",
    2961             :   .default_disabled = 1,
    2962             : };
    2963             : /* *INDENT-ON* */
    2964             : 
    2965             : static clib_error_t *
    2966          25 : quic_config_fn (vlib_main_t * vm, unformat_input_t * input)
    2967             : {
    2968          25 :   unformat_input_t _line_input, *line_input = &_line_input;
    2969          25 :   quic_main_t *qm = &quic_main;
    2970          25 :   clib_error_t *error = 0;
    2971             :   uword tmp;
    2972             :   u32 i;
    2973             : 
    2974          25 :   qm->udp_fifo_size = QUIC_DEFAULT_FIFO_SIZE;
    2975          25 :   qm->udp_fifo_prealloc = 0;
    2976          25 :   qm->connection_timeout = QUIC_DEFAULT_CONN_TIMEOUT;
    2977             : 
    2978          25 :   if (!unformat_user (input, unformat_line_input, line_input))
    2979          25 :     return 0;
    2980             : 
    2981           0 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    2982             :     {
    2983           0 :       if (unformat (line_input, "fifo-size %U", unformat_memory_size, &tmp))
    2984             :         {
    2985           0 :           if (tmp >= 0x100000000ULL)
    2986             :             {
    2987           0 :               error = clib_error_return (0,
    2988             :                                          "fifo-size %llu (0x%llx) too large",
    2989             :                                          tmp, tmp);
    2990           0 :               goto done;
    2991             :             }
    2992           0 :           qm->udp_fifo_size = tmp;
    2993             :         }
    2994           0 :       else if (unformat (line_input, "conn-timeout %u", &i))
    2995           0 :         qm->connection_timeout = i;
    2996           0 :       else if (unformat (line_input, "fifo-prealloc %u", &i))
    2997           0 :         qm->udp_fifo_prealloc = i;
    2998             :       else
    2999             :         {
    3000           0 :           error = clib_error_return (0, "unknown input '%U'",
    3001             :                                      format_unformat_error, line_input);
    3002           0 :           goto done;
    3003             :         }
    3004             :     }
    3005           0 : done:
    3006           0 :   unformat_free (line_input);
    3007           0 :   return error;
    3008             : }
    3009             : 
    3010          75 : VLIB_EARLY_CONFIG_FUNCTION (quic_config_fn, "quic");
    3011             : 
    3012             : static uword
    3013           0 : quic_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
    3014             :               vlib_frame_t * frame)
    3015             : {
    3016           0 :   return 0;
    3017             : }
    3018             : 
    3019             : /* *INDENT-OFF* */
    3020        1900 : VLIB_REGISTER_NODE (quic_input_node) =
    3021             : {
    3022             :   .function = quic_node_fn,
    3023             :   .name = "quic-input",
    3024             :   .vector_size = sizeof (u32),
    3025             :   .type = VLIB_NODE_TYPE_INTERNAL,
    3026             :   .n_errors = ARRAY_LEN (quic_error_strings),
    3027             :   .error_strings = quic_error_strings,
    3028             : };
    3029             : /* *INDENT-ON* */
    3030             : 
    3031             : /*
    3032             :  * fd.io coding-style-patch-verification: ON
    3033             :  *
    3034             :  * Local Variables:
    3035             :  * eval: (c-set-style "gnu")
    3036             :  * End:
    3037             :  */

Generated by: LCOV version 1.14