LCOV - code coverage report
Current view: top level - plugins/quic - quic.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 944 1435 65.8 %
Date: 2023-10-26 01:39:38 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       63817 : quic_crypto_context_get (u32 cr_index, u32 thread_index)
     136             : {
     137       63817 :   quic_main_t *qm = &quic_main;
     138       63817 :   ASSERT (cr_index >> 24 == thread_index);
     139       63817 :   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      789304 : quic_ctx_get (u32 ctx_index, u32 thread_index)
     415             : {
     416      789304 :   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      197522 : quic_ctx_is_stream (quic_ctx_t * ctx)
     444             : {
     445      197522 :   return (ctx->flags & QUIC_F_IS_STREAM);
     446             : }
     447             : 
     448             : static inline int
     449           5 : quic_ctx_is_listener (quic_ctx_t * ctx)
     450             : {
     451           5 :   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       50949 : 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       50949 :   stream_data = (quic_stream_data_t *) stream->data;
     467       50949 :   *ctx = quic_ctx_get (stream_data->ctx_id, stream_data->thread_index);
     468       50949 :   return session_get ((*ctx)->c_s_index, stream_data->thread_index);
     469             : }
     470             : 
     471             : static inline void
     472       50568 : quic_make_connection_key (clib_bihash_kv_16_8_t * kv,
     473             :                           const quicly_cid_plaintext_t * id)
     474             : {
     475       50568 :   kv->key[0] = ((u64) id->master_id) << 32 | (u64) id->thread_id;
     476       50568 :   kv->key[1] = id->node_id;
     477       50568 : }
     478             : 
     479             : static int
     480       14318 : quic_sendable_packet_count (session_t * udp_session)
     481             : {
     482             :   u32 max_enqueue;
     483       14318 :   u32 packet_size = QUIC_MAX_PACKET_SIZE + SESSION_CONN_HDR_LEN;
     484       14318 :   max_enqueue = svm_fifo_max_enqueue (udp_session->tx_fifo);
     485       14318 :   return clib_min (max_enqueue / packet_size, QUIC_SEND_PACKET_VEC_SIZE);
     486             : }
     487             : 
     488             : static quicly_context_t *
     489       63750 : quic_get_quicly_ctx_from_ctx (quic_ctx_t * ctx)
     490             : {
     491             :   crypto_context_t *crctx =
     492       63750 :     quic_crypto_context_get (ctx->crypto_context_index, ctx->c_thread_index);
     493       63750 :   quic_crypto_context_data_t *data =
     494             :     (quic_crypto_context_data_t *) crctx->data;
     495       63750 :   return &data->quicly_ctx;
     496             : }
     497             : 
     498             : static quicly_context_t *
     499       50496 : quic_get_quicly_ctx_from_udp (u64 udp_session_handle)
     500             : {
     501       50496 :   session_t *udp_session = session_get_from_handle (udp_session_handle);
     502             :   quic_ctx_t *ctx =
     503       50496 :     quic_ctx_get (udp_session->opaque, udp_session->thread_index);
     504       50496 :   return quic_get_quicly_ctx_from_ctx (ctx);
     505             : }
     506             : 
     507             : static inline void
     508       13182 : quic_set_udp_tx_evt (session_t * udp_session)
     509             : {
     510       13182 :   int rv = 0;
     511       13182 :   if (svm_fifo_set_event (udp_session->tx_fifo))
     512        5238 :     rv = session_send_io_evt_to_thread (udp_session->tx_fifo,
     513             :                                         SESSION_IO_EVT_TX);
     514       13182 :   if (PREDICT_FALSE (rv))
     515           0 :     clib_warning ("Event enqueue errored %d", rv);
     516       13182 : }
     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      108872 : 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      108872 :   sctx = quic_ctx_get (stream_session->connection_index,
     542      108872 :                        stream_session->thread_index);
     543      108872 :   QUIC_ASSERT (quic_ctx_is_stream (sctx));
     544      108872 :   stream = sctx->stream;
     545      108872 :   stream_data = (quic_stream_data_t *) stream->data;
     546             : 
     547      108872 :   f = stream_session->rx_fifo;
     548      108872 :   max_deq = svm_fifo_max_dequeue (f);
     549             : 
     550      108872 :   QUIC_ASSERT (stream_data->app_rx_data_len >= max_deq);
     551      108872 :   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      108872 :   stream_data->app_rx_data_len = max_deq;
     554      108872 : }
     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      101064 : quic_increment_counter (u8 evt, u8 val)
     604             : {
     605      101064 :   vlib_main_t *vm = vlib_get_main ();
     606      101064 :   vlib_node_increment_counter (vm, quic_input_node.index, evt, val);
     607      101064 : }
     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       50496 : 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       50496 :   len = packet->iov_len;
     663       50496 :   f = udp_session->tx_fifo;
     664       50496 :   tc = session_get_transport (udp_session);
     665       50496 :   max_enqueue = svm_fifo_max_enqueue (f);
     666       50496 :   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       50496 :   hdr.data_length = len;
     675       50496 :   hdr.data_offset = 0;
     676       50496 :   hdr.is_ip4 = tc->is_ip4;
     677       50496 :   clib_memcpy (&hdr.lcl_ip, &tc->lcl_ip, sizeof (ip46_address_t));
     678       50496 :   hdr.lcl_port = tc->lcl_port;
     679       50496 :   hdr.gso_size = 0;
     680             : 
     681             :   /*  Read dest address from quicly-provided sockaddr */
     682       50496 :   if (hdr.is_ip4)
     683             :     {
     684       50496 :       QUIC_ASSERT (dest->sa.sa_family == AF_INET);
     685       50496 :       struct sockaddr_in *sa4 = (struct sockaddr_in *) &dest->sa;
     686       50496 :       hdr.rmt_port = sa4->sin_port;
     687       50496 :       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       50496 :   svm_fifo_seg_t segs[2] = { { (u8 *) &hdr, sizeof (hdr) },
     698       50496 :                              { packet->iov_base, len } };
     699             : 
     700       50496 :   ret = svm_fifo_enqueue_segments (f, segs, 2, 0 /* allow partial */);
     701       50496 :   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       50496 :   quic_increment_counter (QUIC_ERROR_TX_PACKETS, 1);
     708             : 
     709       50496 :   return 0;
     710             : }
     711             : 
     712             : static int
     713       13218 : quic_send_packets (quic_ctx_t * ctx)
     714       13218 : {
     715             :   struct iovec packets[QUIC_SEND_PACKET_VEC_SIZE];
     716             :   uint8_t
     717       13218 :     buf[QUIC_SEND_PACKET_VEC_SIZE * quic_get_quicly_ctx_from_ctx (ctx)
     718       13218 :                                       ->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       13218 :   u32 n_sent = 0;
     724       13218 :   int err = 0;
     725             : 
     726             :   /* We have sctx, get qctx */
     727       13218 :   if (quic_ctx_is_stream (ctx))
     728        4517 :     ctx = quic_ctx_get (ctx->quic_connection_ctx_id, ctx->c_thread_index);
     729             : 
     730       13218 :   QUIC_ASSERT (!quic_ctx_is_stream (ctx));
     731             : 
     732       13218 :   udp_session = session_get_from_handle_if_valid (ctx->udp_session_handle);
     733       13218 :   if (!udp_session)
     734           0 :     goto quicly_error;
     735             : 
     736       13218 :   conn = ctx->conn;
     737       13218 :   if (!conn)
     738           0 :     return 0;
     739             : 
     740             :   do
     741             :     {
     742             :       /* TODO : quicly can assert it can send min_packets up to 2 */
     743       14318 :       max_packets = quic_sendable_packet_count (udp_session);
     744       14318 :       if (max_packets < 2)
     745          35 :         break;
     746             : 
     747       14283 :       num_packets = max_packets;
     748       14283 :       if ((err = quicly_send (conn, &dest, &src, packets, &num_packets, buf,
     749             :                               sizeof (buf))))
     750          36 :         goto quicly_error;
     751             : 
     752       64743 :       for (i = 0; i != num_packets; ++i)
     753             :         {
     754             : 
     755       50496 :           if ((err =
     756       50496 :                  quic_send_datagram (udp_session, &packets[i], &dest, &src)))
     757           0 :             goto quicly_error;
     758             : 
     759             :         }
     760       14247 :       n_sent += num_packets;
     761             :     }
     762       14247 :   while (num_packets > 0 && num_packets == max_packets);
     763             : 
     764       13182 :   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       13182 :   quic_update_timer (ctx);
     769       13182 :   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             :   QUIC_DBG (2, "DESTROYED_STREAM: session 0x%lx (%U)",
     788             :             session_handle (stream_session), quic_format_err, err);
     789             : 
     790          36 :   session_transport_closing_notify (&sctx->connection);
     791          36 :   session_transport_delete_notify (&sctx->connection);
     792             : 
     793          36 :   quic_increment_counter (QUIC_ERROR_CLOSED_STREAM, 1);
     794          36 :   quic_ctx_free (sctx);
     795          36 :   clib_mem_free (stream->data);
     796          36 : }
     797             : 
     798             : static void
     799           0 : quic_on_stop_sending (quicly_stream_t * stream, int err)
     800             : {
     801             : #if QUIC_DEBUG >= 2
     802             :   quic_stream_data_t *stream_data = (quic_stream_data_t *) stream->data;
     803             :   quic_ctx_t *sctx = quic_ctx_get (stream_data->ctx_id,
     804             :                                    stream_data->thread_index);
     805             :   session_t *stream_session = session_get (sctx->c_s_index,
     806             :                                            sctx->c_thread_index);
     807             :   clib_warning ("(NOT IMPLEMENTD) STOP_SENDING: session 0x%lx (%U)",
     808             :                 session_handle (stream_session), quic_format_err, err);
     809             : #endif
     810             :   /* TODO : handle STOP_SENDING */
     811           0 : }
     812             : 
     813             : static void
     814           0 : quic_on_receive_reset (quicly_stream_t * stream, int err)
     815             : {
     816           0 :   quic_stream_data_t *stream_data = (quic_stream_data_t *) stream->data;
     817           0 :   quic_ctx_t *sctx = quic_ctx_get (stream_data->ctx_id,
     818             :                                    stream_data->thread_index);
     819             : #if QUIC_DEBUG >= 2
     820             :   session_t *stream_session = session_get (sctx->c_s_index,
     821             :                                            sctx->c_thread_index);
     822             :   clib_warning ("RESET_STREAM: session 0x%lx (%U)",
     823             :                 session_handle (stream_session), quic_format_err, err);
     824             : #endif
     825           0 :   session_transport_closing_notify (&sctx->connection);
     826           0 : }
     827             : 
     828             : static void
     829       46187 : quic_on_receive (quicly_stream_t * stream, size_t off, const void *src,
     830             :                  size_t len)
     831             : {
     832             :   QUIC_DBG (3, "received data: %lu bytes, offset %lu", len, off);
     833             :   u32 max_enq;
     834             :   quic_ctx_t *sctx;
     835             :   session_t *stream_session;
     836             :   app_worker_t *app_wrk;
     837             :   svm_fifo_t *f;
     838             :   quic_stream_data_t *stream_data;
     839             :   int rlen;
     840             : 
     841       46187 :   if (!len)
     842          18 :     return;
     843             : 
     844       46169 :   stream_data = (quic_stream_data_t *) stream->data;
     845       46169 :   sctx = quic_ctx_get (stream_data->ctx_id, stream_data->thread_index);
     846       46169 :   stream_session = session_get (sctx->c_s_index, stream_data->thread_index);
     847       46169 :   f = stream_session->rx_fifo;
     848             : 
     849       46169 :   max_enq = svm_fifo_max_enqueue_prod (f);
     850             :   QUIC_DBG (3, "Enqueuing %u at off %u in %u space", len, off, max_enq);
     851             :   /* Handle duplicate packet/chunk from quicly */
     852       46169 :   if (off < stream_data->app_rx_data_len)
     853             :     {
     854             :       QUIC_DBG (3, "Session [idx %u, app_wrk %u, thread %u, rx-fifo 0x%llx]: "
     855             :                 "DUPLICATE PACKET (max_enq %u, len %u, "
     856             :                 "app_rx_data_len %u, off %u, ToBeNQ %u)",
     857             :                 stream_session->session_index,
     858             :                 stream_session->app_wrk_index,
     859             :                 stream_session->thread_index, f,
     860             :                 max_enq, len, stream_data->app_rx_data_len, off,
     861             :                 off - stream_data->app_rx_data_len + len);
     862           0 :       return;
     863             :     }
     864       46169 :   if (PREDICT_FALSE ((off - stream_data->app_rx_data_len + len) > max_enq))
     865             :     {
     866           0 :       QUIC_ERR ("Session [idx %u, app_wrk %u, thread %u, rx-fifo 0x%llx]: "
     867             :                 "RX FIFO IS FULL (max_enq %u, len %u, "
     868             :                 "app_rx_data_len %u, off %u, ToBeNQ %u)",
     869             :                 stream_session->session_index,
     870             :                 stream_session->app_wrk_index,
     871             :                 stream_session->thread_index, f,
     872             :                 max_enq, len, stream_data->app_rx_data_len, off,
     873             :                 off - stream_data->app_rx_data_len + len);
     874           0 :       return;                   /* This shouldn't happen */
     875             :     }
     876       46169 :   if (off == stream_data->app_rx_data_len)
     877             :     {
     878             :       /* Streams live on the same thread so (f, stream_data) should stay consistent */
     879       46161 :       rlen = svm_fifo_enqueue (f, len, (u8 *) src);
     880       46161 :       if (PREDICT_FALSE (rlen < 0))
     881             :         {
     882             :           /*
     883             :            * drop, fifo full
     884             :            * drop, fifo grow
     885             :            */
     886           0 :           return;
     887             :         }
     888             :       QUIC_DBG (3, "Session [idx %u, app_wrk %u, ti %u, rx-fifo 0x%llx]: "
     889             :                 "Enqueuing %u (rlen %u) at off %u in %u space, ",
     890             :                 stream_session->session_index,
     891             :                 stream_session->app_wrk_index,
     892             :                 stream_session->thread_index, f, len, rlen, off, max_enq);
     893       46161 :       stream_data->app_rx_data_len += rlen;
     894       46161 :       QUIC_ASSERT (rlen >= len);
     895       46161 :       app_wrk = app_worker_get_if_valid (stream_session->app_wrk_index);
     896       46161 :       if (PREDICT_TRUE (app_wrk != 0))
     897             :         {
     898       46161 :           app_worker_rx_notify (app_wrk, stream_session);
     899             :         }
     900       46161 :       quic_ack_rx_data (stream_session);
     901             :     }
     902             :   else
     903             :     {
     904           8 :       rlen = svm_fifo_enqueue_with_offset (f,
     905           8 :                                            off - stream_data->app_rx_data_len,
     906             :                                            len, (u8 *) src);
     907           8 :       if (PREDICT_FALSE (rlen < 0))
     908             :         {
     909             :           /*
     910             :            * drop, fifo full
     911             :            * drop, fifo grow
     912             :            */
     913           0 :           return;
     914             :         }
     915           8 :       QUIC_ASSERT (rlen == 0);
     916             :     }
     917       46169 :   return;
     918             : }
     919             : 
     920             : void
     921        4779 : quic_fifo_egress_shift (quicly_stream_t * stream, size_t delta)
     922             : {
     923             :   quic_stream_data_t *stream_data;
     924             :   session_t *stream_session;
     925             :   quic_ctx_t *ctx;
     926             :   svm_fifo_t *f;
     927             :   u32 rv;
     928             : 
     929        4779 :   stream_data = (quic_stream_data_t *) stream->data;
     930        4779 :   stream_session = get_stream_session_and_ctx_from_stream (stream, &ctx);
     931        4779 :   f = stream_session->tx_fifo;
     932             : 
     933        4779 :   QUIC_ASSERT (stream_data->app_tx_data_len >= delta);
     934        4779 :   stream_data->app_tx_data_len -= delta;
     935        4779 :   ctx->bytes_written += delta;
     936        4779 :   rv = svm_fifo_dequeue_drop (f, delta);
     937        4779 :   QUIC_ASSERT (rv == delta);
     938             : 
     939        4779 :   rv = quicly_stream_sync_sendbuf (stream, 0);
     940        4779 :   QUIC_ASSERT (!rv);
     941        4779 : }
     942             : 
     943             : void
     944       46170 : quic_fifo_egress_emit (quicly_stream_t * stream, size_t off, void *dst,
     945             :                        size_t * len, int *wrote_all)
     946             : {
     947             :   quic_stream_data_t *stream_data;
     948             :   quic_ctx_t *ctx;
     949             :   session_t *stream_session;
     950             :   svm_fifo_t *f;
     951             :   u32 deq_max;
     952             : 
     953       46170 :   stream_data = (quic_stream_data_t *) stream->data;
     954       46170 :   stream_session = get_stream_session_and_ctx_from_stream (stream, &ctx);
     955       46170 :   f = stream_session->tx_fifo;
     956             : 
     957             :   QUIC_DBG (3, "Emitting %u, offset %u", *len, off);
     958             : 
     959       46170 :   deq_max = svm_fifo_max_dequeue_cons (f);
     960       46170 :   QUIC_ASSERT (off <= deq_max);
     961       46170 :   if (off + *len < deq_max)
     962             :     {
     963       45850 :       *wrote_all = 0;
     964             :     }
     965             :   else
     966             :     {
     967         320 :       *wrote_all = 1;
     968         320 :       *len = deq_max - off;
     969             :     }
     970       46170 :   QUIC_ASSERT (*len > 0);
     971             : 
     972       46170 :   if (off + *len > stream_data->app_tx_data_len)
     973         135 :     stream_data->app_tx_data_len = off + *len;
     974             : 
     975       46170 :   svm_fifo_peek (f, off, *len, dst);
     976       46170 : }
     977             : 
     978             : static const quicly_stream_callbacks_t quic_stream_callbacks = {
     979             :   .on_destroy = quic_on_stream_destroy,
     980             :   .on_send_shift = quic_fifo_egress_shift,
     981             :   .on_send_emit = quic_fifo_egress_emit,
     982             :   .on_send_stop = quic_on_stop_sending,
     983             :   .on_receive = quic_on_receive,
     984             :   .on_receive_reset = quic_on_receive_reset
     985             : };
     986             : 
     987             : static int
     988          36 : quic_on_stream_open (quicly_stream_open_t * self, quicly_stream_t * stream)
     989             : {
     990             :   /* Return code for this function ends either
     991             :    * - in quicly_receive : if not QUICLY_ERROR_PACKET_IGNORED, will close connection
     992             :    * - in quicly_open_stream, returned directly
     993             :    */
     994             : 
     995             :   session_t *stream_session, *quic_session;
     996             :   quic_stream_data_t *stream_data;
     997             :   app_worker_t *app_wrk;
     998             :   quic_ctx_t *qctx, *sctx;
     999             :   u32 sctx_id;
    1000             :   int rv;
    1001             : 
    1002             :   QUIC_DBG (2, "on_stream_open called");
    1003          36 :   stream->data = clib_mem_alloc (sizeof (quic_stream_data_t));
    1004          36 :   stream->callbacks = &quic_stream_callbacks;
    1005             :   /* Notify accept on parent qsession, but only if this is not a locally
    1006             :    * initiated stream */
    1007          36 :   if (quicly_stream_is_self_initiated (stream))
    1008          18 :     return 0;
    1009             : 
    1010          18 :   sctx_id = quic_ctx_alloc (vlib_get_thread_index ());
    1011          18 :   qctx = quic_get_conn_ctx (stream->conn);
    1012             : 
    1013             :   /* Might need to signal that the connection is ready if the first thing the
    1014             :    * server does is open a stream */
    1015          18 :   quic_check_quic_session_connected (qctx);
    1016             :   /* ctx might be invalidated */
    1017          18 :   qctx = quic_get_conn_ctx (stream->conn);
    1018             : 
    1019          18 :   stream_session = session_alloc (qctx->c_thread_index);
    1020             :   QUIC_DBG (2, "ACCEPTED stream_session 0x%lx ctx %u",
    1021             :             session_handle (stream_session), sctx_id);
    1022          18 :   sctx = quic_ctx_get (sctx_id, qctx->c_thread_index);
    1023          18 :   sctx->parent_app_wrk_id = qctx->parent_app_wrk_id;
    1024          18 :   sctx->parent_app_id = qctx->parent_app_id;
    1025          18 :   sctx->quic_connection_ctx_id = qctx->c_c_index;
    1026          18 :   sctx->c_c_index = sctx_id;
    1027          18 :   sctx->c_s_index = stream_session->session_index;
    1028          18 :   sctx->stream = stream;
    1029          18 :   sctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
    1030          18 :   sctx->flags |= QUIC_F_IS_STREAM;
    1031          18 :   sctx->crypto_context_index = qctx->crypto_context_index;
    1032          18 :   if (quicly_stream_is_unidirectional (stream->stream_id))
    1033           0 :     stream_session->flags |= SESSION_F_UNIDIRECTIONAL;
    1034             : 
    1035          18 :   stream_data = (quic_stream_data_t *) stream->data;
    1036          18 :   stream_data->ctx_id = sctx_id;
    1037          18 :   stream_data->thread_index = sctx->c_thread_index;
    1038          18 :   stream_data->app_rx_data_len = 0;
    1039          18 :   stream_data->app_tx_data_len = 0;
    1040             : 
    1041          18 :   sctx->c_s_index = stream_session->session_index;
    1042          18 :   stream_session->session_state = SESSION_STATE_CREATED;
    1043          18 :   stream_session->app_wrk_index = sctx->parent_app_wrk_id;
    1044          18 :   stream_session->connection_index = sctx->c_c_index;
    1045          18 :   stream_session->session_type =
    1046          18 :     session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC, qctx->udp_is_ip4);
    1047          18 :   quic_session = session_get (qctx->c_s_index, qctx->c_thread_index);
    1048             :   /* Make sure quic session is in listening state */
    1049          18 :   quic_session->session_state = SESSION_STATE_LISTENING;
    1050          18 :   stream_session->listener_handle = listen_session_get_handle (quic_session);
    1051             : 
    1052          18 :   app_wrk = app_worker_get (stream_session->app_wrk_index);
    1053          18 :   if ((rv = app_worker_init_connected (app_wrk, stream_session)))
    1054             :     {
    1055           0 :       QUIC_ERR ("failed to allocate fifos");
    1056           0 :       quicly_reset_stream (stream, QUIC_APP_ALLOCATION_ERROR);
    1057           0 :       return 0;                 /* Frame is still valid */
    1058             :     }
    1059          18 :   svm_fifo_add_want_deq_ntf (stream_session->rx_fifo,
    1060             :                              SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL |
    1061             :                              SVM_FIFO_WANT_DEQ_NOTIF_IF_EMPTY);
    1062             : 
    1063          18 :   stream_session->session_state = SESSION_STATE_ACCEPTING;
    1064          18 :   if ((rv = app_worker_accept_notify (app_wrk, stream_session)))
    1065             :     {
    1066           0 :       QUIC_ERR ("failed to notify accept worker app");
    1067           0 :       quicly_reset_stream (stream, QUIC_APP_ACCEPT_NOTIFY_ERROR);
    1068           0 :       return 0;                 /* Frame is still valid */
    1069             :     }
    1070             : 
    1071          18 :   return 0;
    1072             : }
    1073             : 
    1074             : static void
    1075          18 : quic_on_closed_by_remote (quicly_closed_by_remote_t *self, quicly_conn_t *conn,
    1076             :                           int code, uint64_t frame_type, const char *reason,
    1077             :                           size_t reason_len)
    1078             : {
    1079          18 :   quic_ctx_t *ctx = quic_get_conn_ctx (conn);
    1080             : #if QUIC_DEBUG >= 2
    1081             :   session_t *quic_session = session_get (ctx->c_s_index, ctx->c_thread_index);
    1082             :   clib_warning ("Session 0x%lx closed by peer (%U) %.*s ",
    1083             :                 session_handle (quic_session), quic_format_err, code,
    1084             :                 reason_len, reason);
    1085             : #endif
    1086          18 :   ctx->conn_state = QUIC_CONN_STATE_PASSIVE_CLOSING;
    1087          18 :   session_transport_closing_notify (&ctx->connection);
    1088          18 : }
    1089             : 
    1090             : /* Timer handling */
    1091             : 
    1092             : static int64_t
    1093       78033 : quic_get_thread_time (u8 thread_index)
    1094             : {
    1095       78033 :   return quic_main.wrk_ctx[thread_index].time_now;
    1096             : }
    1097             : 
    1098             : static int64_t
    1099       78033 : quic_get_time (quicly_now_t * self)
    1100             : {
    1101       78033 :   u8 thread_index = vlib_get_thread_index ();
    1102       78033 :   return quic_get_thread_time (thread_index);
    1103             : }
    1104             : 
    1105             : static u32
    1106     8113960 : quic_set_time_now (u32 thread_index)
    1107             : {
    1108     8113960 :   vlib_main_t *vlib_main = vlib_get_main ();
    1109     8101990 :   f64 time = vlib_time_now (vlib_main);
    1110     8165150 :   quic_main.wrk_ctx[thread_index].time_now = (int64_t) (time * 1000.f);
    1111     8165150 :   return quic_main.wrk_ctx[thread_index].time_now;
    1112             : }
    1113             : 
    1114             : /* Transport proto callback */
    1115             : static void
    1116     8120540 : quic_update_time (f64 now, u8 thread_index)
    1117             : {
    1118             :   tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
    1119             : 
    1120     8120540 :   tw = &quic_main.wrk_ctx[thread_index].timer_wheel;
    1121     8120540 :   quic_set_time_now (thread_index);
    1122     8163980 :   tw_timer_expire_timers_1t_3w_1024sl_ov (tw, now);
    1123     8171020 : }
    1124             : 
    1125             : static void
    1126          76 : quic_timer_expired (u32 conn_index)
    1127             : {
    1128             :   quic_ctx_t *ctx;
    1129             :   QUIC_DBG (4, "Timer expired for conn %u at %ld", conn_index,
    1130             :             quic_get_time (NULL));
    1131          76 :   ctx = quic_ctx_get (conn_index, vlib_get_thread_index ());
    1132          76 :   ctx->timer_handle = QUIC_TIMER_HANDLE_INVALID;
    1133          76 :   quic_send_packets (ctx);
    1134          76 : }
    1135             : 
    1136             : static void
    1137       13182 : quic_update_timer (quic_ctx_t * ctx)
    1138             : {
    1139             :   tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
    1140             :   int64_t next_timeout, next_interval;
    1141             :   session_t *quic_session;
    1142             :   int rv;
    1143             : 
    1144             :   /*  This timeout is in ms which is the unit of our timer */
    1145       13182 :   next_timeout = quicly_get_first_timeout (ctx->conn);
    1146       13182 :   next_interval = next_timeout - quic_get_time (NULL);
    1147             : 
    1148       13182 :   if (next_timeout == 0 || next_interval <= 0)
    1149             :     {
    1150          24 :       if (ctx->c_s_index == QUIC_SESSION_INVALID)
    1151             :         {
    1152           0 :           next_interval = 1;
    1153             :         }
    1154             :       else
    1155             :         {
    1156          24 :           quic_session = session_get (ctx->c_s_index, ctx->c_thread_index);
    1157          24 :           if (svm_fifo_set_event (quic_session->tx_fifo))
    1158             :             {
    1159           5 :               rv = session_send_io_evt_to_thread_custom (
    1160           5 :                 quic_session, quic_session->thread_index, SESSION_IO_EVT_TX);
    1161           5 :               if (PREDICT_FALSE (rv))
    1162           0 :                 QUIC_ERR ("Failed to enqueue builtin_tx %d", rv);
    1163             :             }
    1164          24 :           return;
    1165             :         }
    1166             :     }
    1167             : 
    1168       13158 :   ASSERT (vlib_get_thread_index () == ctx->c_thread_index ||
    1169             :           vlib_get_thread_index () == 0);
    1170       13158 :   tw = &quic_main.wrk_ctx[ctx->c_thread_index].timer_wheel;
    1171             : 
    1172             :   QUIC_DBG (4, "Timer set to %ld (int %ld) for ctx %u", next_timeout,
    1173             :             next_interval, ctx->c_c_index);
    1174             : 
    1175       13158 :   if (ctx->timer_handle == QUIC_TIMER_HANDLE_INVALID)
    1176             :     {
    1177          76 :       if (next_timeout == INT64_MAX)
    1178             :         {
    1179             :           QUIC_DBG (4, "timer for ctx %u already stopped", ctx->c_c_index);
    1180           0 :           return;
    1181             :         }
    1182          76 :       ctx->timer_handle = tw_timer_start_1t_3w_1024sl_ov (tw, ctx->c_c_index,
    1183             :                                                           0, next_interval);
    1184             :     }
    1185             :   else
    1186             :     {
    1187       13082 :       if (next_timeout == INT64_MAX)
    1188             :         {
    1189           0 :           quic_stop_ctx_timer (ctx);
    1190             :         }
    1191             :       else
    1192       13082 :         tw_timer_update_1t_3w_1024sl_ov (tw, ctx->timer_handle,
    1193             :                                          next_interval);
    1194             :     }
    1195       13158 :   return;
    1196             : }
    1197             : 
    1198             : static void
    1199          46 : quic_expired_timers_dispatch (u32 * expired_timers)
    1200             : {
    1201             :   int i;
    1202             : 
    1203         122 :   for (i = 0; i < vec_len (expired_timers); i++)
    1204             :     {
    1205          76 :       quic_timer_expired (expired_timers[i]);
    1206             :     }
    1207          46 : }
    1208             : 
    1209             : /* Transport proto functions */
    1210             : static int
    1211          18 : quic_connect_stream (session_t * quic_session, session_endpoint_cfg_t * sep)
    1212             : {
    1213             :   uint64_t quic_session_handle;
    1214             :   session_t *stream_session;
    1215             :   quic_stream_data_t *stream_data;
    1216             :   quicly_stream_t *stream;
    1217             :   quicly_conn_t *conn;
    1218             :   app_worker_t *app_wrk;
    1219             :   quic_ctx_t *qctx, *sctx;
    1220             :   u32 sctx_index;
    1221             :   u8 is_unidir;
    1222             :   int rv;
    1223             : 
    1224             :   /*  Find base session to which the user want to attach a stream */
    1225          18 :   quic_session_handle = session_handle (quic_session);
    1226             :   QUIC_DBG (2, "Opening new stream (qsession %u)", quic_session_handle);
    1227             : 
    1228          18 :   if (session_type_transport_proto (quic_session->session_type) !=
    1229             :       TRANSPORT_PROTO_QUIC)
    1230             :     {
    1231           0 :       QUIC_ERR ("received incompatible session");
    1232           0 :       return -1;
    1233             :     }
    1234             : 
    1235          18 :   app_wrk = app_worker_get_if_valid (quic_session->app_wrk_index);
    1236          18 :   if (!app_wrk)
    1237             :     {
    1238           0 :       QUIC_ERR ("Invalid app worker :(");
    1239           0 :       return -1;
    1240             :     }
    1241             : 
    1242          18 :   sctx_index = quic_ctx_alloc (quic_session->thread_index);  /*  Allocate before we get pointers */
    1243          18 :   sctx = quic_ctx_get (sctx_index, quic_session->thread_index);
    1244          18 :   qctx = quic_ctx_get (quic_session->connection_index,
    1245          18 :                        quic_session->thread_index);
    1246          18 :   if (quic_ctx_is_stream (qctx))
    1247             :     {
    1248           0 :       QUIC_ERR ("session is a stream");
    1249           0 :       quic_ctx_free (sctx);
    1250           0 :       return -1;
    1251             :     }
    1252             : 
    1253          18 :   sctx->parent_app_wrk_id = qctx->parent_app_wrk_id;
    1254          18 :   sctx->parent_app_id = qctx->parent_app_id;
    1255          18 :   sctx->quic_connection_ctx_id = qctx->c_c_index;
    1256          18 :   sctx->c_c_index = sctx_index;
    1257          18 :   sctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
    1258          18 :   sctx->flags |= QUIC_F_IS_STREAM;
    1259             : 
    1260          18 :   conn = qctx->conn;
    1261             : 
    1262          18 :   if (!conn || !quicly_connection_is_ready (conn))
    1263           0 :     return -1;
    1264             : 
    1265          18 :   is_unidir = sep->transport_flags & TRANSPORT_CFG_F_UNIDIRECTIONAL;
    1266          18 :   if ((rv = quicly_open_stream (conn, &stream, is_unidir)))
    1267             :     {
    1268             :       QUIC_DBG (2, "Stream open failed with %d", rv);
    1269           0 :       return -1;
    1270             :     }
    1271          18 :   quic_increment_counter (QUIC_ERROR_OPENED_STREAM, 1);
    1272             : 
    1273          18 :   sctx->stream = stream;
    1274          18 :   sctx->crypto_context_index = qctx->crypto_context_index;
    1275             : 
    1276             :   QUIC_DBG (2, "Opened stream %d, creating session", stream->stream_id);
    1277             : 
    1278          18 :   stream_session = session_alloc (qctx->c_thread_index);
    1279             :   QUIC_DBG (2, "Allocated stream_session 0x%lx ctx %u",
    1280             :             session_handle (stream_session), sctx_index);
    1281          18 :   stream_session->app_wrk_index = app_wrk->wrk_index;
    1282          18 :   stream_session->connection_index = sctx_index;
    1283          18 :   stream_session->listener_handle = quic_session_handle;
    1284          18 :   stream_session->session_type =
    1285          18 :     session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC, qctx->udp_is_ip4);
    1286          18 :   if (is_unidir)
    1287           0 :     stream_session->flags |= SESSION_F_UNIDIRECTIONAL;
    1288             : 
    1289          18 :   sctx->c_s_index = stream_session->session_index;
    1290          18 :   stream_data = (quic_stream_data_t *) stream->data;
    1291          18 :   stream_data->ctx_id = sctx->c_c_index;
    1292          18 :   stream_data->thread_index = sctx->c_thread_index;
    1293          18 :   stream_data->app_rx_data_len = 0;
    1294          18 :   stream_data->app_tx_data_len = 0;
    1295          18 :   stream_session->session_state = SESSION_STATE_READY;
    1296          18 :   stream_session->opaque = sep->opaque;
    1297             : 
    1298             :   /* For now we only reset streams. Cleanup will be triggered by timers */
    1299          18 :   if ((rv = app_worker_init_connected (app_wrk, stream_session)))
    1300             :     {
    1301           0 :       QUIC_ERR ("failed to app_worker_init_connected");
    1302           0 :       quicly_reset_stream (stream, QUIC_APP_CONNECT_NOTIFY_ERROR);
    1303           0 :       return app_worker_connect_notify (app_wrk, NULL, rv, sep->opaque);
    1304             :     }
    1305             : 
    1306          18 :   svm_fifo_add_want_deq_ntf (stream_session->rx_fifo,
    1307             :                              SVM_FIFO_WANT_DEQ_NOTIF_IF_FULL |
    1308             :                              SVM_FIFO_WANT_DEQ_NOTIF_IF_EMPTY);
    1309             : 
    1310          18 :   if (app_worker_connect_notify (app_wrk, stream_session, SESSION_E_NONE,
    1311             :                                  sep->opaque))
    1312             :     {
    1313           0 :       QUIC_ERR ("failed to notify app");
    1314           0 :       quic_increment_counter (QUIC_ERROR_CLOSED_STREAM, 1);
    1315           0 :       quicly_reset_stream (stream, QUIC_APP_CONNECT_NOTIFY_ERROR);
    1316           0 :       return -1;
    1317             :     }
    1318             : 
    1319          18 :   return 0;
    1320             : }
    1321             : 
    1322             : static int
    1323          18 : quic_connect_connection (session_endpoint_cfg_t * sep)
    1324             : {
    1325          18 :   vnet_connect_args_t _cargs, *cargs = &_cargs;
    1326             :   transport_endpt_crypto_cfg_t *ccfg;
    1327          18 :   quic_main_t *qm = &quic_main;
    1328             :   u32 ctx_index, thread_index;
    1329             :   quic_ctx_t *ctx;
    1330             :   app_worker_t *app_wrk;
    1331             :   application_t *app;
    1332             :   int error;
    1333             : 
    1334          18 :   if (!sep->ext_cfg)
    1335           0 :     return SESSION_E_NOEXTCFG;
    1336             : 
    1337             :   /* Use pool on thread 1 if we have workers because of UDP */
    1338          18 :   thread_index = transport_cl_thread ();
    1339          18 :   ccfg = &sep->ext_cfg->crypto;
    1340             : 
    1341          18 :   clib_memset (cargs, 0, sizeof (*cargs));
    1342          18 :   ctx_index = quic_ctx_alloc (thread_index);
    1343          18 :   ctx = quic_ctx_get (ctx_index, thread_index);
    1344          18 :   ctx->parent_app_wrk_id = sep->app_wrk_index;
    1345          18 :   ctx->c_s_index = QUIC_SESSION_INVALID;
    1346          18 :   ctx->c_c_index = ctx_index;
    1347          18 :   ctx->udp_is_ip4 = sep->is_ip4;
    1348          18 :   ctx->timer_handle = QUIC_TIMER_HANDLE_INVALID;
    1349          18 :   ctx->conn_state = QUIC_CONN_STATE_HANDSHAKE;
    1350          18 :   ctx->client_opaque = sep->opaque;
    1351          18 :   ctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
    1352          18 :   if (ccfg->hostname[0])
    1353           0 :     ctx->srv_hostname = format (0, "%s", ccfg->hostname);
    1354             :   else
    1355             :     /*  needed by quic for crypto + determining client / server */
    1356          18 :     ctx->srv_hostname = format (0, "%U", format_ip46_address,
    1357          18 :                                 &sep->ip, sep->is_ip4);
    1358          18 :   vec_terminate_c_string (ctx->srv_hostname);
    1359             : 
    1360          18 :   clib_memcpy (&cargs->sep_ext, sep, sizeof (session_endpoint_cfg_t));
    1361          18 :   cargs->sep.transport_proto = TRANSPORT_PROTO_UDP;
    1362          18 :   cargs->app_index = qm->app_index;
    1363          18 :   cargs->api_context = ctx_index;
    1364             : 
    1365          18 :   app_wrk = app_worker_get (sep->app_wrk_index);
    1366          18 :   app = application_get (app_wrk->app_index);
    1367          18 :   ctx->parent_app_id = app_wrk->app_index;
    1368          18 :   cargs->sep_ext.ns_index = app->ns_index;
    1369          18 :   cargs->sep_ext.transport_flags = TRANSPORT_CFG_F_CONNECTED;
    1370             : 
    1371          18 :   ctx->crypto_engine = ccfg->crypto_engine;
    1372          18 :   ctx->ckpair_index = ccfg->ckpair_index;
    1373          18 :   if ((error = quic_acquire_crypto_context (ctx)))
    1374           0 :     return error;
    1375             : 
    1376          18 :   if ((error = vnet_connect (cargs)))
    1377           0 :     return error;
    1378             : 
    1379          18 :   return 0;
    1380             : }
    1381             : 
    1382             : static int
    1383          36 : quic_connect (transport_endpoint_cfg_t * tep)
    1384             : {
    1385             :   QUIC_DBG (2, "Called quic_connect");
    1386          36 :   session_endpoint_cfg_t *sep = (session_endpoint_cfg_t *) tep;
    1387             :   session_t *quic_session;
    1388          36 :   sep = (session_endpoint_cfg_t *) tep;
    1389             : 
    1390          36 :   quic_session = session_get_from_handle_if_valid (sep->parent_handle);
    1391          36 :   if (quic_session)
    1392          18 :     return quic_connect_stream (quic_session, sep);
    1393             :   else
    1394          18 :     return quic_connect_connection (sep);
    1395             : }
    1396             : 
    1397             : static void
    1398          54 : quic_proto_on_close (u32 ctx_index, u32 thread_index)
    1399             : {
    1400             :   int err;
    1401          54 :   quic_ctx_t *ctx = quic_ctx_get_if_valid (ctx_index, thread_index);
    1402          54 :   if (!ctx)
    1403           0 :     return;
    1404          54 :   session_t *stream_session = session_get (ctx->c_s_index,
    1405             :                                            ctx->c_thread_index);
    1406             : #if QUIC_DEBUG >= 2
    1407             :   clib_warning ("Closing session 0x%lx", session_handle (stream_session));
    1408             : #endif
    1409          54 :   if (quic_ctx_is_stream (ctx))
    1410             :     {
    1411          18 :       quicly_stream_t *stream = ctx->stream;
    1412          18 :       if (!quicly_stream_has_send_side (quicly_is_client (stream->conn),
    1413             :                                         stream->stream_id))
    1414           0 :         return;
    1415          36 :       quicly_sendstate_shutdown (&stream->sendstate, ctx->bytes_written +
    1416          18 :                                  svm_fifo_max_dequeue
    1417             :                                  (stream_session->tx_fifo));
    1418          18 :       err = quicly_stream_sync_sendbuf (stream, 1);
    1419          18 :       if (err)
    1420             :         {
    1421             :           QUIC_DBG (1, "sendstate_shutdown failed for stream session %lu",
    1422             :                     session_handle (stream_session));
    1423           0 :           quicly_reset_stream (stream, QUIC_APP_ERROR_CLOSE_NOTIFY);
    1424             :         }
    1425          18 :       quic_send_packets (ctx);
    1426          18 :       return;
    1427             :     }
    1428             : 
    1429          36 :   switch (ctx->conn_state)
    1430             :     {
    1431          18 :     case QUIC_CONN_STATE_OPENED:
    1432             :     case QUIC_CONN_STATE_HANDSHAKE:
    1433             :     case QUIC_CONN_STATE_READY:
    1434          18 :       ctx->conn_state = QUIC_CONN_STATE_ACTIVE_CLOSING;
    1435          18 :       quicly_conn_t *conn = ctx->conn;
    1436             :       /* Start connection closing. Keep sending packets until quicly_send
    1437             :          returns QUICLY_ERROR_FREE_CONNECTION */
    1438             : 
    1439          18 :       quic_increment_counter (QUIC_ERROR_CLOSED_CONNECTION, 1);
    1440          18 :       quicly_close (conn, QUIC_APP_ERROR_CLOSE_NOTIFY, "Closed by peer");
    1441             :       /* This also causes all streams to be closed (and the cb called) */
    1442          18 :       quic_send_packets (ctx);
    1443          18 :       break;
    1444          18 :     case QUIC_CONN_STATE_PASSIVE_CLOSING:
    1445          18 :       ctx->conn_state = QUIC_CONN_STATE_PASSIVE_CLOSING_APP_CLOSED;
    1446             :       /* send_packets will eventually return an error, we delete the conn at
    1447             :          that point */
    1448          18 :       break;
    1449           0 :     case QUIC_CONN_STATE_PASSIVE_CLOSING_QUIC_CLOSED:
    1450           0 :       quic_connection_delete (ctx);
    1451           0 :       break;
    1452           0 :     case QUIC_CONN_STATE_ACTIVE_CLOSING:
    1453           0 :       break;
    1454           0 :     default:
    1455           0 :       QUIC_ERR ("Trying to close conn in state %d", ctx->conn_state);
    1456           0 :       break;
    1457             :     }
    1458             : }
    1459             : 
    1460             : static u32
    1461           5 : quic_start_listen (u32 quic_listen_session_index,
    1462             :                    transport_endpoint_cfg_t *tep)
    1463             : {
    1464           5 :   vnet_listen_args_t _bargs, *args = &_bargs;
    1465             :   transport_endpt_crypto_cfg_t *ccfg;
    1466           5 :   quic_main_t *qm = &quic_main;
    1467             :   session_handle_t udp_handle;
    1468             :   session_endpoint_cfg_t *sep;
    1469             :   session_t *udp_listen_session;
    1470             :   app_worker_t *app_wrk;
    1471             :   application_t *app;
    1472             :   quic_ctx_t *lctx;
    1473             :   u32 lctx_index;
    1474             :   app_listener_t *app_listener;
    1475             :   int rv;
    1476             : 
    1477           5 :   sep = (session_endpoint_cfg_t *) tep;
    1478           5 :   if (!sep->ext_cfg)
    1479           0 :     return SESSION_E_NOEXTCFG;
    1480             : 
    1481           5 :   ccfg = &sep->ext_cfg->crypto;
    1482           5 :   app_wrk = app_worker_get (sep->app_wrk_index);
    1483           5 :   app = application_get (app_wrk->app_index);
    1484             :   QUIC_DBG (2, "Called quic_start_listen for app %d", app_wrk->app_index);
    1485             : 
    1486           5 :   clib_memset (args, 0, sizeof (*args));
    1487           5 :   args->app_index = qm->app_index;
    1488           5 :   args->sep_ext = *sep;
    1489           5 :   args->sep_ext.ns_index = app->ns_index;
    1490           5 :   args->sep_ext.transport_proto = TRANSPORT_PROTO_UDP;
    1491           5 :   args->sep_ext.transport_flags = TRANSPORT_CFG_F_CONNECTED;
    1492           5 :   if ((rv = vnet_listen (args)))
    1493           0 :     return rv;
    1494             : 
    1495           5 :   lctx_index = quic_ctx_alloc (0);
    1496           5 :   udp_handle = args->handle;
    1497           5 :   app_listener = app_listener_get_w_handle (udp_handle);
    1498           5 :   udp_listen_session = app_listener_get_session (app_listener);
    1499           5 :   udp_listen_session->opaque = lctx_index;
    1500             : 
    1501           5 :   lctx = quic_ctx_get (lctx_index, 0);
    1502           5 :   lctx->flags |= QUIC_F_IS_LISTENER;
    1503             : 
    1504           5 :   clib_memcpy (&lctx->c_rmt_ip, &args->sep.peer.ip, sizeof (ip46_address_t));
    1505           5 :   clib_memcpy (&lctx->c_lcl_ip, &args->sep.ip, sizeof (ip46_address_t));
    1506           5 :   lctx->c_rmt_port = args->sep.peer.port;
    1507           5 :   lctx->c_lcl_port = args->sep.port;
    1508           5 :   lctx->c_is_ip4 = args->sep.is_ip4;
    1509           5 :   lctx->c_fib_index = args->sep.fib_index;
    1510           5 :   lctx->c_proto = TRANSPORT_PROTO_QUIC;
    1511           5 :   lctx->parent_app_wrk_id = sep->app_wrk_index;
    1512           5 :   lctx->parent_app_id = app_wrk->app_index;
    1513           5 :   lctx->udp_session_handle = udp_handle;
    1514           5 :   lctx->c_s_index = quic_listen_session_index;
    1515           5 :   lctx->crypto_engine = ccfg->crypto_engine;
    1516           5 :   lctx->ckpair_index = ccfg->ckpair_index;
    1517           5 :   if ((rv = quic_acquire_crypto_context (lctx)))
    1518           0 :     return rv;
    1519             : 
    1520             :   QUIC_DBG (2, "Listening UDP session 0x%lx",
    1521             :             session_handle (udp_listen_session));
    1522             :   QUIC_DBG (2, "Listening QUIC session 0x%lx", quic_listen_session_index);
    1523           5 :   return lctx_index;
    1524             : }
    1525             : 
    1526             : static u32
    1527           2 : quic_stop_listen (u32 lctx_index)
    1528             : {
    1529             :   QUIC_DBG (2, "Called quic_stop_listen");
    1530             :   quic_ctx_t *lctx;
    1531           2 :   lctx = quic_ctx_get (lctx_index, 0);
    1532           2 :   QUIC_ASSERT (quic_ctx_is_listener (lctx));
    1533           2 :   vnet_unlisten_args_t a = {
    1534           2 :     .handle = lctx->udp_session_handle,
    1535           2 :     .app_index = quic_main.app_index,
    1536             :     .wrk_map_index = 0          /* default wrk */
    1537             :   };
    1538           2 :   if (vnet_unlisten (&a))
    1539           0 :     clib_warning ("unlisten errored");
    1540             : 
    1541           2 :   quic_release_crypto_context (lctx->crypto_context_index,
    1542             :                                0 /* thread_index */ );
    1543           2 :   quic_ctx_free (lctx);
    1544           2 :   return 0;
    1545             : }
    1546             : 
    1547             : static transport_connection_t *
    1548      212748 : quic_connection_get (u32 ctx_index, u32 thread_index)
    1549             : {
    1550             :   quic_ctx_t *ctx;
    1551      212748 :   ctx = quic_ctx_get (ctx_index, thread_index);
    1552      212748 :   return &ctx->connection;
    1553             : }
    1554             : 
    1555             : static transport_connection_t *
    1556          13 : quic_listener_get (u32 listener_index)
    1557             : {
    1558             :   QUIC_DBG (2, "Called quic_listener_get");
    1559             :   quic_ctx_t *ctx;
    1560          13 :   ctx = quic_ctx_get (listener_index, 0);
    1561          13 :   return &ctx->connection;
    1562             : }
    1563             : 
    1564             : static u8 *
    1565           0 : format_quic_ctx (u8 * s, va_list * args)
    1566             : {
    1567           0 :   quic_ctx_t *ctx = va_arg (*args, quic_ctx_t *);
    1568           0 :   u32 verbose = va_arg (*args, u32);
    1569           0 :   u8 *str = 0;
    1570             : 
    1571           0 :   if (!ctx)
    1572           0 :     return s;
    1573           0 :   str = format (str, "[%d:%d][Q] ", ctx->c_thread_index, ctx->c_s_index);
    1574             : 
    1575           0 :   if (quic_ctx_is_listener (ctx))
    1576           0 :     str = format (str, "Listener, UDP %ld", ctx->udp_session_handle);
    1577           0 :   else if (quic_ctx_is_stream (ctx))
    1578           0 :     str = format (str, "Stream %ld conn %d",
    1579           0 :                   ctx->stream->stream_id, ctx->quic_connection_ctx_id);
    1580             :   else                          /* connection */
    1581           0 :     str = format (str, "Conn %d UDP %d", ctx->c_c_index,
    1582             :                   ctx->udp_session_handle);
    1583             : 
    1584           0 :   str = format (str, " app %d wrk %d", ctx->parent_app_id,
    1585             :                 ctx->parent_app_wrk_id);
    1586             : 
    1587           0 :   if (verbose == 1)
    1588           0 :     s = format (s, "%-" SESSION_CLI_ID_LEN "s%-" SESSION_CLI_STATE_LEN "d",
    1589           0 :                 str, ctx->conn_state);
    1590             :   else
    1591           0 :     s = format (s, "%s\n", str);
    1592           0 :   vec_free (str);
    1593           0 :   return s;
    1594             : }
    1595             : 
    1596             : static u8 *
    1597           0 : format_quic_connection (u8 * s, va_list * args)
    1598             : {
    1599           0 :   u32 qc_index = va_arg (*args, u32);
    1600           0 :   u32 thread_index = va_arg (*args, u32);
    1601           0 :   u32 verbose = va_arg (*args, u32);
    1602           0 :   quic_ctx_t *ctx = quic_ctx_get (qc_index, thread_index);
    1603           0 :   s = format (s, "%U", format_quic_ctx, ctx, verbose);
    1604           0 :   return s;
    1605             : }
    1606             : 
    1607             : static u8 *
    1608           0 : format_quic_half_open (u8 * s, va_list * args)
    1609             : {
    1610           0 :   u32 qc_index = va_arg (*args, u32);
    1611           0 :   u32 thread_index = va_arg (*args, u32);
    1612           0 :   quic_ctx_t *ctx = quic_ctx_get (qc_index, thread_index);
    1613           0 :   s = format (s, "[#%d][Q] half-open app %u", thread_index,
    1614             :               ctx->parent_app_id);
    1615           0 :   return s;
    1616             : }
    1617             : 
    1618             : /*  TODO improve */
    1619             : static u8 *
    1620           0 : format_quic_listener (u8 * s, va_list * args)
    1621             : {
    1622           0 :   u32 tci = va_arg (*args, u32);
    1623           0 :   u32 thread_index = va_arg (*args, u32);
    1624           0 :   u32 verbose = va_arg (*args, u32);
    1625           0 :   quic_ctx_t *ctx = quic_ctx_get (tci, thread_index);
    1626           0 :   s = format (s, "%U", format_quic_ctx, ctx, verbose);
    1627           0 :   return s;
    1628             : }
    1629             : 
    1630             : /* Session layer callbacks */
    1631             : 
    1632             : static inline void
    1633       50514 : quic_build_sockaddr (struct sockaddr *sa, socklen_t * salen,
    1634             :                      ip46_address_t * addr, u16 port, u8 is_ip4)
    1635             : {
    1636       50514 :   if (is_ip4)
    1637             :     {
    1638       50514 :       struct sockaddr_in *sa4 = (struct sockaddr_in *) sa;
    1639       50514 :       sa4->sin_family = AF_INET;
    1640       50514 :       sa4->sin_port = port;
    1641       50514 :       sa4->sin_addr.s_addr = addr->ip4.as_u32;
    1642       50514 :       *salen = sizeof (struct sockaddr_in);
    1643             :     }
    1644             :   else
    1645             :     {
    1646           0 :       struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) sa;
    1647           0 :       sa6->sin6_family = AF_INET6;
    1648           0 :       sa6->sin6_port = port;
    1649           0 :       clib_memcpy (&sa6->sin6_addr, &addr->ip6, 16);
    1650           0 :       *salen = sizeof (struct sockaddr_in6);
    1651             :     }
    1652       50514 : }
    1653             : 
    1654             : static void
    1655          18 : quic_on_quic_session_connected (quic_ctx_t * ctx)
    1656             : {
    1657             :   session_t *quic_session;
    1658             :   app_worker_t *app_wrk;
    1659          18 :   u32 ctx_id = ctx->c_c_index;
    1660          18 :   u32 thread_index = ctx->c_thread_index;
    1661             :   int rv;
    1662             : 
    1663          18 :   quic_session = session_alloc (thread_index);
    1664             : 
    1665             :   QUIC_DBG (2, "Allocated quic session 0x%lx", session_handle (quic_session));
    1666          18 :   ctx->c_s_index = quic_session->session_index;
    1667          18 :   quic_session->app_wrk_index = ctx->parent_app_wrk_id;
    1668          18 :   quic_session->connection_index = ctx->c_c_index;
    1669          18 :   quic_session->listener_handle = SESSION_INVALID_HANDLE;
    1670          18 :   quic_session->session_type =
    1671          18 :     session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC, ctx->udp_is_ip4);
    1672             : 
    1673             :   /* If quic session connected fails, immediatly close connection */
    1674          18 :   app_wrk = app_worker_get (ctx->parent_app_wrk_id);
    1675          18 :   if ((rv = app_worker_init_connected (app_wrk, quic_session)))
    1676             :     {
    1677           0 :       QUIC_ERR ("failed to app_worker_init_connected");
    1678           0 :       quic_proto_on_close (ctx_id, thread_index);
    1679           0 :       app_worker_connect_notify (app_wrk, NULL, rv, ctx->client_opaque);
    1680           0 :       return;
    1681             :     }
    1682             : 
    1683          18 :   quic_session->session_state = SESSION_STATE_CONNECTING;
    1684          18 :   if ((rv = app_worker_connect_notify (app_wrk, quic_session,
    1685             :                                        SESSION_E_NONE, ctx->client_opaque)))
    1686             :     {
    1687           0 :       QUIC_ERR ("failed to notify app %d", rv);
    1688           0 :       quic_proto_on_close (ctx_id, thread_index);
    1689           0 :       return;
    1690             :     }
    1691             : }
    1692             : 
    1693             : static void
    1694       50496 : quic_check_quic_session_connected (quic_ctx_t * ctx)
    1695             : {
    1696             :   /* Called when we need to trigger quic session connected
    1697             :    * we may call this function on the server side / at
    1698             :    * stream opening */
    1699             : 
    1700             :   /* Conn may be set to null if the connection is terminated */
    1701       50496 :   if (!ctx->conn || ctx->conn_state != QUIC_CONN_STATE_HANDSHAKE)
    1702       50441 :     return;
    1703          55 :   if (!quicly_connection_is_ready (ctx->conn))
    1704          37 :     return;
    1705          18 :   ctx->conn_state = QUIC_CONN_STATE_READY;
    1706          18 :   if (!quicly_is_client (ctx->conn))
    1707           0 :     return;
    1708          18 :   quic_on_quic_session_connected (ctx);
    1709             : }
    1710             : 
    1711             : static inline void
    1712           0 : quic_update_conn_ctx (quicly_conn_t * conn, quicly_context_t * quicly_context)
    1713             : {
    1714             :   /* we need to update the quicly_conn on migrate
    1715             :    * as it contains a pointer to the crypto context */
    1716             :   ptls_context_t **tls;
    1717             :   quicly_context_t **_quicly_context;
    1718           0 :   _quicly_context = (quicly_context_t **) conn;
    1719           0 :   *_quicly_context = quicly_context;
    1720           0 :   tls = (ptls_context_t **) quicly_get_tls (conn);
    1721           0 :   *tls = quicly_context->tls;
    1722           0 : }
    1723             : 
    1724             : static void
    1725           0 : quic_receive_connection (void *arg)
    1726             : {
    1727           0 :   u32 new_ctx_id, thread_index = vlib_get_thread_index ();
    1728             :   quic_ctx_t *temp_ctx, *new_ctx;
    1729             :   clib_bihash_kv_16_8_t kv;
    1730             :   quicly_conn_t *conn;
    1731             :   quicly_context_t *quicly_context;
    1732             :   session_t *udp_session;
    1733             : 
    1734           0 :   temp_ctx = arg;
    1735           0 :   new_ctx_id = quic_ctx_alloc (thread_index);
    1736           0 :   new_ctx = quic_ctx_get (new_ctx_id, thread_index);
    1737             : 
    1738             :   QUIC_DBG (2, "Received conn %u (now %u)", temp_ctx->c_thread_index,
    1739             :             new_ctx_id);
    1740             : 
    1741           0 :   clib_memcpy (new_ctx, temp_ctx, sizeof (quic_ctx_t));
    1742           0 :   clib_mem_free (temp_ctx);
    1743             : 
    1744           0 :   new_ctx->c_thread_index = thread_index;
    1745           0 :   new_ctx->c_c_index = new_ctx_id;
    1746           0 :   quic_acquire_crypto_context (new_ctx);
    1747             : 
    1748           0 :   conn = new_ctx->conn;
    1749           0 :   quicly_context = quic_get_quicly_ctx_from_ctx (new_ctx);
    1750           0 :   quic_update_conn_ctx (conn, quicly_context);
    1751             : 
    1752           0 :   quic_store_conn_ctx (conn, new_ctx);
    1753           0 :   quic_make_connection_key (&kv, quicly_get_master_id (conn));
    1754           0 :   kv.value = ((u64) thread_index) << 32 | (u64) new_ctx_id;
    1755             :   QUIC_DBG (2, "Registering conn with id %lu %lu", kv.key[0], kv.key[1]);
    1756           0 :   clib_bihash_add_del_16_8 (&quic_main.connection_hash, &kv, 1 /* is_add */ );
    1757           0 :   new_ctx->timer_handle = QUIC_TIMER_HANDLE_INVALID;
    1758           0 :   quic_update_timer (new_ctx);
    1759             : 
    1760             :   /*  Trigger write on this connection if necessary */
    1761           0 :   udp_session = session_get_from_handle (new_ctx->udp_session_handle);
    1762           0 :   udp_session->opaque = new_ctx_id;
    1763           0 :   udp_session->flags &= ~SESSION_F_IS_MIGRATING;
    1764           0 :   if (svm_fifo_max_dequeue (udp_session->tx_fifo))
    1765           0 :     quic_set_udp_tx_evt (udp_session);
    1766           0 : }
    1767             : 
    1768             : static void
    1769           0 : quic_transfer_connection (u32 ctx_index, u32 dest_thread)
    1770             : {
    1771             :   quic_ctx_t *ctx, *temp_ctx;
    1772           0 :   u32 thread_index = vlib_get_thread_index ();
    1773             : 
    1774             :   QUIC_DBG (2, "Transferring conn %u to thread %u", ctx_index, dest_thread);
    1775             : 
    1776           0 :   temp_ctx = clib_mem_alloc (sizeof (quic_ctx_t));
    1777           0 :   QUIC_ASSERT (temp_ctx != NULL);
    1778           0 :   ctx = quic_ctx_get (ctx_index, thread_index);
    1779             : 
    1780           0 :   clib_memcpy (temp_ctx, ctx, sizeof (quic_ctx_t));
    1781             : 
    1782           0 :   quic_stop_ctx_timer (ctx);
    1783           0 :   quic_release_crypto_context (ctx->crypto_context_index, thread_index);
    1784           0 :   quic_ctx_free (ctx);
    1785             : 
    1786             :   /*  Send connection to destination thread */
    1787           0 :   session_send_rpc_evt_to_thread (dest_thread, quic_receive_connection,
    1788             :                                   (void *) temp_ctx);
    1789           0 : }
    1790             : 
    1791             : static int
    1792          18 : quic_udp_session_connected_callback (u32 quic_app_index, u32 ctx_index,
    1793             :                                      session_t * udp_session,
    1794             :                                      session_error_t err)
    1795             : {
    1796             :   QUIC_DBG (2, "UDP Session is now connected (id %u)",
    1797             :             udp_session->session_index);
    1798             :   /* This should always be called before quic_connect returns since UDP always
    1799             :    * connects instantly. */
    1800             :   clib_bihash_kv_16_8_t kv;
    1801             :   struct sockaddr_in6 sa6;
    1802          18 :   struct sockaddr *sa = (struct sockaddr *) &sa6;
    1803             :   socklen_t salen;
    1804             :   transport_connection_t *tc;
    1805             :   app_worker_t *app_wrk;
    1806             :   quicly_conn_t *conn;
    1807             :   quic_ctx_t *ctx;
    1808             :   u32 thread_index;
    1809             :   int ret;
    1810             :   quicly_context_t *quicly_ctx;
    1811             : 
    1812             :   /* Allocate session on whatever thread udp used, i.e., probably first
    1813             :    * worker, although this may be main thread. If it is main, it's done
    1814             :    * with a worker barrier */
    1815          18 :   thread_index = udp_session->thread_index;
    1816          18 :   ASSERT (thread_index == 0 || thread_index == 1);
    1817          18 :   ctx = quic_ctx_get (ctx_index, thread_index);
    1818          18 :   if (err)
    1819             :     {
    1820             :       u32 api_context;
    1821           0 :       app_wrk = app_worker_get_if_valid (ctx->parent_app_wrk_id);
    1822           0 :       if (app_wrk)
    1823             :         {
    1824           0 :           api_context = ctx->c_s_index;
    1825           0 :           app_worker_connect_notify (app_wrk, 0, SESSION_E_NONE, api_context);
    1826             :         }
    1827           0 :       return 0;
    1828             :     }
    1829             : 
    1830             :   QUIC_DBG (2, "New ctx [%u]%x", thread_index, (ctx) ? ctx_index : ~0);
    1831             : 
    1832          18 :   ctx->udp_session_handle = session_handle (udp_session);
    1833          18 :   udp_session->opaque = ctx_index;
    1834             : 
    1835             :   /* Init QUIC lib connection
    1836             :    * Generate required sockaddr & salen */
    1837          18 :   tc = session_get_transport (udp_session);
    1838          18 :   quic_build_sockaddr (sa, &salen, &tc->rmt_ip, tc->rmt_port, tc->is_ip4);
    1839             : 
    1840          18 :   quicly_ctx = quic_get_quicly_ctx_from_ctx (ctx);
    1841          18 :   ret = quicly_connect (&ctx->conn, quicly_ctx, (char *) ctx->srv_hostname,
    1842          18 :                         sa, NULL, &quic_main.wrk_ctx[thread_index].next_cid,
    1843             :                         ptls_iovec_init (NULL, 0), &quic_main.hs_properties,
    1844             :                         NULL);
    1845          18 :   ++quic_main.wrk_ctx[thread_index].next_cid.master_id;
    1846             :   /*  Save context handle in quicly connection */
    1847          18 :   quic_store_conn_ctx (ctx->conn, ctx);
    1848          18 :   assert (ret == 0);
    1849             : 
    1850             :   /*  Register connection in connections map */
    1851          18 :   conn = ctx->conn;
    1852          18 :   quic_make_connection_key (&kv, quicly_get_master_id (conn));
    1853          18 :   kv.value = ((u64) thread_index) << 32 | (u64) ctx_index;
    1854             :   QUIC_DBG (2, "Registering conn with id %lu %lu", kv.key[0], kv.key[1]);
    1855          18 :   clib_bihash_add_del_16_8 (&quic_main.connection_hash, &kv, 1 /* is_add */ );
    1856             : 
    1857          18 :   quic_send_packets (ctx);
    1858             : 
    1859          18 :   return ret;
    1860             : }
    1861             : 
    1862             : static void
    1863           0 : quic_udp_session_disconnect_callback (session_t * s)
    1864             : {
    1865           0 :   clib_warning ("UDP session disconnected???");
    1866           0 : }
    1867             : 
    1868             : static void
    1869          72 : quic_udp_session_cleanup_callback (session_t * udp_session,
    1870             :                                    session_cleanup_ntf_t ntf)
    1871             : {
    1872             :   quic_ctx_t *ctx;
    1873             : 
    1874          72 :   if (ntf != SESSION_CLEANUP_SESSION)
    1875          36 :     return;
    1876             : 
    1877          36 :   ctx = quic_ctx_get (udp_session->opaque, udp_session->thread_index);
    1878          36 :   quic_stop_ctx_timer (ctx);
    1879          36 :   quic_release_crypto_context (ctx->crypto_context_index,
    1880          36 :                                ctx->c_thread_index);
    1881          36 :   quic_ctx_free (ctx);
    1882             : }
    1883             : 
    1884             : static void
    1885           0 : quic_udp_session_reset_callback (session_t * s)
    1886             : {
    1887           0 :   clib_warning ("UDP session reset???");
    1888           0 : }
    1889             : 
    1890             : static void
    1891           0 : quic_udp_session_migrate_callback (session_t * s, session_handle_t new_sh)
    1892             : {
    1893           0 :   u32 new_thread = session_thread_from_handle (new_sh);
    1894             :   quic_ctx_t *ctx;
    1895             : 
    1896             :   QUIC_DBG (2, "Session %x migrated to %lx", s->session_index, new_sh);
    1897           0 :   QUIC_ASSERT (vlib_get_thread_index () == s->thread_index);
    1898           0 :   ctx = quic_ctx_get (s->opaque, s->thread_index);
    1899           0 :   QUIC_ASSERT (ctx->udp_session_handle == session_handle (s));
    1900             : 
    1901           0 :   ctx->udp_session_handle = new_sh;
    1902             : #if QUIC_DEBUG >= 1
    1903             :   s->opaque = 0xfeedface;
    1904             : #endif
    1905           0 :   quic_transfer_connection (ctx->c_c_index, new_thread);
    1906           0 : }
    1907             : 
    1908             : int
    1909          18 : quic_udp_session_accepted_callback (session_t * udp_session)
    1910             : {
    1911             :   /* New UDP connection, try to accept it */
    1912             :   u32 ctx_index;
    1913             :   quic_ctx_t *ctx, *lctx;
    1914             :   session_t *udp_listen_session;
    1915          18 :   u32 thread_index = vlib_get_thread_index ();
    1916             : 
    1917             :   udp_listen_session =
    1918          18 :     listen_session_get_from_handle (udp_session->listener_handle);
    1919             : 
    1920          18 :   ctx_index = quic_ctx_alloc (thread_index);
    1921          18 :   ctx = quic_ctx_get (ctx_index, thread_index);
    1922          18 :   ctx->c_thread_index = udp_session->thread_index;
    1923          18 :   ctx->c_c_index = ctx_index;
    1924          18 :   ctx->c_s_index = QUIC_SESSION_INVALID;
    1925          18 :   ctx->udp_session_handle = session_handle (udp_session);
    1926             :   QUIC_DBG (2, "ACCEPTED UDP 0x%lx", ctx->udp_session_handle);
    1927          18 :   ctx->listener_ctx_id = udp_listen_session->opaque;
    1928          18 :   lctx = quic_ctx_get (udp_listen_session->opaque,
    1929          18 :                        udp_listen_session->thread_index);
    1930          18 :   ctx->udp_is_ip4 = lctx->c_is_ip4;
    1931          18 :   ctx->parent_app_id = lctx->parent_app_id;
    1932          18 :   ctx->parent_app_wrk_id = lctx->parent_app_wrk_id;
    1933          18 :   ctx->timer_handle = QUIC_TIMER_HANDLE_INVALID;
    1934          18 :   ctx->conn_state = QUIC_CONN_STATE_OPENED;
    1935          18 :   ctx->c_flags |= TRANSPORT_CONNECTION_F_NO_LOOKUP;
    1936             : 
    1937          18 :   ctx->crypto_engine = lctx->crypto_engine;
    1938          18 :   ctx->ckpair_index = lctx->ckpair_index;
    1939          18 :   quic_acquire_crypto_context (ctx);
    1940          18 :   udp_session->opaque = ctx_index;
    1941          18 :   udp_session->session_state = SESSION_STATE_READY;
    1942             : 
    1943             :   /* TODO timeout to delete these if they never connect */
    1944          18 :   return 0;
    1945             : }
    1946             : 
    1947             : static int
    1948           5 : quic_add_segment_callback (u32 client_index, u64 seg_handle)
    1949             : {
    1950             :   /* No-op for builtin */
    1951           5 :   return 0;
    1952             : }
    1953             : 
    1954             : static int
    1955           0 : quic_del_segment_callback (u32 client_index, u64 seg_handle)
    1956             : {
    1957             :   /* No-op for builtin */
    1958           0 :   return 0;
    1959             : }
    1960             : 
    1961             : static int
    1962         617 : quic_custom_app_rx_callback (transport_connection_t * tc)
    1963             : {
    1964             :   quic_ctx_t *ctx;
    1965         617 :   session_t *stream_session = session_get (tc->s_index, tc->thread_index);
    1966             :   QUIC_DBG (3, "Received app READ notification");
    1967         617 :   quic_ack_rx_data (stream_session);
    1968         617 :   svm_fifo_reset_has_deq_ntf (stream_session->rx_fifo);
    1969             : 
    1970             :   /* Need to send packets (acks may never be sent otherwise) */
    1971         617 :   ctx = quic_ctx_get (stream_session->connection_index,
    1972         617 :                       stream_session->thread_index);
    1973         617 :   quic_send_packets (ctx);
    1974         617 :   return 0;
    1975             : }
    1976             : 
    1977             : static int
    1978       62098 : quic_custom_tx_callback (void *s, transport_send_params_t * sp)
    1979             : {
    1980       62098 :   session_t *stream_session = (session_t *) s;
    1981             :   quic_stream_data_t *stream_data;
    1982             :   quicly_stream_t *stream;
    1983             :   quic_ctx_t *ctx;
    1984             :   u32 max_deq;
    1985             :   int rv;
    1986             : 
    1987       62098 :   if (PREDICT_FALSE
    1988             :       (stream_session->session_state >= SESSION_STATE_TRANSPORT_CLOSING))
    1989           4 :     return 0;
    1990       62094 :   ctx = quic_ctx_get (stream_session->connection_index,
    1991       62094 :                       stream_session->thread_index);
    1992       62094 :   if (PREDICT_FALSE (!quic_ctx_is_stream (ctx)))
    1993             :     {
    1994           0 :       goto tx_end;              /* Most probably a reschedule */
    1995             :     }
    1996             : 
    1997             :   QUIC_DBG (3, "Stream TX event");
    1998       62094 :   quic_ack_rx_data (stream_session);
    1999       62094 :   stream = ctx->stream;
    2000       62094 :   if (!quicly_sendstate_is_open (&stream->sendstate))
    2001             :     {
    2002           0 :       QUIC_ERR ("Warning: tried to send on closed stream");
    2003           0 :       return 0;
    2004             :     }
    2005             : 
    2006       62094 :   stream_data = (quic_stream_data_t *) stream->data;
    2007       62094 :   max_deq = svm_fifo_max_dequeue (stream_session->tx_fifo);
    2008       62094 :   QUIC_ASSERT (max_deq >= stream_data->app_tx_data_len);
    2009       62094 :   if (max_deq == stream_data->app_tx_data_len)
    2010             :     {
    2011             :       QUIC_DBG (3, "TX but no data %d / %d", max_deq,
    2012             :                 stream_data->app_tx_data_len);
    2013       58212 :       return 0;
    2014             :     }
    2015        3882 :   stream_data->app_tx_data_len = max_deq;
    2016        3882 :   rv = quicly_stream_sync_sendbuf (stream, 1);
    2017        3882 :   QUIC_ASSERT (!rv);
    2018             : 
    2019        3882 : tx_end:
    2020        3882 :   return quic_send_packets (ctx);
    2021             : }
    2022             : 
    2023             : /*
    2024             :  * Returns 0 if a matching connection is found and is on the right thread.
    2025             :  * Otherwise returns -1.
    2026             :  * If a connection is found, even on the wrong thread, ctx_thread and ctx_index
    2027             :  * will be set.
    2028             :  */
    2029             : static inline int
    2030       50496 : quic_find_packet_ctx (quic_rx_packet_ctx_t * pctx, u32 caller_thread_index)
    2031             : {
    2032             :   clib_bihash_kv_16_8_t kv;
    2033             :   clib_bihash_16_8_t *h;
    2034             :   quic_ctx_t *ctx;
    2035             :   u32 index, thread_id;
    2036             : 
    2037       50496 :   h = &quic_main.connection_hash;
    2038       50496 :   quic_make_connection_key (&kv, &pctx->packet.cid.dest.plaintext);
    2039             :   QUIC_DBG (3, "Searching conn with id %lu %lu", kv.key[0], kv.key[1]);
    2040             : 
    2041       50496 :   if (clib_bihash_search_16_8 (h, &kv, &kv))
    2042             :     {
    2043             :       QUIC_DBG (3, "connection not found");
    2044          18 :       return QUIC_PACKET_TYPE_NONE;
    2045             :     }
    2046             : 
    2047       50478 :   index = kv.value & UINT32_MAX;
    2048       50478 :   thread_id = kv.value >> 32;
    2049             :   /* Check if this connection belongs to this thread, otherwise
    2050             :    * ask for it to be moved */
    2051       50478 :   if (thread_id != caller_thread_index)
    2052             :     {
    2053             :       QUIC_DBG (2, "Connection is on wrong thread");
    2054             :       /* Cannot make full check with quicly_is_destination... */
    2055           0 :       pctx->ctx_index = index;
    2056           0 :       pctx->thread_index = thread_id;
    2057           0 :       return QUIC_PACKET_TYPE_MIGRATE;
    2058             :     }
    2059       50478 :   ctx = quic_ctx_get (index, vlib_get_thread_index ());
    2060       50478 :   if (!ctx->conn)
    2061             :     {
    2062           0 :       QUIC_ERR ("ctx has no conn");
    2063           0 :       return QUIC_PACKET_TYPE_NONE;
    2064             :     }
    2065       50478 :   if (!quicly_is_destination (ctx->conn, NULL, &pctx->sa, &pctx->packet))
    2066           0 :     return QUIC_PACKET_TYPE_NONE;
    2067             : 
    2068             :   QUIC_DBG (3, "Connection found");
    2069       50478 :   pctx->ctx_index = index;
    2070       50478 :   pctx->thread_index = thread_id;
    2071       50478 :   return QUIC_PACKET_TYPE_RECEIVE;
    2072             : }
    2073             : 
    2074             : static void
    2075          18 : quic_accept_connection (quic_rx_packet_ctx_t * pctx)
    2076             : {
    2077             :   quicly_context_t *quicly_ctx;
    2078             :   session_t *quic_session;
    2079             :   clib_bihash_kv_16_8_t kv;
    2080             :   app_worker_t *app_wrk;
    2081             :   quicly_conn_t *conn;
    2082             :   quic_ctx_t *ctx;
    2083             :   quic_ctx_t *lctx;
    2084             :   int rv;
    2085             : 
    2086             :   /* new connection, accept and create context if packet is valid
    2087             :    * TODO: check if socket is actually listening? */
    2088          18 :   ctx = quic_ctx_get (pctx->ctx_index, pctx->thread_index);
    2089          18 :   if (ctx->c_s_index != QUIC_SESSION_INVALID)
    2090             :     {
    2091             :       QUIC_DBG (2, "already accepted ctx 0x%x", ctx->c_s_index);
    2092           0 :       return;
    2093             :     }
    2094             : 
    2095          18 :   quicly_ctx = quic_get_quicly_ctx_from_ctx (ctx);
    2096          18 :   if ((rv = quicly_accept (&conn, quicly_ctx, NULL, &pctx->sa,
    2097             :                            &pctx->packet, NULL,
    2098          18 :                            &quic_main.wrk_ctx[pctx->thread_index].next_cid,
    2099             :                            NULL)))
    2100             :     {
    2101             :       /* Invalid packet, pass */
    2102           0 :       assert (conn == NULL);
    2103           0 :       QUIC_ERR ("Accept failed with %U", quic_format_err, rv);
    2104             :       /* TODO: cleanup created quic ctx and UDP session */
    2105           0 :       return;
    2106             :     }
    2107          18 :   assert (conn != NULL);
    2108             : 
    2109          18 :   ++quic_main.wrk_ctx[pctx->thread_index].next_cid.master_id;
    2110             :   /* Save ctx handle in quicly connection */
    2111          18 :   quic_store_conn_ctx (conn, ctx);
    2112          18 :   ctx->conn = conn;
    2113             : 
    2114          18 :   quic_session = session_alloc (ctx->c_thread_index);
    2115             :   QUIC_DBG (2, "Allocated quic_session, 0x%lx ctx %u",
    2116             :             session_handle (quic_session), ctx->c_c_index);
    2117          18 :   ctx->c_s_index = quic_session->session_index;
    2118             : 
    2119          18 :   lctx = quic_ctx_get (ctx->listener_ctx_id, 0);
    2120             : 
    2121          18 :   quic_session->app_wrk_index = lctx->parent_app_wrk_id;
    2122          18 :   quic_session->connection_index = ctx->c_c_index;
    2123          18 :   quic_session->session_type =
    2124          18 :     session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC, ctx->udp_is_ip4);
    2125          18 :   quic_session->listener_handle = lctx->c_s_index;
    2126             : 
    2127             :   /* Register connection in connections map */
    2128          18 :   quic_make_connection_key (&kv, quicly_get_master_id (conn));
    2129          18 :   kv.value = ((u64) pctx->thread_index) << 32 | (u64) pctx->ctx_index;
    2130          18 :   clib_bihash_add_del_16_8 (&quic_main.connection_hash, &kv, 1 /* is_add */ );
    2131             :   QUIC_DBG (2, "Registering conn with id %lu %lu", kv.key[0], kv.key[1]);
    2132             : 
    2133             :   /* If notify fails, reset connection immediatly */
    2134          18 :   if ((rv = app_worker_init_accepted (quic_session)))
    2135             :     {
    2136           0 :       QUIC_ERR ("failed to allocate fifos");
    2137           0 :       quic_proto_on_close (pctx->ctx_index, pctx->thread_index);
    2138           0 :       return;
    2139             :     }
    2140             : 
    2141          18 :   app_wrk = app_worker_get (quic_session->app_wrk_index);
    2142          18 :   quic_session->session_state = SESSION_STATE_ACCEPTING;
    2143          18 :   if ((rv = app_worker_accept_notify (app_wrk, quic_session)))
    2144             :     {
    2145           0 :       QUIC_ERR ("failed to notify accept worker app");
    2146           0 :       quic_proto_on_close (pctx->ctx_index, pctx->thread_index);
    2147           0 :       return;
    2148             :     }
    2149             : 
    2150          18 :   ctx->conn_state = QUIC_CONN_STATE_READY;
    2151             : }
    2152             : 
    2153             : static int
    2154           0 : quic_reset_connection (u64 udp_session_handle, quic_rx_packet_ctx_t * pctx)
    2155           0 : {
    2156             :   /* short header packet; potentially a dead connection. No need to check the
    2157             :    * length of the incoming packet, because loop is prevented by authenticating
    2158             :    * the CID (by checking node_id and thread_id). If the peer is also sending a
    2159             :    * reset, then the next CID is highly likely to contain a non-authenticating
    2160             :    * CID, ... */
    2161             :   QUIC_DBG (2, "Sending stateless reset");
    2162             :   int rv;
    2163             :   session_t *udp_session;
    2164             :   quicly_context_t *quicly_ctx;
    2165           0 :   if (pctx->packet.cid.dest.plaintext.node_id != 0
    2166           0 :       || pctx->packet.cid.dest.plaintext.thread_id != 0)
    2167           0 :     return 0;
    2168           0 :   quicly_ctx = quic_get_quicly_ctx_from_udp (udp_session_handle);
    2169           0 :   quic_ctx_t *qctx = quic_ctx_get (pctx->ctx_index, pctx->thread_index);
    2170             : 
    2171             :   quicly_address_t src;
    2172           0 :   uint8_t payload[quicly_ctx->transport_params.max_udp_payload_size];
    2173             :   size_t payload_len =
    2174           0 :     quicly_send_stateless_reset (quicly_ctx, &src.sa, payload);
    2175           0 :   if (payload_len == 0)
    2176           0 :     return 1;
    2177             : 
    2178             :   struct iovec packet;
    2179           0 :   packet.iov_len = payload_len;
    2180           0 :   packet.iov_base = payload;
    2181             : 
    2182           0 :   struct _st_quicly_conn_public_t *conn =
    2183             :     (struct _st_quicly_conn_public_t *) qctx->conn;
    2184             : 
    2185           0 :   udp_session = session_get_from_handle (udp_session_handle);
    2186           0 :   rv = quic_send_datagram (udp_session, &packet, &conn->remote.address,
    2187             :                            &conn->local.address);
    2188           0 :   quic_set_udp_tx_evt (udp_session);
    2189           0 :   return rv;
    2190             : }
    2191             : 
    2192             : static int
    2193       50496 : quic_process_one_rx_packet (u64 udp_session_handle, svm_fifo_t * f,
    2194             :                             u32 fifo_offset, quic_rx_packet_ctx_t * pctx)
    2195             : {
    2196             :   size_t plen;
    2197             :   u32 full_len, ret;
    2198       50496 :   u32 thread_index = vlib_get_thread_index ();
    2199       50496 :   u32 cur_deq = svm_fifo_max_dequeue (f) - fifo_offset;
    2200             :   quicly_context_t *quicly_ctx;
    2201             :   session_t *udp_session;
    2202             :   int rv;
    2203             : 
    2204      100992 :   ret = svm_fifo_peek (f, fifo_offset,
    2205       50496 :                        SESSION_CONN_HDR_LEN, (u8 *) & pctx->ph);
    2206       50496 :   QUIC_ASSERT (ret == SESSION_CONN_HDR_LEN);
    2207       50496 :   QUIC_ASSERT (pctx->ph.data_offset == 0);
    2208       50496 :   full_len = pctx->ph.data_length + SESSION_CONN_HDR_LEN;
    2209       50496 :   if (full_len > cur_deq)
    2210             :     {
    2211           0 :       QUIC_ERR ("Not enough data in fifo RX");
    2212           0 :       return 1;
    2213             :     }
    2214             : 
    2215             :   /* Quicly can read len bytes from the fifo at offset:
    2216             :    * ph.data_offset + SESSION_CONN_HDR_LEN */
    2217      100992 :   ret = svm_fifo_peek (f, SESSION_CONN_HDR_LEN + fifo_offset,
    2218       50496 :                        pctx->ph.data_length, pctx->data);
    2219       50496 :   if (ret != pctx->ph.data_length)
    2220             :     {
    2221           0 :       QUIC_ERR ("Not enough data peeked in RX");
    2222           0 :       return 1;
    2223             :     }
    2224             : 
    2225       50496 :   quic_increment_counter (QUIC_ERROR_RX_PACKETS, 1);
    2226       50496 :   quic_build_sockaddr (&pctx->sa, &pctx->salen, &pctx->ph.rmt_ip,
    2227       50496 :                        pctx->ph.rmt_port, pctx->ph.is_ip4);
    2228       50496 :   quicly_ctx = quic_get_quicly_ctx_from_udp (udp_session_handle);
    2229             : 
    2230       50496 :   size_t off = 0;
    2231       50496 :   plen = quicly_decode_packet (quicly_ctx, &pctx->packet, pctx->data,
    2232       50496 :                                pctx->ph.data_length, &off);
    2233             : 
    2234       50496 :   if (plen == SIZE_MAX)
    2235             :     {
    2236           0 :       return 1;
    2237             :     }
    2238             : 
    2239       50496 :   rv = quic_find_packet_ctx (pctx, thread_index);
    2240       50496 :   if (rv == QUIC_PACKET_TYPE_RECEIVE)
    2241             :     {
    2242       50478 :       pctx->ptype = QUIC_PACKET_TYPE_RECEIVE;
    2243             : 
    2244       50478 :       if (quic_main.vnet_crypto_enabled &&
    2245       50478 :           quic_main.default_crypto_engine == CRYPTO_ENGINE_VPP)
    2246             :         {
    2247       50478 :           quic_ctx_t *qctx = quic_ctx_get (pctx->ctx_index, thread_index);
    2248       50478 :           quic_crypto_decrypt_packet (qctx, pctx);
    2249             :         }
    2250       50478 :       return 0;
    2251             :     }
    2252          18 :   else if (rv == QUIC_PACKET_TYPE_MIGRATE)
    2253             :     {
    2254           0 :       pctx->ptype = QUIC_PACKET_TYPE_MIGRATE;
    2255             :       /*  Connection found but on wrong thread, ask move */
    2256             :     }
    2257          18 :   else if (QUICLY_PACKET_IS_LONG_HEADER (pctx->packet.octets.base[0]))
    2258             :     {
    2259          18 :       pctx->ptype = QUIC_PACKET_TYPE_ACCEPT;
    2260          18 :       udp_session = session_get_from_handle (udp_session_handle);
    2261          18 :       pctx->ctx_index = udp_session->opaque;
    2262          18 :       pctx->thread_index = thread_index;
    2263             :     }
    2264             :   else
    2265             :     {
    2266           0 :       pctx->ptype = QUIC_PACKET_TYPE_RESET;
    2267             :     }
    2268          18 :   return 1;
    2269             : }
    2270             : 
    2271             : static int
    2272        8008 : quic_udp_session_rx_callback (session_t * udp_session)
    2273             : {
    2274             :   /*  Read data from UDP rx_fifo and pass it to the quicly conn. */
    2275        8008 :   quic_ctx_t *ctx = NULL, *prev_ctx = NULL;
    2276        8008 :   svm_fifo_t *f = udp_session->rx_fifo;
    2277             :   u32 max_deq;
    2278        8008 :   u64 udp_session_handle = session_handle (udp_session);
    2279        8008 :   int rv = 0;
    2280        8008 :   u32 thread_index = vlib_get_thread_index ();
    2281             :   u32 cur_deq, fifo_offset, max_packets, i;
    2282             : 
    2283             :   quic_rx_packet_ctx_t packets_ctx[QUIC_RCV_MAX_PACKETS];
    2284             : 
    2285        8008 :   if (udp_session->flags & SESSION_F_IS_MIGRATING)
    2286             :     {
    2287             :       QUIC_DBG (3, "RX on migrating udp session");
    2288           0 :       return 0;
    2289             :     }
    2290             : 
    2291        8008 : rx_start:
    2292        8589 :   max_deq = svm_fifo_max_dequeue (f);
    2293        8589 :   if (max_deq == 0)
    2294           0 :     return 0;
    2295             : 
    2296        8589 :   fifo_offset = 0;
    2297        8589 :   max_packets = QUIC_RCV_MAX_PACKETS;
    2298             : 
    2299             : #if CLIB_DEBUG > 0
    2300        8589 :   clib_memset (packets_ctx, 0xfa,
    2301             :                QUIC_RCV_MAX_PACKETS * sizeof (quic_rx_packet_ctx_t));
    2302             : #endif
    2303       59067 :   for (i = 0; i < max_packets; i++)
    2304             :     {
    2305       58404 :       packets_ctx[i].thread_index = UINT32_MAX;
    2306       58404 :       packets_ctx[i].ctx_index = UINT32_MAX;
    2307       58404 :       packets_ctx[i].ptype = QUIC_PACKET_TYPE_DROP;
    2308             : 
    2309       58404 :       cur_deq = max_deq - fifo_offset;
    2310       58404 :       if (cur_deq == 0)
    2311             :         {
    2312        7908 :           max_packets = i + 1;
    2313        7908 :           break;
    2314             :         }
    2315       50496 :       if (cur_deq < SESSION_CONN_HDR_LEN)
    2316             :         {
    2317           0 :           fifo_offset = max_deq;
    2318           0 :           max_packets = i + 1;
    2319           0 :           QUIC_ERR ("Fifo %d < header size in RX", cur_deq);
    2320           0 :           break;
    2321             :         }
    2322       50496 :       rv = quic_process_one_rx_packet (udp_session_handle, f,
    2323             :                                        fifo_offset, &packets_ctx[i]);
    2324       50496 :       if (packets_ctx[i].ptype != QUIC_PACKET_TYPE_MIGRATE)
    2325       50496 :         fifo_offset += SESSION_CONN_HDR_LEN + packets_ctx[i].ph.data_length;
    2326       50496 :       if (rv)
    2327             :         {
    2328          18 :           max_packets = i + 1;
    2329          18 :           break;
    2330             :         }
    2331             :     }
    2332             : 
    2333       66993 :   for (i = 0; i < max_packets; i++)
    2334             :     {
    2335       58404 :       switch (packets_ctx[i].ptype)
    2336             :         {
    2337       50478 :         case QUIC_PACKET_TYPE_RECEIVE:
    2338       50478 :           ctx = quic_ctx_get (packets_ctx[i].ctx_index, thread_index);
    2339       50478 :           rv = quicly_receive (ctx->conn, NULL, &packets_ctx[i].sa,
    2340             :                                &packets_ctx[i].packet);
    2341       50478 :           if (rv && rv != QUICLY_ERROR_PACKET_IGNORED)
    2342             :             {
    2343           0 :               QUIC_ERR ("quicly_receive return error %U",
    2344             :                         quic_format_err, rv);
    2345             :             }
    2346       50478 :           break;
    2347          18 :         case QUIC_PACKET_TYPE_ACCEPT:
    2348          18 :           quic_accept_connection (&packets_ctx[i]);
    2349          18 :           break;
    2350           0 :         case QUIC_PACKET_TYPE_RESET:
    2351           0 :           quic_reset_connection (udp_session_handle, &packets_ctx[i]);
    2352           0 :           break;
    2353             :         }
    2354       58404 :     }
    2355        8589 :   ctx = prev_ctx = NULL;
    2356       66993 :   for (i = 0; i < max_packets; i++)
    2357             :     {
    2358       58404 :       prev_ctx = ctx;
    2359       58404 :       switch (packets_ctx[i].ptype)
    2360             :         {
    2361       50478 :         case QUIC_PACKET_TYPE_RECEIVE:
    2362       50478 :           ctx = quic_ctx_get (packets_ctx[i].ctx_index,
    2363             :                               packets_ctx[i].thread_index);
    2364       50478 :           quic_check_quic_session_connected (ctx);
    2365       50478 :           ctx = quic_ctx_get (packets_ctx[i].ctx_index,
    2366             :                               packets_ctx[i].thread_index);
    2367       50478 :           break;
    2368          18 :         case QUIC_PACKET_TYPE_ACCEPT:
    2369          18 :           ctx = quic_ctx_get (packets_ctx[i].ctx_index,
    2370             :                               packets_ctx[i].thread_index);
    2371          18 :           break;
    2372        7908 :         default:
    2373        7908 :           continue;             /* this exits the for loop since other packet types are
    2374             :                                    necessarily the last in the batch */
    2375             :         }
    2376       50496 :       if (ctx != prev_ctx)
    2377        8589 :         quic_send_packets (ctx);
    2378             :     }
    2379             : 
    2380        8589 :   udp_session = session_get_from_handle (udp_session_handle);   /*  session alloc might have happened */
    2381        8589 :   f = udp_session->rx_fifo;
    2382        8589 :   svm_fifo_dequeue_drop (f, fifo_offset);
    2383             : 
    2384        8589 :   if (svm_fifo_max_dequeue (f))
    2385         581 :     goto rx_start;
    2386             : 
    2387        8008 :   return 0;
    2388             : }
    2389             : 
    2390             : always_inline void
    2391          12 : quic_common_get_transport_endpoint (quic_ctx_t * ctx,
    2392             :                                     transport_endpoint_t * tep, u8 is_lcl)
    2393             : {
    2394             :   session_t *udp_session;
    2395          12 :   if (!quic_ctx_is_stream (ctx))
    2396             :     {
    2397           6 :       udp_session = session_get_from_handle (ctx->udp_session_handle);
    2398           6 :       session_get_endpoint (udp_session, tep, is_lcl);
    2399             :     }
    2400          12 : }
    2401             : 
    2402             : static void
    2403           3 : quic_get_transport_listener_endpoint (u32 listener_index,
    2404             :                                       transport_endpoint_t * tep, u8 is_lcl)
    2405             : {
    2406             :   quic_ctx_t *ctx;
    2407             :   app_listener_t *app_listener;
    2408             :   session_t *udp_listen_session;
    2409           3 :   ctx = quic_ctx_get (listener_index, vlib_get_thread_index ());
    2410           3 :   if (quic_ctx_is_listener (ctx))
    2411             :     {
    2412           2 :       app_listener = app_listener_get_w_handle (ctx->udp_session_handle);
    2413           2 :       udp_listen_session = app_listener_get_session (app_listener);
    2414           2 :       return session_get_endpoint (udp_listen_session, tep, is_lcl);
    2415             :     }
    2416           1 :   quic_common_get_transport_endpoint (ctx, tep, is_lcl);
    2417             : }
    2418             : 
    2419             : static void
    2420          11 : quic_get_transport_endpoint (u32 ctx_index, u32 thread_index,
    2421             :                              transport_endpoint_t * tep, u8 is_lcl)
    2422             : {
    2423             :   quic_ctx_t *ctx;
    2424          11 :   ctx = quic_ctx_get (ctx_index, thread_index);
    2425          11 :   quic_common_get_transport_endpoint (ctx, tep, is_lcl);
    2426          11 : }
    2427             : 
    2428             : /* *INDENT-OFF* */
    2429             : static session_cb_vft_t quic_app_cb_vft = {
    2430             :   .session_accept_callback = quic_udp_session_accepted_callback,
    2431             :   .session_disconnect_callback = quic_udp_session_disconnect_callback,
    2432             :   .session_connected_callback = quic_udp_session_connected_callback,
    2433             :   .session_reset_callback = quic_udp_session_reset_callback,
    2434             :   .session_migrate_callback = quic_udp_session_migrate_callback,
    2435             :   .add_segment_callback = quic_add_segment_callback,
    2436             :   .del_segment_callback = quic_del_segment_callback,
    2437             :   .builtin_app_rx_callback = quic_udp_session_rx_callback,
    2438             :   .session_cleanup_callback = quic_udp_session_cleanup_callback,
    2439             :   .app_cert_key_pair_delete_callback = quic_app_cert_key_pair_delete_callback,
    2440             : };
    2441             : 
    2442             : static const transport_proto_vft_t quic_proto = {
    2443             :   .connect = quic_connect,
    2444             :   .close = quic_proto_on_close,
    2445             :   .start_listen = quic_start_listen,
    2446             :   .stop_listen = quic_stop_listen,
    2447             :   .get_connection = quic_connection_get,
    2448             :   .get_listener = quic_listener_get,
    2449             :   .update_time = quic_update_time,
    2450             :   .app_rx_evt = quic_custom_app_rx_callback,
    2451             :   .custom_tx = quic_custom_tx_callback,
    2452             :   .format_connection = format_quic_connection,
    2453             :   .format_half_open = format_quic_half_open,
    2454             :   .format_listener = format_quic_listener,
    2455             :   .get_transport_endpoint = quic_get_transport_endpoint,
    2456             :   .get_transport_listener_endpoint = quic_get_transport_listener_endpoint,
    2457             :   .transport_options = {
    2458             :     .name = "quic",
    2459             :     .short_name = "Q",
    2460             :     .tx_type = TRANSPORT_TX_INTERNAL,
    2461             :     .service_type = TRANSPORT_SERVICE_APP,
    2462             :   },
    2463             : };
    2464             : /* *INDENT-ON* */
    2465             : 
    2466             : static quicly_stream_open_t on_stream_open = { quic_on_stream_open };
    2467             : static quicly_closed_by_remote_t on_closed_by_remote = {
    2468             :   quic_on_closed_by_remote
    2469             : };
    2470             : static quicly_now_t quicly_vpp_now_cb = { quic_get_time };
    2471             : 
    2472             : static void
    2473          50 : quic_register_cipher_suite (crypto_engine_type_t type,
    2474             :                             ptls_cipher_suite_t ** ciphers)
    2475             : {
    2476          50 :   quic_main_t *qm = &quic_main;
    2477          50 :   vec_validate (qm->quic_ciphers, type);
    2478          50 :   clib_bitmap_set (qm->available_crypto_engines, type, 1);
    2479          50 :   qm->quic_ciphers[type] = ciphers;
    2480          50 : }
    2481             : 
    2482             : static void
    2483           2 : quic_update_fifo_size ()
    2484             : {
    2485           2 :   quic_main_t *qm = &quic_main;
    2486             :   segment_manager_props_t *seg_mgr_props =
    2487           2 :     application_get_segment_manager_properties (qm->app_index);
    2488             : 
    2489           2 :   if (!seg_mgr_props)
    2490             :     {
    2491           0 :       clib_warning
    2492             :         ("error while getting segment_manager_props_t, can't update fifo-size");
    2493           0 :       return;
    2494             :     }
    2495             : 
    2496           2 :   seg_mgr_props->tx_fifo_size = qm->udp_fifo_size;
    2497           2 :   seg_mgr_props->rx_fifo_size = qm->udp_fifo_size;
    2498             : }
    2499             : 
    2500             : static clib_error_t *
    2501          25 : quic_init (vlib_main_t * vm)
    2502             : {
    2503          25 :   u32 segment_size = 256 << 20;
    2504          25 :   vlib_thread_main_t *vtm = vlib_get_thread_main ();
    2505             :   tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
    2506          25 :   vnet_app_attach_args_t _a, *a = &_a;
    2507             :   u64 options[APP_OPTIONS_N_OPTIONS];
    2508          25 :   quic_main_t *qm = &quic_main;
    2509             :   u32 num_threads, i;
    2510             : 
    2511          25 :   num_threads = 1 /* main thread */  + vtm->n_threads;
    2512             : 
    2513          25 :   clib_memset (a, 0, sizeof (*a));
    2514          25 :   clib_memset (options, 0, sizeof (options));
    2515             : 
    2516          25 :   a->session_cb_vft = &quic_app_cb_vft;
    2517          25 :   a->api_client_index = APP_INVALID_INDEX;
    2518          25 :   a->options = options;
    2519          25 :   a->name = format (0, "quic");
    2520          25 :   a->options[APP_OPTIONS_SEGMENT_SIZE] = segment_size;
    2521          25 :   a->options[APP_OPTIONS_ADD_SEGMENT_SIZE] = segment_size;
    2522          25 :   a->options[APP_OPTIONS_RX_FIFO_SIZE] = qm->udp_fifo_size;
    2523          25 :   a->options[APP_OPTIONS_TX_FIFO_SIZE] = qm->udp_fifo_size;
    2524          25 :   a->options[APP_OPTIONS_PREALLOC_FIFO_PAIRS] = qm->udp_fifo_prealloc;
    2525          25 :   a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
    2526          25 :   a->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
    2527          25 :   a->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_IS_TRANSPORT_APP;
    2528             : 
    2529          25 :   if (vnet_application_attach (a))
    2530             :     {
    2531           0 :       clib_warning ("failed to attach quic app");
    2532           0 :       return clib_error_return (0, "failed to attach quic app");
    2533             :     }
    2534             : 
    2535          25 :   vec_validate (qm->ctx_pool, num_threads - 1);
    2536          25 :   vec_validate (qm->wrk_ctx, num_threads - 1);
    2537             : 
    2538          71 :   for (i = 0; i < num_threads; i++)
    2539             :     {
    2540          46 :       qm->wrk_ctx[i].next_cid.thread_id = i;
    2541          46 :       tw = &qm->wrk_ctx[i].timer_wheel;
    2542          46 :       tw_timer_wheel_init_1t_3w_1024sl_ov (tw, quic_expired_timers_dispatch,
    2543             :                                            1e-3 /* timer period 1ms */ , ~0);
    2544          46 :       tw->last_run_time = vlib_time_now (vlib_get_main ());
    2545          46 :       clib_bihash_init_24_8 (&qm->wrk_ctx[i].crypto_context_hash,
    2546             :                              "quic crypto contexts", 64, 128 << 10);
    2547             :     }
    2548             : 
    2549          25 :   clib_bihash_init_16_8 (&qm->connection_hash, "quic connections", 1024,
    2550             :                          4 << 20);
    2551             : 
    2552          25 :   qm->app_index = a->app_index;
    2553          25 :   qm->tstamp_ticks_per_clock = vm->clib_time.seconds_per_clock
    2554          25 :     / QUIC_TSTAMP_RESOLUTION;
    2555          25 :   qm->session_cache.super.cb = quic_encrypt_ticket_cb;
    2556             : 
    2557          25 :   transport_register_protocol (TRANSPORT_PROTO_QUIC, &quic_proto,
    2558             :                                FIB_PROTOCOL_IP4, ~0);
    2559          25 :   transport_register_protocol (TRANSPORT_PROTO_QUIC, &quic_proto,
    2560             :                                FIB_PROTOCOL_IP6, ~0);
    2561             : 
    2562             :   quic_load_openssl3_legacy_provider ();
    2563          25 :   clib_bitmap_alloc (qm->available_crypto_engines,
    2564             :                      app_crypto_engine_n_types ());
    2565          25 :   quic_register_cipher_suite (CRYPTO_ENGINE_PICOTLS,
    2566             :                               ptls_openssl_cipher_suites);
    2567          25 :   qm->default_crypto_engine = CRYPTO_ENGINE_PICOTLS;
    2568             : 
    2569          25 :   vnet_crypto_main_t *cm = &crypto_main;
    2570          25 :   if (vec_len (cm->engines) == 0)
    2571           0 :     qm->vnet_crypto_enabled = 0;
    2572             :   else
    2573          25 :     qm->vnet_crypto_enabled = 1;
    2574          25 :   if (qm->vnet_crypto_enabled == 1)
    2575             :     {
    2576          25 :       u8 empty_key[32] = {};
    2577          25 :       quic_register_cipher_suite (CRYPTO_ENGINE_VPP,
    2578             :                                   quic_crypto_cipher_suites);
    2579          25 :       qm->default_crypto_engine = CRYPTO_ENGINE_VPP;
    2580          25 :       vec_validate (qm->per_thread_crypto_key_indices, num_threads);
    2581          71 :       for (i = 0; i < num_threads; i++)
    2582             :         {
    2583          46 :           qm->per_thread_crypto_key_indices[i] = vnet_crypto_key_add (
    2584             :             vm, VNET_CRYPTO_ALG_AES_256_CTR, empty_key, 32);
    2585             :         }
    2586             :     }
    2587             : 
    2588          25 :   qm->max_packets_per_key = DEFAULT_MAX_PACKETS_PER_KEY;
    2589          25 :   qm->default_quic_cc = QUIC_CC_RENO;
    2590             : 
    2591          25 :   vec_free (a->name);
    2592          25 :   return 0;
    2593             : }
    2594             : 
    2595          50 : VLIB_INIT_FUNCTION (quic_init);
    2596             : 
    2597             : static clib_error_t *
    2598           0 : quic_plugin_crypto_command_fn (vlib_main_t * vm,
    2599             :                                unformat_input_t * input,
    2600             :                                vlib_cli_command_t * cmd)
    2601             : {
    2602           0 :   unformat_input_t _line_input, *line_input = &_line_input;
    2603           0 :   quic_main_t *qm = &quic_main;
    2604           0 :   clib_error_t *e = 0;
    2605             : 
    2606           0 :   if (!unformat_user (input, unformat_line_input, line_input))
    2607           0 :     return 0;
    2608             : 
    2609           0 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    2610             :     {
    2611           0 :       if (unformat (line_input, "vpp"))
    2612           0 :         qm->default_crypto_engine = CRYPTO_ENGINE_VPP;
    2613           0 :       else if (unformat (line_input, "picotls"))
    2614           0 :         qm->default_crypto_engine = CRYPTO_ENGINE_PICOTLS;
    2615             :       else
    2616             :         {
    2617           0 :           e = clib_error_return (0, "unknown input '%U'",
    2618             :                                  format_unformat_error, line_input);
    2619           0 :           goto done;
    2620             :         }
    2621             :     }
    2622           0 : done:
    2623           0 :   unformat_free (line_input);
    2624           0 :   return e;
    2625             : }
    2626             : 
    2627             : u64 quic_fifosize = 0;
    2628             : static clib_error_t *
    2629           2 : quic_plugin_set_fifo_size_command_fn (vlib_main_t * vm,
    2630             :                                       unformat_input_t * input,
    2631             :                                       vlib_cli_command_t * cmd)
    2632             : {
    2633           2 :   quic_main_t *qm = &quic_main;
    2634           2 :   unformat_input_t _line_input, *line_input = &_line_input;
    2635             :   uword tmp;
    2636             : 
    2637           2 :   if (!unformat_user (input, unformat_line_input, line_input))
    2638           0 :     return 0;
    2639             : 
    2640           4 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    2641             :     {
    2642           2 :       if (unformat (line_input, "%U", unformat_memory_size, &tmp))
    2643             :         {
    2644           2 :           if (tmp >= 0x100000000ULL)
    2645             :             {
    2646           0 :               return clib_error_return
    2647             :                 (0, "fifo-size %llu (0x%llx) too large", tmp, tmp);
    2648             :             }
    2649           2 :           qm->udp_fifo_size = tmp;
    2650           2 :           quic_update_fifo_size ();
    2651             :         }
    2652             :       else
    2653           0 :         return clib_error_return (0, "unknown input '%U'",
    2654             :                                   format_unformat_error, line_input);
    2655             :     }
    2656             : 
    2657           2 :   return 0;
    2658             : }
    2659             : 
    2660             : static inline u64
    2661           0 : quic_get_counter_value (u32 event_code)
    2662             : {
    2663             :   vlib_node_t *n;
    2664             :   vlib_main_t *vm;
    2665             :   vlib_error_main_t *em;
    2666             : 
    2667             :   u32 code, i;
    2668           0 :   u64 c, sum = 0;
    2669             : 
    2670           0 :   vm = vlib_get_main ();
    2671           0 :   em = &vm->error_main;
    2672           0 :   n = vlib_get_node (vm, quic_input_node.index);
    2673           0 :   code = event_code;
    2674           0 :   foreach_vlib_main ()
    2675             :     {
    2676           0 :       em = &this_vlib_main->error_main;
    2677           0 :       i = n->error_heap_index + code;
    2678           0 :       c = em->counters[i];
    2679             : 
    2680           0 :       if (i < vec_len (em->counters_last_clear))
    2681           0 :         c -= em->counters_last_clear[i];
    2682           0 :       sum += c;
    2683             :     }
    2684           0 :   return sum;
    2685             : }
    2686             : 
    2687             : static void
    2688           0 : quic_show_aggregated_stats (vlib_main_t * vm)
    2689             : {
    2690           0 :   u32 num_workers = vlib_num_workers ();
    2691           0 :   quic_main_t *qm = &quic_main;
    2692           0 :   quic_ctx_t *ctx = NULL;
    2693             :   quicly_stats_t st, agg_stats;
    2694           0 :   u32 i, nconn = 0, nstream = 0;
    2695             : 
    2696           0 :   clib_memset (&agg_stats, 0, sizeof (agg_stats));
    2697           0 :   for (i = 0; i < num_workers + 1; i++)
    2698             :     {
    2699             :       /* *INDENT-OFF* */
    2700           0 :       pool_foreach (ctx, qm->ctx_pool[i])
    2701             :        {
    2702           0 :         if (quic_ctx_is_conn (ctx) && ctx->conn)
    2703             :           {
    2704           0 :             quicly_get_stats (ctx->conn, &st);
    2705           0 :             agg_stats.rtt.smoothed += st.rtt.smoothed;
    2706           0 :             agg_stats.rtt.minimum += st.rtt.minimum;
    2707           0 :             agg_stats.rtt.variance += st.rtt.variance;
    2708           0 :             agg_stats.num_packets.received += st.num_packets.received;
    2709           0 :             agg_stats.num_packets.sent += st.num_packets.sent;
    2710           0 :             agg_stats.num_packets.lost += st.num_packets.lost;
    2711           0 :             agg_stats.num_packets.ack_received += st.num_packets.ack_received;
    2712           0 :             agg_stats.num_bytes.received += st.num_bytes.received;
    2713           0 :             agg_stats.num_bytes.sent += st.num_bytes.sent;
    2714           0 :             nconn++;
    2715             :           }
    2716           0 :         else if (quic_ctx_is_stream (ctx))
    2717           0 :           nstream++;
    2718             :       }
    2719             :       /* *INDENT-ON* */
    2720             :     }
    2721           0 :   vlib_cli_output (vm, "-------- Connections --------");
    2722           0 :   vlib_cli_output (vm, "Current:         %u", nconn);
    2723           0 :   vlib_cli_output (vm, "Opened:          %d",
    2724             :                    quic_get_counter_value (QUIC_ERROR_OPENED_CONNECTION));
    2725           0 :   vlib_cli_output (vm, "Closed:          %d",
    2726             :                    quic_get_counter_value (QUIC_ERROR_CLOSED_CONNECTION));
    2727           0 :   vlib_cli_output (vm, "---------- Streams ----------");
    2728           0 :   vlib_cli_output (vm, "Current:         %u", nstream);
    2729           0 :   vlib_cli_output (vm, "Opened:          %d",
    2730             :                    quic_get_counter_value (QUIC_ERROR_OPENED_STREAM));
    2731           0 :   vlib_cli_output (vm, "Closed:          %d",
    2732             :                    quic_get_counter_value (QUIC_ERROR_CLOSED_STREAM));
    2733           0 :   vlib_cli_output (vm, "---------- Packets ----------");
    2734           0 :   vlib_cli_output (vm, "RX Total:        %d",
    2735             :                    quic_get_counter_value (QUIC_ERROR_RX_PACKETS));
    2736           0 :   vlib_cli_output (vm, "RX 0RTT:         %d",
    2737             :                    quic_get_counter_value (QUIC_ERROR_ZERO_RTT_RX_PACKETS));
    2738           0 :   vlib_cli_output (vm, "RX 1RTT:         %d",
    2739             :                    quic_get_counter_value (QUIC_ERROR_ONE_RTT_RX_PACKETS));
    2740           0 :   vlib_cli_output (vm, "TX Total:        %d",
    2741             :                    quic_get_counter_value (QUIC_ERROR_TX_PACKETS));
    2742           0 :   vlib_cli_output (vm, "----------- Stats -----------");
    2743           0 :   vlib_cli_output (vm, "Min      RTT     %f",
    2744           0 :                    nconn > 0 ? agg_stats.rtt.minimum / nconn : 0);
    2745           0 :   vlib_cli_output (vm, "Smoothed RTT     %f",
    2746           0 :                    nconn > 0 ? agg_stats.rtt.smoothed / nconn : 0);
    2747           0 :   vlib_cli_output (vm, "Variance on RTT  %f",
    2748           0 :                    nconn > 0 ? agg_stats.rtt.variance / nconn : 0);
    2749           0 :   vlib_cli_output (vm, "Packets Received %lu",
    2750             :                    agg_stats.num_packets.received);
    2751           0 :   vlib_cli_output (vm, "Packets Sent     %lu", agg_stats.num_packets.sent);
    2752           0 :   vlib_cli_output (vm, "Packets Lost     %lu", agg_stats.num_packets.lost);
    2753           0 :   vlib_cli_output (vm, "Packets Acks     %lu",
    2754             :                    agg_stats.num_packets.ack_received);
    2755           0 :   vlib_cli_output (vm, "RX bytes         %lu", agg_stats.num_bytes.received);
    2756           0 :   vlib_cli_output (vm, "TX bytes         %lu", agg_stats.num_bytes.sent);
    2757           0 : }
    2758             : 
    2759             : static u8 *
    2760           0 : quic_format_quicly_conn_id (u8 * s, va_list * args)
    2761             : {
    2762           0 :   quicly_cid_plaintext_t *mid = va_arg (*args, quicly_cid_plaintext_t *);
    2763           0 :   s = format (s, "C%x_%x", mid->master_id, mid->thread_id);
    2764           0 :   return s;
    2765             : }
    2766             : 
    2767             : static u8 *
    2768           0 : quic_format_quicly_stream_id (u8 * s, va_list * args)
    2769             : {
    2770           0 :   quicly_stream_t *stream = va_arg (*args, quicly_stream_t *);
    2771             :   s =
    2772           0 :     format (s, "%U S%lx", quic_format_quicly_conn_id,
    2773             :             quicly_get_master_id (stream->conn), stream->stream_id);
    2774           0 :   return s;
    2775             : }
    2776             : 
    2777             : static u8 *
    2778           0 : quic_format_listener_ctx (u8 * s, va_list * args)
    2779             : {
    2780           0 :   quic_ctx_t *ctx = va_arg (*args, quic_ctx_t *);
    2781           0 :   s = format (s, "[#%d][%x][Listener]", ctx->c_thread_index, ctx->c_c_index);
    2782           0 :   return s;
    2783             : }
    2784             : 
    2785             : static u8 *
    2786           0 : quic_format_connection_ctx (u8 * s, va_list * args)
    2787             : {
    2788           0 :   quic_ctx_t *ctx = va_arg (*args, quic_ctx_t *);
    2789             :   quicly_stats_t quicly_stats;
    2790             : 
    2791           0 :   s = format (s, "[#%d][%x]", ctx->c_thread_index, ctx->c_c_index);
    2792             : 
    2793           0 :   if (!ctx->conn)
    2794             :     {
    2795           0 :       s = format (s, "- no conn -\n");
    2796           0 :       return s;
    2797             :     }
    2798           0 :   s = format (s, "[%U]",
    2799             :               quic_format_quicly_conn_id, quicly_get_master_id (ctx->conn));
    2800           0 :   quicly_get_stats (ctx->conn, &quicly_stats);
    2801             : 
    2802           0 :   s = format (s, "[RTT >%3d, ~%3d, V%3d, last %3d]",
    2803             :               quicly_stats.rtt.minimum, quicly_stats.rtt.smoothed,
    2804             :               quicly_stats.rtt.variance, quicly_stats.rtt.latest);
    2805           0 :   s = format (s, " TX:%d RX:%d loss:%d ack:%d",
    2806             :               quicly_stats.num_packets.sent,
    2807             :               quicly_stats.num_packets.received,
    2808             :               quicly_stats.num_packets.lost,
    2809             :               quicly_stats.num_packets.ack_received);
    2810             :   s =
    2811           0 :     format (s, "\ncwnd:%u ssthresh:%u recovery_end:%lu", quicly_stats.cc.cwnd,
    2812             :             quicly_stats.cc.ssthresh, quicly_stats.cc.recovery_end);
    2813             : 
    2814           0 :   quicly_context_t *quicly_ctx = quic_get_quicly_ctx_from_ctx (ctx);
    2815           0 :   if (quicly_ctx->init_cc == &quicly_cc_cubic_init)
    2816             :     {
    2817           0 :       s = format (
    2818             :         s,
    2819             :         "\nk:%d w_max:%u w_last_max:%u avoidance_start:%ld last_sent_time:%ld",
    2820             :         quicly_stats.cc.state.cubic.k, quicly_stats.cc.state.cubic.w_max,
    2821             :         quicly_stats.cc.state.cubic.w_last_max,
    2822             :         quicly_stats.cc.state.cubic.avoidance_start,
    2823             :         quicly_stats.cc.state.cubic.last_sent_time);
    2824             :     }
    2825           0 :   else if (quicly_ctx->init_cc == &quicly_cc_reno_init)
    2826             :     {
    2827           0 :       s = format (s, " stash:%u", quicly_stats.cc.state.reno.stash);
    2828             :     }
    2829             : 
    2830           0 :   return s;
    2831             : }
    2832             : 
    2833             : static u8 *
    2834           0 : quic_format_stream_ctx (u8 * s, va_list * args)
    2835             : {
    2836           0 :   quic_ctx_t *ctx = va_arg (*args, quic_ctx_t *);
    2837             :   session_t *stream_session;
    2838           0 :   quicly_stream_t *stream = ctx->stream;
    2839             :   u32 txs, rxs;
    2840             : 
    2841           0 :   s = format (s, "[#%d][%x]", ctx->c_thread_index, ctx->c_c_index);
    2842           0 :   s = format (s, "[%U]", quic_format_quicly_stream_id, stream);
    2843             : 
    2844           0 :   stream_session = session_get_if_valid (ctx->c_s_index, ctx->c_thread_index);
    2845           0 :   if (!stream_session)
    2846             :     {
    2847           0 :       s = format (s, "- no session -\n");
    2848           0 :       return s;
    2849             :     }
    2850           0 :   txs = svm_fifo_max_dequeue (stream_session->tx_fifo);
    2851           0 :   rxs = svm_fifo_max_dequeue (stream_session->rx_fifo);
    2852           0 :   s = format (s, "[rx %d tx %d]\n", rxs, txs);
    2853           0 :   return s;
    2854             : }
    2855             : 
    2856             : static clib_error_t *
    2857           0 : quic_show_connections_command_fn (vlib_main_t * vm,
    2858             :                                   unformat_input_t * input,
    2859             :                                   vlib_cli_command_t * cmd)
    2860             : {
    2861           0 :   unformat_input_t _line_input, *line_input = &_line_input;
    2862           0 :   u8 show_listeners = 0, show_conn = 0, show_stream = 0;
    2863           0 :   u32 num_workers = vlib_num_workers ();
    2864           0 :   quic_main_t *qm = &quic_main;
    2865           0 :   clib_error_t *error = 0;
    2866           0 :   quic_ctx_t *ctx = NULL;
    2867             : 
    2868           0 :   session_cli_return_if_not_enabled ();
    2869             : 
    2870           0 :   if (!unformat_user (input, unformat_line_input, line_input))
    2871             :     {
    2872           0 :       quic_show_aggregated_stats (vm);
    2873           0 :       return 0;
    2874             :     }
    2875             : 
    2876           0 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    2877             :     {
    2878           0 :       if (unformat (line_input, "listener"))
    2879           0 :         show_listeners = 1;
    2880           0 :       else if (unformat (line_input, "conn"))
    2881           0 :         show_conn = 1;
    2882           0 :       else if (unformat (line_input, "stream"))
    2883           0 :         show_stream = 1;
    2884             :       else
    2885             :         {
    2886           0 :           error = clib_error_return (0, "unknown input `%U'",
    2887             :                                      format_unformat_error, line_input);
    2888           0 :           goto done;
    2889             :         }
    2890             :     }
    2891             : 
    2892           0 :   for (int i = 0; i < num_workers + 1; i++)
    2893             :     {
    2894             :       /* *INDENT-OFF* */
    2895           0 :       pool_foreach (ctx, qm->ctx_pool[i])
    2896             :        {
    2897           0 :         if (quic_ctx_is_stream (ctx) && show_stream)
    2898           0 :           vlib_cli_output (vm, "%U", quic_format_stream_ctx, ctx);
    2899           0 :         else if (quic_ctx_is_listener (ctx) && show_listeners)
    2900           0 :           vlib_cli_output (vm, "%U", quic_format_listener_ctx, ctx);
    2901           0 :         else if (quic_ctx_is_conn (ctx) && show_conn)
    2902           0 :           vlib_cli_output (vm, "%U", quic_format_connection_ctx, ctx);
    2903             :       }
    2904             :       /* *INDENT-ON* */
    2905             :     }
    2906             : 
    2907           0 : done:
    2908           0 :   unformat_free (line_input);
    2909           0 :   return error;
    2910             : }
    2911             : 
    2912             : /* *INDENT-OFF* */
    2913        2025 : VLIB_CLI_COMMAND (quic_plugin_crypto_command, static) = {
    2914             :   .path = "quic set crypto api",
    2915             :   .short_help = "quic set crypto api [picotls|vpp]",
    2916             :   .function = quic_plugin_crypto_command_fn,
    2917             : };
    2918        2025 : VLIB_CLI_COMMAND(quic_plugin_set_fifo_size_command, static)=
    2919             : {
    2920             :   .path = "quic set fifo-size",
    2921             :   .short_help = "quic set fifo-size N[K|M|G] (default 64K)",
    2922             :   .function = quic_plugin_set_fifo_size_command_fn,
    2923             : };
    2924        2025 : VLIB_CLI_COMMAND(quic_show_ctx_command, static)=
    2925             : {
    2926             :   .path = "show quic",
    2927             :   .short_help = "show quic",
    2928             :   .function = quic_show_connections_command_fn,
    2929             : };
    2930        2025 : VLIB_CLI_COMMAND (quic_list_crypto_context_command, static) =
    2931             : {
    2932             :   .path = "show quic crypto context",
    2933             :   .short_help = "list quic crypto contextes",
    2934             :   .function = quic_list_crypto_context_command_fn,
    2935             : };
    2936        2025 : VLIB_CLI_COMMAND (quic_set_max_packets_per_key, static) =
    2937             : {
    2938             :   .path = "set quic max_packets_per_key",
    2939             :   .short_help = "set quic max_packets_per_key 16777216",
    2940             :   .function = quic_set_max_packets_per_key_fn,
    2941             : };
    2942        2025 : VLIB_CLI_COMMAND (quic_set_cc, static) = {
    2943             :   .path = "set quic cc",
    2944             :   .short_help = "set quic cc [reno|cubic]",
    2945             :   .function = quic_set_cc_fn,
    2946             : };
    2947             : VLIB_PLUGIN_REGISTER () =
    2948             : {
    2949             :   .version = VPP_BUILD_VER,
    2950             :   .description = "Quic transport protocol",
    2951             :   .default_disabled = 1,
    2952             : };
    2953             : /* *INDENT-ON* */
    2954             : 
    2955             : static clib_error_t *
    2956          25 : quic_config_fn (vlib_main_t * vm, unformat_input_t * input)
    2957             : {
    2958          25 :   unformat_input_t _line_input, *line_input = &_line_input;
    2959          25 :   quic_main_t *qm = &quic_main;
    2960          25 :   clib_error_t *error = 0;
    2961             :   uword tmp;
    2962             :   u32 i;
    2963             : 
    2964          25 :   qm->udp_fifo_size = QUIC_DEFAULT_FIFO_SIZE;
    2965          25 :   qm->udp_fifo_prealloc = 0;
    2966          25 :   qm->connection_timeout = QUIC_DEFAULT_CONN_TIMEOUT;
    2967             : 
    2968          25 :   if (!unformat_user (input, unformat_line_input, line_input))
    2969          25 :     return 0;
    2970             : 
    2971           0 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    2972             :     {
    2973           0 :       if (unformat (line_input, "fifo-size %U", unformat_memory_size, &tmp))
    2974             :         {
    2975           0 :           if (tmp >= 0x100000000ULL)
    2976             :             {
    2977           0 :               error = clib_error_return (0,
    2978             :                                          "fifo-size %llu (0x%llx) too large",
    2979             :                                          tmp, tmp);
    2980           0 :               goto done;
    2981             :             }
    2982           0 :           qm->udp_fifo_size = tmp;
    2983             :         }
    2984           0 :       else if (unformat (line_input, "conn-timeout %u", &i))
    2985           0 :         qm->connection_timeout = i;
    2986           0 :       else if (unformat (line_input, "fifo-prealloc %u", &i))
    2987           0 :         qm->udp_fifo_prealloc = i;
    2988             :       else
    2989             :         {
    2990           0 :           error = clib_error_return (0, "unknown input '%U'",
    2991             :                                      format_unformat_error, line_input);
    2992           0 :           goto done;
    2993             :         }
    2994             :     }
    2995           0 : done:
    2996           0 :   unformat_free (line_input);
    2997           0 :   return error;
    2998             : }
    2999             : 
    3000          75 : VLIB_EARLY_CONFIG_FUNCTION (quic_config_fn, "quic");
    3001             : 
    3002             : static uword
    3003           0 : quic_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
    3004             :               vlib_frame_t * frame)
    3005             : {
    3006           0 :   return 0;
    3007             : }
    3008             : 
    3009             : /* *INDENT-OFF* */
    3010        1950 : VLIB_REGISTER_NODE (quic_input_node) =
    3011             : {
    3012             :   .function = quic_node_fn,
    3013             :   .name = "quic-input",
    3014             :   .vector_size = sizeof (u32),
    3015             :   .type = VLIB_NODE_TYPE_INTERNAL,
    3016             :   .n_errors = ARRAY_LEN (quic_error_strings),
    3017             :   .error_strings = quic_error_strings,
    3018             : };
    3019             : /* *INDENT-ON* */
    3020             : 
    3021             : /*
    3022             :  * fd.io coding-style-patch-verification: ON
    3023             :  *
    3024             :  * Local Variables:
    3025             :  * eval: (c-set-style "gnu")
    3026             :  * End:
    3027             :  */

Generated by: LCOV version 1.14