+static void quic_check_quic_session_connected (quic_ctx_t * ctx);
+static int quic_reset_connection (u64 udp_session_handle,
+ quic_rx_packet_ctx_t * pctx);
+static void quic_proto_on_close (u32 ctx_index, u32 thread_index);
+
+static quicly_stream_open_t on_stream_open;
+static quicly_closed_by_peer_t on_closed_by_peer;
+static quicly_now_t quicly_vpp_now_cb;
+
+/* Crypto contexts */
+
+static inline void
+quic_crypto_context_make_key_from_ctx (clib_bihash_kv_24_8_t * kv,
+ quic_ctx_t * ctx)
+{
+ application_t *app = application_get (ctx->parent_app_id);
+ kv->key[0] = ((u64) ctx->ckpair_index) << 32 | (u64) ctx->crypto_engine;
+ kv->key[1] = app->sm_properties.rx_fifo_size - 1;
+ kv->key[2] = app->sm_properties.tx_fifo_size - 1;
+}
+
+static inline void
+quic_crypto_context_make_key_from_crctx (clib_bihash_kv_24_8_t * kv,
+ crypto_context_t * crctx)
+{
+ quic_crypto_context_data_t *data =
+ (quic_crypto_context_data_t *) crctx->data;
+ kv->key[0] = ((u64) crctx->ckpair_index) << 32 | (u64) crctx->crypto_engine;
+ kv->key[1] = data->quicly_ctx.transport_params.max_stream_data.bidi_local;
+ kv->key[2] = data->quicly_ctx.transport_params.max_stream_data.bidi_remote;
+}
+
+static void
+quic_crypto_context_free_if_needed (crypto_context_t * crctx, u8 thread_index)
+{
+ quic_main_t *qm = &quic_main;
+ clib_bihash_kv_24_8_t kv;
+ if (crctx->n_subscribers)
+ return;
+ quic_crypto_context_make_key_from_crctx (&kv, crctx);
+ clib_bihash_add_del_24_8 (&qm->wrk_ctx[thread_index].crypto_context_hash,
+ &kv, 0 /* is_add */ );
+ clib_mem_free (crctx->data);
+ pool_put (qm->wrk_ctx[thread_index].crypto_ctx_pool, crctx);
+}
+
+static quicly_datagram_t *
+quic_alloc_packet (quicly_packet_allocator_t * self, size_t payloadsize)
+{
+ quicly_datagram_t *packet;
+ if ((packet =
+ clib_mem_alloc (sizeof (*packet) + payloadsize +
+ sizeof (quic_encrypt_cb_ctx))) == NULL)
+ return NULL;
+ packet->data.base =
+ (uint8_t *) packet + sizeof (*packet) + sizeof (quic_encrypt_cb_ctx);
+ quic_encrypt_cb_ctx *encrypt_cb_ctx =
+ (quic_encrypt_cb_ctx *) ((uint8_t *) packet + sizeof (*packet));
+
+ clib_memset (encrypt_cb_ctx, 0, sizeof (*encrypt_cb_ctx));
+ return packet;
+}
+
+static void
+quic_free_packet (quicly_packet_allocator_t * self,
+ quicly_datagram_t * packet)
+{
+ clib_mem_free (packet);
+}
+
+quicly_packet_allocator_t quic_packet_allocator =
+ { quic_alloc_packet, quic_free_packet };
+
+static int
+quic_app_cert_key_pair_delete_callback (app_cert_key_pair_t * ckpair)
+{
+ quic_main_t *qm = &quic_main;
+ crypto_context_t *crctx;
+ clib_bihash_kv_24_8_t kv;
+ vlib_thread_main_t *vtm = vlib_get_thread_main ();
+ int num_threads = 1 /* main thread */ + vtm->n_threads;
+ int i;
+
+ for (i = 0; i < num_threads; i++)
+ {
+ /* *INDENT-OFF* */
+ pool_foreach (crctx, qm->wrk_ctx[i].crypto_ctx_pool, ({
+ if (crctx->ckpair_index == ckpair->cert_key_index)
+ {
+ quic_crypto_context_make_key_from_crctx (&kv, crctx);
+ clib_bihash_add_del_24_8 (&qm->wrk_ctx[i].crypto_context_hash, &kv, 0 /* is_add */ );
+ }
+ }));
+ /* *INDENT-ON* */
+ }
+ return 0;
+}
+
+static crypto_context_t *
+quic_crypto_context_alloc (u8 thread_index)
+{
+ quic_main_t *qm = &quic_main;
+ crypto_context_t *crctx;
+ u32 idx;
+
+ pool_get (qm->wrk_ctx[thread_index].crypto_ctx_pool, crctx);
+ clib_memset (crctx, 0, sizeof (*crctx));
+ idx = (crctx - qm->wrk_ctx[thread_index].crypto_ctx_pool);
+ crctx->ctx_index = ((u32) thread_index) << 24 | idx;
+
+ return crctx;
+}
+
+static crypto_context_t *
+quic_crypto_context_get (u32 cr_index, u32 thread_index)
+{
+ quic_main_t *qm = &quic_main;
+ ASSERT (cr_index >> 24 == thread_index);
+ return pool_elt_at_index (qm->wrk_ctx[thread_index].crypto_ctx_pool,
+ cr_index & 0x00ffffff);
+}
+
+static clib_error_t *
+quic_list_crypto_context_command_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ quic_main_t *qm = &quic_main;
+ crypto_context_t *crctx;
+ vlib_thread_main_t *vtm = vlib_get_thread_main ();
+ int i, num_threads = 1 /* main thread */ + vtm->n_threads;
+ for (i = 0; i < num_threads; i++)
+ {
+ /* *INDENT-OFF* */
+ pool_foreach (crctx, qm->wrk_ctx[i].crypto_ctx_pool, ({
+ vlib_cli_output (vm, "[%d][Q]%U", i, format_crypto_context, crctx);
+ }));
+ /* *INDENT-ON* */
+ }
+ return 0;
+}
+
+static clib_error_t *
+quic_set_max_packets_per_key_fn (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ quic_main_t *qm = &quic_main;
+ unformat_input_t _line_input, *line_input = &_line_input;
+ u64 tmp;
+
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "%U", unformat_memory_size, &tmp))
+ {
+ qm->max_packets_per_key = tmp;
+ }
+ else
+ return clib_error_return (0, "unknown input '%U'",
+ format_unformat_error, line_input);
+ }
+
+ return 0;
+}
+
+static void
+quic_release_crypto_context (u32 crypto_context_index, u8 thread_index)
+{
+ crypto_context_t *crctx;
+ crctx = quic_crypto_context_get (crypto_context_index, thread_index);
+ crctx->n_subscribers--;
+ quic_crypto_context_free_if_needed (crctx, thread_index);
+}
+
+static int
+quic_init_crypto_context (crypto_context_t * crctx, quic_ctx_t * ctx)
+{
+ quic_main_t *qm = &quic_main;
+ quicly_context_t *quicly_ctx;
+ ptls_iovec_t key_vec;
+ app_cert_key_pair_t *ckpair;
+ application_t *app;
+ quic_crypto_context_data_t *data;
+ ptls_context_t *ptls_ctx;
+
+ QUIC_DBG (2, "Init quic crctx %d thread %d", crctx->ctx_index,
+ ctx->c_thread_index);
+
+ data = clib_mem_alloc (sizeof (*data));
+ /* picotls depends on data being zeroed */
+ clib_memset (data, 0, sizeof (*data));
+ crctx->data = (void *) data;
+ quicly_ctx = &data->quicly_ctx;
+ ptls_ctx = &data->ptls_ctx;
+
+ ptls_ctx->random_bytes = ptls_openssl_random_bytes;
+ ptls_ctx->get_time = &ptls_get_time;
+ ptls_ctx->key_exchanges = ptls_openssl_key_exchanges;
+ ptls_ctx->cipher_suites = qm->quic_ciphers[ctx->crypto_engine];
+ ptls_ctx->certificates.list = NULL;
+ ptls_ctx->certificates.count = 0;
+ ptls_ctx->esni = NULL;
+ ptls_ctx->on_client_hello = NULL;
+ ptls_ctx->emit_certificate = NULL;
+ ptls_ctx->sign_certificate = NULL;
+ ptls_ctx->verify_certificate = NULL;
+ ptls_ctx->ticket_lifetime = 86400;
+ ptls_ctx->max_early_data_size = 8192;
+ ptls_ctx->hkdf_label_prefix__obsolete = NULL;
+ ptls_ctx->require_dhe_on_psk = 1;
+ ptls_ctx->encrypt_ticket = &qm->session_cache.super;
+ clib_memcpy (quicly_ctx, &quicly_spec_context, sizeof (quicly_context_t));
+
+ quicly_ctx->max_packet_size = QUIC_MAX_PACKET_SIZE;
+ quicly_ctx->max_packets_per_key = qm->max_packets_per_key;
+ quicly_ctx->tls = ptls_ctx;
+ quicly_ctx->stream_open = &on_stream_open;
+ quicly_ctx->closed_by_peer = &on_closed_by_peer;
+ quicly_ctx->now = &quicly_vpp_now_cb;
+ quicly_amend_ptls_context (quicly_ctx->tls);
+
+ quicly_ctx->packet_allocator = &quic_packet_allocator;
+ quicly_ctx->crypto_engine = &quic_crypto_engine;
+ quicly_ctx->transport_params.max_data = QUIC_INT_MAX;
+ quicly_ctx->transport_params.max_streams_uni = (uint64_t) 1 << 60;
+ quicly_ctx->transport_params.max_streams_bidi = (uint64_t) 1 << 60;
+ quicly_ctx->transport_params.max_idle_timeout = qm->connection_timeout;
+
+ app = application_get (ctx->parent_app_id);
+ quicly_ctx->transport_params.max_stream_data.bidi_local =
+ app->sm_properties.rx_fifo_size - 1;
+ quicly_ctx->transport_params.max_stream_data.bidi_remote =
+ app->sm_properties.tx_fifo_size - 1;
+ quicly_ctx->transport_params.max_stream_data.uni = QUIC_INT_MAX;
+
+ if (!app->quic_iv_set)
+ {
+ ptls_openssl_random_bytes (app->quic_iv, QUIC_IV_LEN - 1);
+ app->quic_iv[QUIC_IV_LEN - 1] = 0;
+ app->quic_iv_set = 1;
+ }
+
+ clib_memcpy (data->cid_key, app->quic_iv, QUIC_IV_LEN);
+ key_vec = ptls_iovec_init (data->cid_key, QUIC_IV_LEN);
+ quicly_ctx->cid_encryptor =
+ quicly_new_default_cid_encryptor (&ptls_openssl_bfecb,
+ &ptls_openssl_aes128ecb,
+ &ptls_openssl_sha256, key_vec);
+
+ ckpair = app_cert_key_pair_get_if_valid (crctx->ckpair_index);
+ if (!ckpair || !ckpair->key || !ckpair->cert)
+ {
+ QUIC_DBG (1, "Wrong ckpair id %d\n", crctx->ckpair_index);
+ return -1;
+ }
+ if (load_bio_private_key (quicly_ctx->tls, (char *) ckpair->key))
+ {
+ QUIC_DBG (1, "failed to read private key from app configuration\n");
+ return -1;
+ }
+ if (load_bio_certificate_chain (quicly_ctx->tls, (char *) ckpair->cert))
+ {
+ QUIC_DBG (1, "failed to load certificate\n");
+ return -1;
+ }
+ return 0;
+
+}
+
+static int
+quic_acquire_crypto_context (quic_ctx_t * ctx)
+{
+ quic_main_t *qm = &quic_main;
+ crypto_context_t *crctx;
+ clib_bihash_kv_24_8_t kv;
+
+ if (ctx->crypto_engine == CRYPTO_ENGINE_NONE)
+ {
+ QUIC_DBG (2, "No crypto engine specified, using %d",
+ qm->default_crypto_engine);
+ ctx->crypto_engine = qm->default_crypto_engine;
+ }
+ if (!clib_bitmap_get (qm->available_crypto_engines, ctx->crypto_engine))
+ {
+ QUIC_DBG (1, "Quic does not support crypto engine %d",
+ ctx->crypto_engine);
+ return VNET_API_ERROR_MISSING_CERT_KEY;
+ }
+
+ /* Check for exisiting crypto ctx */
+ quic_crypto_context_make_key_from_ctx (&kv, ctx);
+ if (clib_bihash_search_24_8
+ (&qm->wrk_ctx[ctx->c_thread_index].crypto_context_hash, &kv, &kv) == 0)
+ {
+ crctx = quic_crypto_context_get (kv.value, ctx->c_thread_index);
+ QUIC_DBG (2, "Found exisiting crypto context %d", kv.value);
+ ctx->crypto_context_index = kv.value;
+ crctx->n_subscribers++;
+ return 0;
+ }
+
+ crctx = quic_crypto_context_alloc (ctx->c_thread_index);
+ ctx->crypto_context_index = crctx->ctx_index;
+ kv.value = crctx->ctx_index;
+ crctx->crypto_engine = ctx->crypto_engine;
+ crctx->ckpair_index = ctx->ckpair_index;
+ if (quic_init_crypto_context (crctx, ctx))
+ goto error;
+ if (vnet_app_add_cert_key_interest (ctx->ckpair_index, qm->app_index))
+ goto error;
+ crctx->n_subscribers++;
+ clib_bihash_add_del_24_8 (&qm->
+ wrk_ctx[ctx->c_thread_index].crypto_context_hash,
+ &kv, 1 /* is_add */ );
+ return 0;