2 * Copyright (c) 2019 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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include <sys/socket.h>
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 #include <openssl/pem.h>
25 #include <vppinfra/lock.h>
27 #include <quic/quic.h>
29 #include <quicly/streambuf.h>
30 #include <picotls/openssl.h>
31 #include <picotls/pembase64.h>
33 static quic_main_t quic_main;
35 static void quic_update_timer (quic_ctx_t * ctx);
36 static int64_t quic_get_time (quicly_now_cb * self);
37 static void quic_connection_closed (u32 conn_index);
38 static void quic_disconnect (u32 ctx_index, u32 thread_index);
40 #define QUIC_INT_MAX 0x3FFFFFFFFFFFFFFF
43 quic_ctx_half_open_alloc (void)
45 quic_main_t *qm = &quic_main;
50 pool_get_aligned_will_expand (qm->half_open_ctx_pool, will_expand, 0);
51 if (PREDICT_FALSE (will_expand && vlib_num_workers ()))
53 clib_rwlock_writer_lock (&qm->half_open_rwlock);
54 pool_get (qm->half_open_ctx_pool, ctx);
55 ctx_index = ctx - qm->half_open_ctx_pool;
56 clib_rwlock_writer_unlock (&qm->half_open_rwlock);
60 /* reader lock assumption: only main thread will call pool_get */
61 clib_rwlock_reader_lock (&qm->half_open_rwlock);
62 pool_get (qm->half_open_ctx_pool, ctx);
63 ctx_index = ctx - qm->half_open_ctx_pool;
64 clib_rwlock_reader_unlock (&qm->half_open_rwlock);
66 memset (ctx, 0, sizeof (*ctx));
71 quic_ctx_half_open_free (u32 ho_index)
73 quic_main_t *qm = &quic_main;
74 clib_rwlock_writer_lock (&qm->half_open_rwlock);
75 pool_put_index (qm->half_open_ctx_pool, ho_index);
76 clib_rwlock_writer_unlock (&qm->half_open_rwlock);
80 quic_ctx_half_open_get (u32 ctx_index)
82 quic_main_t *qm = &quic_main;
83 clib_rwlock_reader_lock (&qm->half_open_rwlock);
84 return pool_elt_at_index (qm->half_open_ctx_pool, ctx_index);
88 quic_ctx_half_open_reader_unlock ()
90 clib_rwlock_reader_unlock (&quic_main.half_open_rwlock);
94 quic_ctx_half_open_index (quic_ctx_t * ctx)
96 return (ctx - quic_main.half_open_ctx_pool);
102 u8 thread_index = vlib_get_thread_index ();
103 quic_main_t *qm = &quic_main;
106 pool_get (qm->ctx_pool[thread_index], ctx);
108 memset (ctx, 0, sizeof (quic_ctx_t));
109 ctx->c_thread_index = thread_index;
110 return ctx - qm->ctx_pool[thread_index];
114 quic_ctx_free (quic_ctx_t * ctx)
116 QUIC_DBG (2, "Free ctx %u", ctx->c_c_index);
117 u32 thread_index = ctx->c_thread_index;
119 memset (ctx, 0xfb, sizeof (*ctx));
120 pool_put (quic_main.ctx_pool[thread_index], ctx);
124 quic_ctx_get (u32 ctx_index)
126 return pool_elt_at_index (quic_main.ctx_pool[vlib_get_thread_index ()],
131 quic_ctx_get_w_thread (u32 ctx_index, u8 thread_index)
133 return pool_elt_at_index (quic_main.ctx_pool[thread_index], ctx_index);
137 quic_disconnect_transport (quic_ctx_t * ctx)
139 QUIC_DBG (2, "Called quic_disconnect_transport");
140 vnet_disconnect_args_t a = {
141 .handle = ctx->c_quic_ctx_id.quic_session,
142 .app_index = quic_main.app_index,
145 if (vnet_disconnect_session (&a))
146 clib_warning ("UDP session disconnect errored");
150 quic_send_datagram (session_t * session, quicly_datagram_t * packet)
152 QUIC_DBG (2, "Called quic_send_datagram at %ld", quic_get_time (NULL));
154 session_dgram_hdr_t hdr;
158 transport_connection_t *tc;
160 len = packet->data.len;
161 f = session->tx_fifo;
162 tc = session_get_transport (session);
164 max_enqueue = svm_fifo_max_enqueue (f);
165 if (max_enqueue <= sizeof (session_dgram_hdr_t))
168 max_enqueue -= sizeof (session_dgram_hdr_t);
170 if (max_enqueue < len)
173 // Build packet header for fifo
174 hdr.data_length = len;
176 hdr.is_ip4 = tc->is_ip4;
177 clib_memcpy (&hdr.lcl_ip, &tc->lcl_ip, sizeof (ip46_address_t));
178 hdr.lcl_port = tc->lcl_port;
180 // Read dest address from quicly-provided sockaddr
183 ASSERT (packet->sa.sa_family == AF_INET);
184 struct sockaddr_in *sa4 = (struct sockaddr_in *) &packet->sa;
185 hdr.rmt_port = sa4->sin_port;
186 hdr.rmt_ip.ip4.as_u32 = sa4->sin_addr.s_addr;
190 ASSERT (packet->sa.sa_family == AF_INET6);
191 struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &packet->sa;
192 hdr.rmt_port = sa6->sin6_port;
193 clib_memcpy (&hdr.rmt_ip.ip6, &sa6->sin6_addr, 16);
196 rv = svm_fifo_enqueue_nowait (f, sizeof (hdr), (u8 *) & hdr);
197 ASSERT (rv == sizeof (hdr));
198 if (svm_fifo_enqueue_nowait (f, len, packet->data.base) != len)
204 quic_send_packets (quic_ctx_t * ctx)
206 //QUIC_DBG (2, "Called quic_send_packets");
207 quicly_datagram_t *packets[16];
208 session_t *quic_session;
210 size_t num_packets, i;
213 quic_session = session_get_from_handle (ctx->c_quic_ctx_id.quic_session);
214 conn = ctx->c_quic_ctx_id.conn;
221 num_packets = sizeof (packets) / sizeof (packets[0]);
222 if ((ret = quicly_send (conn, packets, &num_packets)) == 0)
224 for (i = 0; i != num_packets; ++i)
226 if (quic_send_datagram (quic_session, packets[i]))
228 QUIC_DBG (2, "quic_send_datagram failed");
232 quicly_default_free_packet_cb.cb
233 (&quicly_default_free_packet_cb, packets[i]);
238 QUIC_DBG (2, "quicly_send returned %d, closing connection\n", ret);
242 while (ret == 0 && num_packets == sizeof (packets) / sizeof (packets[0]));
245 if (svm_fifo_set_event (quic_session->tx_fifo))
246 session_send_io_evt_to_thread (quic_session->tx_fifo, FIFO_EVENT_APP_TX);
248 quic_update_timer (ctx);
252 /*****************************************************************************
253 * START QUICLY CALLBACKS
254 * Called from QUIC lib
255 *****************************************************************************/
258 quic_on_stop_sending (quicly_stream_t * stream, int error_code)
260 QUIC_DBG (2, "received STOP_SENDING: %d", error_code);
265 quic_on_receive_reset (quicly_stream_t * stream, int error_code)
267 QUIC_DBG (2, "received RESET_STREAM: %d", error_code);
272 quic_on_receive (quicly_stream_t * stream, size_t off, const void *src,
275 QUIC_DBG (2, "received data: %lu bytes", len);
276 u32 to_enqueue, ctx_id;
278 session_t *app_session;
280 app_worker_t *app_wrk;
282 ctx_id = (u64) * quicly_get_data (stream->conn);
283 ctx = quic_ctx_get (ctx_id);
284 app_session = session_get_from_handle (ctx->c_quic_ctx_id.app_session);
285 rx_fifo = app_session->rx_fifo;
286 to_enqueue = svm_fifo_max_enqueue (rx_fifo);
287 if (to_enqueue > len)
289 // TODO what happens to the excess bytes?
291 svm_fifo_enqueue_nowait (rx_fifo, to_enqueue, src);
294 app_wrk = app_worker_get_if_valid (app_session->app_wrk_index);
295 if (PREDICT_TRUE (app_wrk != 0))
296 app_worker_lock_and_send_event (app_wrk, app_session, SESSION_IO_EVT_RX);
300 static const quicly_stream_callbacks_t quic_stream_callbacks = {
301 .on_destroy = quicly_streambuf_destroy,
302 .on_send_shift = quicly_streambuf_egress_shift,
303 .on_send_emit = quicly_streambuf_egress_emit,
304 .on_send_stop = quic_on_stop_sending,
305 .on_receive = quic_on_receive,
306 .on_receive_reset = quic_on_receive_reset
310 quic_on_stream_open (quicly_stream_open_cb * self, quicly_stream_t * stream)
312 QUIC_DBG (2, "on_stream_open called");
315 quicly_streambuf_create (stream, sizeof (quicly_streambuf_t))) != 0)
319 stream->callbacks = &quic_stream_callbacks;
323 static quicly_stream_open_cb on_stream_open = { &quic_on_stream_open };
326 quic_on_conn_close (quicly_closed_by_peer_cb * self, quicly_conn_t * conn,
327 int code, uint64_t frame_type,
328 const char *reason, size_t reason_len)
330 QUIC_DBG (2, "connection closed, reason: %s", reason);
331 u32 ctx_index = (u64) * quicly_get_data (conn);
332 quic_connection_closed (ctx_index);
335 static quicly_closed_by_peer_cb on_closed_by_peer = { &quic_on_conn_close };
338 /*****************************************************************************
339 * END QUICLY CALLBACKS
340 *****************************************************************************/
342 /* single-entry session cache */
343 struct st_util_session_cache_t
345 ptls_encrypt_ticket_t super;
351 encrypt_ticket_cb (ptls_encrypt_ticket_t * _self, ptls_t * tls,
352 int is_encrypt, ptls_buffer_t * dst, ptls_iovec_t src)
354 struct st_util_session_cache_t *self = (void *) _self;
360 /* replace the cached entry along with a newly generated session id */
361 free (self->data.base);
362 if ((self->data.base = malloc (src.len)) == NULL)
363 return PTLS_ERROR_NO_MEMORY;
365 ptls_get_context (tls)->random_bytes (self->id, sizeof (self->id));
366 memcpy (self->data.base, src.base, src.len);
367 self->data.len = src.len;
369 /* store the session id in buffer */
370 if ((ret = ptls_buffer_reserve (dst, sizeof (self->id))) != 0)
372 memcpy (dst->base + dst->off, self->id, sizeof (self->id));
373 dst->off += sizeof (self->id);
379 /* check if session id is the one stored in cache */
380 if (src.len != sizeof (self->id))
381 return PTLS_ERROR_SESSION_NOT_FOUND;
382 if (memcmp (self->id, src.base, sizeof (self->id)) != 0)
383 return PTLS_ERROR_SESSION_NOT_FOUND;
385 /* return the cached value */
386 if ((ret = ptls_buffer_reserve (dst, self->data.len)) != 0)
388 memcpy (dst->base + dst->off, self->data.base, self->data.len);
389 dst->off += self->data.len;
395 static struct st_util_session_cache_t sc = {
397 .cb = encrypt_ticket_cb,
402 static ptls_context_t quic_tlsctx = {
403 .random_bytes = ptls_openssl_random_bytes,
404 .get_time = &ptls_get_time,
405 .key_exchanges = ptls_openssl_key_exchanges,
406 .cipher_suites = ptls_openssl_cipher_suites,
412 .on_client_hello = NULL,
413 .emit_certificate = NULL,
414 .sign_certificate = NULL,
415 .verify_certificate = NULL,
416 .ticket_lifetime = 86400,
417 .max_early_data_size = 8192,
418 .hkdf_label_prefix__obsolete = NULL,
419 .require_dhe_on_psk = 1,
420 .encrypt_ticket = &sc.super,
425 ptls_compare_separator_line (const char *line, const char *begin_or_end,
428 int ret = strncmp (line, "-----", 5);
429 size_t text_index = 5;
433 size_t begin_or_end_length = strlen (begin_or_end);
434 ret = strncmp (line + text_index, begin_or_end, begin_or_end_length);
435 text_index += begin_or_end_length;
440 ret = line[text_index] - ' ';
446 size_t label_length = strlen (label);
447 ret = strncmp (line + text_index, label, label_length);
448 text_index += label_length;
453 ret = strncmp (line + text_index, "-----", 5);
460 ptls_get_bio_pem_object (BIO * bio, const char *label, ptls_buffer_t * buf)
462 int ret = PTLS_ERROR_PEM_LABEL_NOT_FOUND;
464 ptls_base64_decode_state_t state;
466 /* Get the label on a line by itself */
467 while (BIO_gets (bio, line, 256))
469 if (ptls_compare_separator_line (line, "BEGIN", label) == 0)
472 ptls_base64_decode_init (&state);
476 /* Get the data in the buffer */
477 while (ret == 0 && BIO_gets (bio, line, 256))
479 if (ptls_compare_separator_line (line, "END", label) == 0)
481 if (state.status == PTLS_BASE64_DECODE_DONE
482 || (state.status == PTLS_BASE64_DECODE_IN_PROGRESS
489 ret = PTLS_ERROR_INCORRECT_BASE64;
495 ret = ptls_base64_decode (line, &state, buf);
503 ptls_load_bio_pem_objects (BIO * bio, const char *label, ptls_iovec_t * list,
504 size_t list_max, size_t * nb_objects)
513 while (count < list_max)
517 ptls_buffer_init (&buf, "", 0);
519 ret = ptls_get_bio_pem_object (bio, label, &buf);
523 if (buf.off > 0 && buf.is_allocated)
525 list[count].base = buf.base;
526 list[count].len = buf.off;
531 ptls_buffer_dispose (&buf);
536 ptls_buffer_dispose (&buf);
542 if (ret == PTLS_ERROR_PEM_LABEL_NOT_FOUND && count > 0)
552 #define PTLS_MAX_CERTS_IN_CONTEXT 16
555 ptls_load_bio_certificates (ptls_context_t * ctx, BIO * bio)
559 ctx->certificates.list =
560 (ptls_iovec_t *) malloc (PTLS_MAX_CERTS_IN_CONTEXT *
561 sizeof (ptls_iovec_t));
563 if (ctx->certificates.list == NULL)
565 ret = PTLS_ERROR_NO_MEMORY;
570 ptls_load_bio_pem_objects (bio, "CERTIFICATE", ctx->certificates.list,
571 PTLS_MAX_CERTS_IN_CONTEXT,
572 &ctx->certificates.count);
579 load_bio_certificate_chain (ptls_context_t * ctx, const char *cert_data)
582 cert_bio = BIO_new_mem_buf (cert_data, -1);
583 if (ptls_load_bio_certificates (ctx, cert_bio) != 0)
586 fprintf (stderr, "failed to load certificate:%s\n", strerror (errno));
593 load_bio_private_key (ptls_context_t * ctx, const char *pk_data)
595 static ptls_openssl_sign_certificate_t sc;
599 key_bio = BIO_new_mem_buf (pk_data, -1);
600 pkey = PEM_read_bio_PrivateKey (key_bio, NULL, NULL, NULL);
605 fprintf (stderr, "failed to read private key from app configuration\n");
609 ptls_openssl_init_sign_certificate (&sc, pkey);
610 EVP_PKEY_free (pkey);
612 ctx->sign_certificate = &sc.super;
616 quic_connection_closed (u32 ctx_index)
618 QUIC_DBG (2, "QUIC connection closed");
621 ctx = quic_ctx_get (ctx_index);
622 // TODO if connection is not established, just delete the session
624 // Do not try to send anything anymore
626 quicly_free (ctx->c_quic_ctx_id.conn);
627 ctx->c_quic_ctx_id.conn = NULL;
628 session_transport_closing_notify (&ctx->connection);
632 quic_get_time (quicly_now_cb * self)
634 // TODO read value set by set_time_now?
635 // (needs to change it not to call this function)
636 vlib_main_t *vlib_main = vlib_get_main ();
637 f64 time = vlib_time_now (vlib_main);
638 return (int64_t) (time * 1000.f);
640 quicly_now_cb quicly_vpp_now_cb = { quic_get_time };
643 allocate_quicly_ctx (application_t * app, u8 is_client)
645 QUIC_DBG (2, "Called allocate_quicly_ctx");
651 quicly_context_t *quicly_ctx;
654 ctx_data = malloc (sizeof (*ctx_data));
655 quicly_ctx = &ctx_data->_;
656 app->quicly_ctx = (u64 *) quicly_ctx;
657 memcpy (quicly_ctx, &quicly_default_context, sizeof (quicly_context_t));
659 quicly_ctx->tls = &quic_tlsctx;
660 quicly_ctx->stream_open = &on_stream_open;
661 quicly_ctx->closed_by_peer = &on_closed_by_peer;
662 quicly_ctx->now = &quicly_vpp_now_cb;
664 quicly_amend_ptls_context (quicly_ctx->tls);
666 quicly_ctx->event_log.mask = INT64_MAX;
667 quicly_ctx->event_log.cb = quicly_new_default_event_log_cb (stderr);
669 quicly_ctx->transport_params.max_data = QUIC_INT_MAX;
670 quicly_ctx->transport_params.max_streams_uni = QUIC_INT_MAX;
671 quicly_ctx->transport_params.max_streams_bidi = QUIC_INT_MAX;
672 quicly_ctx->transport_params.max_stream_data.bidi_local = QUIC_INT_MAX;
673 quicly_ctx->transport_params.max_stream_data.bidi_remote = QUIC_INT_MAX;
674 quicly_ctx->transport_params.max_stream_data.uni = QUIC_INT_MAX;
678 load_bio_private_key (quicly_ctx->tls, (char *) app->tls_key);
679 load_bio_certificate_chain (quicly_ctx->tls, (char *) app->tls_cert);
680 cid_key = ctx_data->cid_key;
681 quicly_ctx->tls->random_bytes (cid_key, 16);
683 quicly_ctx->encrypt_cid =
684 quicly_new_default_encrypt_cid_cb (&ptls_openssl_bfecb,
685 &ptls_openssl_sha256,
686 ptls_iovec_init (cid_key,
689 quicly_ctx->decrypt_cid =
690 quicly_new_default_decrypt_cid_cb (&ptls_openssl_bfecb,
691 &ptls_openssl_sha256,
692 ptls_iovec_init (cid_key,
699 /*****************************************************************************
700 * BEGIN TIMERS HANDLING
701 *****************************************************************************/
704 quic_set_time_now (u32 thread_index)
706 quic_main.wrk_ctx[thread_index].time_now = quic_get_time (NULL);
707 return quic_main.wrk_ctx[thread_index].time_now;
711 quic_timer_expired (u32 conn_index)
714 QUIC_DBG (2, "Timer expired for conn %u at %ld", conn_index,
715 quic_get_time (NULL));
716 ctx = quic_ctx_get (conn_index);
717 ctx->c_quic_ctx_id.timer_handle = QUIC_TIMER_HANDLE_INVALID;
718 if (quic_send_packets (ctx))
720 quic_connection_closed (conn_index);
725 quic_update_timer (quic_ctx_t * ctx)
727 tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
728 int64_t next_timeout;
730 // This timeout is in ms which is the unit of our timer
731 next_timeout = quicly_get_first_timeout (ctx->c_quic_ctx_id.conn);
732 tw = &quic_main.wrk_ctx[vlib_get_thread_index ()].timer_wheel;
733 f64 next_timeout_f = ((f64) next_timeout) / 1000.f;
735 clib_warning ("Timer set to %ld (%lf)", next_timeout, next_timeout_f);
737 if (ctx->c_quic_ctx_id.timer_handle == QUIC_TIMER_HANDLE_INVALID)
739 if (next_timeout == INT64_MAX)
741 ctx->c_quic_ctx_id.timer_handle =
742 tw_timer_start_1t_3w_1024sl_ov (tw, ctx->c_c_index, 0,
747 if (next_timeout == INT64_MAX)
749 tw_timer_stop_1t_3w_1024sl_ov (tw, ctx->c_quic_ctx_id.timer_handle);
750 ctx->c_quic_ctx_id.timer_handle = QUIC_TIMER_HANDLE_INVALID;
753 tw_timer_update_1t_3w_1024sl_ov (tw, ctx->c_quic_ctx_id.timer_handle,
759 quic_expired_timers_dispatch (u32 * expired_timers)
763 for (i = 0; i < vec_len (expired_timers); i++)
765 quic_timer_expired (expired_timers[i]);
770 /*****************************************************************************
771 * END TIMERS HANDLING
773 * BEGIN TRANSPORT PROTO FUNCTIONS
774 *****************************************************************************/
777 quic_connect (transport_endpoint_cfg_t * tep)
779 QUIC_DBG (2, "Called quic_connect");
780 vnet_connect_args_t _cargs = { {}, }, *cargs = &_cargs;
781 session_endpoint_cfg_t *sep;
782 quic_main_t *qm = &quic_main;
784 app_worker_t *app_wrk;
789 sep = (session_endpoint_cfg_t *) tep;
790 ctx_index = quic_ctx_half_open_alloc ();
791 ctx = quic_ctx_half_open_get (ctx_index);
792 ctx->c_quic_ctx_id.parent_app_wrk_idx = sep->app_wrk_index;
793 ctx->c_s_index = 0xFAFAFAFA;
794 ctx->c_quic_ctx_id.udp_is_ip4 = sep->is_ip4;
795 ctx->c_quic_ctx_id.timer_handle = QUIC_TIMER_HANDLE_INVALID;
796 ctx->c_quic_ctx_id.conn_state = QUIC_CONN_STATE_HANDSHAKE;
797 ctx->client_opaque = sep->opaque;
800 ctx->srv_hostname = format (0, "%v", sep->hostname);
801 vec_terminate_c_string (ctx->srv_hostname);
805 // needed by quic for crypto + determining client / server
807 format (0, "%U", format_ip46_address, &sep->ip, sep->is_ip4);
810 quic_ctx_half_open_reader_unlock ();
812 clib_memcpy (&cargs->sep, sep, sizeof (session_endpoint_cfg_t));
813 cargs->sep.transport_proto = TRANSPORT_PROTO_UDP;
814 cargs->app_index = qm->app_index;
815 cargs->api_context = ctx_index;
817 app_wrk = app_worker_get (sep->app_wrk_index);
818 app = application_get (app_wrk->app_index);
819 ctx->c_quic_ctx_id.parent_app_id = app_wrk->app_index;
820 cargs->sep_ext.ns_index = app->ns_index;
822 allocate_quicly_ctx (app, 1 /* is client */ );
824 if ((error = vnet_connect (cargs)))
827 QUIC_DBG (1, "New connect request %u", ctx_index);
832 quic_disconnect (u32 ctx_index, u32 thread_index)
834 QUIC_DBG (2, "Called quic_disconnect");
835 tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
838 QUIC_DBG (1, "Disconnecting %x", ctx_index);
840 ctx = quic_ctx_get (ctx_index);
841 if (ctx->c_quic_ctx_id.timer_handle != QUIC_TIMER_HANDLE_INVALID)
843 tw = &quic_main.wrk_ctx[vlib_get_thread_index ()].timer_wheel;
844 tw_timer_stop_1t_3w_1024sl_ov (tw, ctx->c_quic_ctx_id.timer_handle);
846 quic_disconnect_transport (ctx);
847 // This removes the session from the lookup table and frees it.
848 session_transport_delete_notify (&ctx->connection);
853 quic_start_listen (u32 app_listen_session_index, transport_endpoint_t * tep)
855 QUIC_DBG (2, "Called quic_start_listen");
856 vnet_listen_args_t _bargs, *args = &_bargs;
857 quic_main_t *qm = &quic_main;
858 session_handle_t udp_handle;
859 session_endpoint_cfg_t *sep;
860 session_t *quic_listen_session, *app_listen_session;
861 app_worker_t *app_wrk;
865 app_listener_t *app_listener;
867 sep = (session_endpoint_cfg_t *) tep;
868 app_wrk = app_worker_get (sep->app_wrk_index);
869 app = application_get (app_wrk->app_index);
871 allocate_quicly_ctx (app, 0 /* is_client */ );
873 sep->transport_proto = TRANSPORT_PROTO_UDP;
874 memset (args, 0, sizeof (*args));
875 args->app_index = qm->app_index;
876 args->sep_ext = *sep;
877 args->sep_ext.ns_index = app->ns_index;
878 if (vnet_listen (args))
881 lctx_index = quic_ctx_alloc (); // listener
882 udp_handle = args->handle;
883 app_listener = app_listener_get_w_handle (udp_handle);
884 quic_listen_session = app_listener_get_session (app_listener);
885 quic_listen_session->opaque = lctx_index;
887 app_listen_session = listen_session_get (app_listen_session_index);
889 lctx = quic_ctx_get (lctx_index); // listener
890 lctx->is_listener = 1;
891 lctx->c_quic_ctx_id.parent_app_wrk_idx = sep->app_wrk_index;
892 lctx->c_quic_ctx_id.parent_app_id = app_wrk->app_index;
893 lctx->c_quic_ctx_id.quic_session = udp_handle;
894 lctx->c_quic_ctx_id.app_session =
895 listen_session_get_handle (app_listen_session);
896 lctx->c_quic_ctx_id.udp_is_ip4 = sep->is_ip4;
898 QUIC_DBG (1, "Started listening %d", lctx_index);
903 quic_stop_listen (u32 lctx_index)
905 QUIC_DBG (2, "Called quic_stop_listen");
908 lctx = quic_ctx_get (lctx_index); // listener
909 vnet_unlisten_args_t a = {
910 .handle = lctx->c_quic_ctx_id.quic_session,
911 .app_index = quic_main.app_index,
912 .wrk_map_index = 0 /* default wrk */
914 if (vnet_unlisten (&a))
915 clib_warning ("unlisten errored");
917 // TODO: crypto state cleanup
919 quic_ctx_free (lctx); // listener
923 transport_connection_t *
924 quic_connection_get (u32 ctx_index, u32 thread_index)
926 QUIC_DBG (2, "Called quic_connection_get");
928 ctx = quic_ctx_get_w_thread (ctx_index, thread_index);
929 return &ctx->connection;
932 transport_connection_t *
933 quic_listener_get (u32 listener_index)
935 QUIC_DBG (2, "Called quic_listener_get");
937 ctx = quic_ctx_get (listener_index);
938 return &ctx->connection;
942 quic_update_time (f64 now, u8 thread_index)
944 tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
946 tw = &quic_main.wrk_ctx[thread_index].timer_wheel;
947 quic_set_time_now (thread_index);
948 tw_timer_expire_timers_1t_3w_1024sl_ov (tw, now);
952 format_quic_connection (u8 * s, va_list * args)
954 s = format (s, "[QUIC] connection");
959 format_quic_half_open (u8 * s, va_list * args)
961 u32 qc_index = va_arg (*args, u32);
962 quic_ctx_t *ctx = quic_ctx_half_open_get (qc_index);
963 s = format (s, "[QUIC] half-open app %u", ctx->c_quic_ctx_id.parent_app_id);
964 quic_ctx_half_open_reader_unlock ();
970 format_quic_listener (u8 * s, va_list * args)
972 s = format (s, "[QUIC] listener");
976 /*****************************************************************************
977 * END TRANSPORT PROTO FUNCTIONS
979 * START SESSION CALLBACKS
980 * Called from UDP layer
981 *****************************************************************************/
984 quic_build_sockaddr (struct sockaddr *sa, socklen_t * salen,
985 ip46_address_t * addr, u16 port, u8 is_ip4)
989 struct sockaddr_in *sa4 = (struct sockaddr_in *) sa;
990 sa4->sin_family = AF_INET;
991 sa4->sin_port = port;
992 sa4->sin_addr.s_addr = addr->ip4.as_u32;
993 *salen = sizeof (struct sockaddr_in);
997 struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) sa;
998 sa6->sin6_family = AF_INET6;
999 sa6->sin6_port = port;
1000 clib_memcpy (&sa6->sin6_addr, &addr->ip6, 16);
1001 *salen = sizeof (struct sockaddr_in6);
1006 quic_delayed_notify_app_connected (void *ctx_index)
1008 QUIC_DBG (1, "quic_notify_app_connected");
1009 session_t *app_session;
1010 app_worker_t *app_wrk;
1012 ctx = quic_ctx_get ((u32) (u64) ctx_index);
1014 app_wrk = app_worker_get_if_valid (ctx->c_quic_ctx_id.parent_app_wrk_idx);
1017 quic_disconnect_transport (ctx);
1021 app_session = session_alloc (ctx->c_thread_index);
1022 QUIC_DBG (1, "Created app_session, id %u", app_session->session_index);
1023 ctx->c_s_index = app_session->session_index;
1024 app_session->app_wrk_index = ctx->c_quic_ctx_id.parent_app_wrk_idx;
1025 app_session->connection_index = ctx->c_c_index;
1026 app_session->session_type =
1027 session_type_from_proto_and_ip (TRANSPORT_PROTO_QUIC,
1028 ctx->c_quic_ctx_id.udp_is_ip4);
1030 if (app_worker_init_connected (app_wrk, app_session)) // TODO dont allocate fifos
1032 quic_disconnect (ctx->c_c_index, vlib_get_thread_index ());
1033 return app_worker_connect_notify (app_wrk, NULL, ctx->client_opaque);
1036 app_session->session_state = SESSION_STATE_CONNECTING;
1037 if (app_worker_connect_notify (app_wrk, app_session, ctx->client_opaque))
1039 QUIC_DBG (1, "failed to notify app");
1040 quic_disconnect (ctx->c_c_index, vlib_get_thread_index ());
1044 ctx->c_quic_ctx_id.app_session = session_handle (app_session);
1045 app_session->session_state = SESSION_STATE_LISTENING;
1046 session_lookup_add_connection (&ctx->connection,
1047 session_handle (app_session));
1053 quic_session_connected_callback (u32 quic_app_index, u32 ho_ctx_idx,
1054 session_t * s, u8 is_fail)
1056 QUIC_DBG (2, "Called quic_session_connected_callback");
1057 // This should always be called before quic_connect returns since UDP always
1058 // connects instantly.
1059 struct sockaddr_in6 sa6;
1060 struct sockaddr *sa = (struct sockaddr *) &sa6;
1062 transport_connection_t *tc;
1063 quic_ctx_t *ho_ctx, *ctx;
1067 app_worker_t *app_wrk;
1069 ho_ctx = quic_ctx_half_open_get (ho_ctx_idx);
1076 app_worker_get_if_valid (ho_ctx->c_quic_ctx_id.parent_app_wrk_idx);
1079 api_context = ho_ctx->c_s_index;
1080 app_worker_connect_notify (app_wrk, 0, api_context);
1082 quic_ctx_half_open_reader_unlock ();
1083 quic_ctx_half_open_free (ho_ctx_idx);
1088 app_worker_get_if_valid (ho_ctx->c_quic_ctx_id.parent_app_wrk_idx);
1091 QUIC_DBG (1, "Appwrk not found");
1094 app = application_get (app_wrk->app_index);
1096 ctx_index = quic_ctx_alloc ();
1097 ctx = quic_ctx_get (ctx_index);
1098 clib_memcpy (ctx, ho_ctx, sizeof (*ctx));
1099 quic_ctx_half_open_reader_unlock (); // TODO: this is a race
1100 quic_ctx_half_open_free (ho_ctx_idx);
1102 ctx->c_thread_index = vlib_get_thread_index ();
1103 ctx->c_c_index = ctx_index;
1105 QUIC_DBG (1, "Quic connect for returned %u. New connection [%u]%x",
1106 is_fail, vlib_get_thread_index (), (ctx) ? ctx_index : ~0);
1108 ctx->c_quic_ctx_id.quic_session = session_handle (s);
1109 s->opaque = ctx_index;
1110 s->session_state = SESSION_STATE_READY;
1112 // Init QUIC lib connection
1113 // Generate required sockaddr & salen
1114 tc = session_get_transport (s);
1115 quic_build_sockaddr (sa, &salen, &tc->rmt_ip, tc->rmt_port, tc->is_ip4);
1118 quicly_connect (&ctx->c_quic_ctx_id.conn,
1119 (quicly_context_t *) app->quicly_ctx,
1120 (char *) ctx->srv_hostname, sa, salen,
1121 &quic_main.next_cid, &quic_main.hs_properties, NULL);
1122 ++quic_main.next_cid.master_id;
1123 // Save context handle in quicly connection
1124 *quicly_get_data (ctx->c_quic_ctx_id.conn) = (void *) (u64) ctx_index;
1127 if (quic_send_packets (ctx))
1129 quic_connection_closed (ctx_index);
1135 quic_session_disconnect_callback (session_t * s)
1137 clib_warning ("UDP session disconnected???");
1141 quic_session_reset_callback (session_t * s)
1143 clib_warning ("UDP session reset???");
1147 quic_add_segment_callback (u32 client_index, u64 seg_handle)
1149 QUIC_DBG (2, "Called quic_add_segment_callback");
1150 QUIC_DBG (2, "NOT IMPLEMENTED");
1151 /* No-op for builtin */
1156 quic_del_segment_callback (u32 client_index, u64 seg_handle)
1158 QUIC_DBG (2, "Called quic_del_segment_callback");
1159 QUIC_DBG (2, "NOT IMPLEMENTED");
1160 /* No-op for builtin */
1165 quic_add_vpp_q_builtin_tx_evt (session_t * s)
1167 if (svm_fifo_set_event (s->tx_fifo))
1168 session_send_io_evt_to_thread_custom (s, s->thread_index,
1169 FIFO_EVENT_BUILTIN_TX);
1174 quic_open_stream_if_ready (quic_ctx_t * ctx)
1176 quicly_conn_t *conn = ctx->c_quic_ctx_id.conn;
1179 QUIC_DBG (2, "----------- > FOUND Stream id %d",
1180 ctx->stream->stream_id);
1181 QUIC_DBG (2, "----------- > FOUND Stream is_open %d",
1182 ctx->stream->sendstate.is_open);
1185 if (quicly_connection_is_ready (conn))
1186 assert (!quicly_open_stream (conn, &ctx->stream, 0));
1187 QUIC_DBG (2, "Stream id %d", ctx->stream->stream_id);
1188 QUIC_DBG (2, "Stream is_open %d", ctx->stream->sendstate.is_open);
1192 quic_custom_tx_callback (void *session)
1194 QUIC_DBG (2, "Called quic_custom_tx_callback");
1195 session_t *app_session = (session_t *) session;
1202 (app_session->session_state >= SESSION_STATE_TRANSPORT_CLOSING))
1204 ctx = quic_ctx_get (app_session->connection_index);
1205 quic_open_stream_if_ready (ctx);
1208 quic_add_vpp_q_builtin_tx_evt (app_session);
1212 f = app_session->tx_fifo;
1213 deq_max = svm_fifo_max_dequeue (f);
1217 data = svm_fifo_head (f);
1218 if (quicly_streambuf_egress_write (ctx->stream, data, deq_max))
1223 QUIC_DBG (2, "Sent %u bytes", deq_max);
1224 svm_fifo_dequeue_drop (f, deq_max);
1225 if (quic_send_packets (ctx))
1227 quic_connection_closed (ctx->c_c_index);
1233 quic_find_packet_ctx (quic_ctx_t ** ctx, quicly_conn_t ** conn,
1234 struct sockaddr *sa, socklen_t salen,
1235 quicly_decoded_packet_t packet)
1238 quicly_conn_t *conn_;
1240 pool_foreach (ctx_, quic_main.ctx_pool[vlib_get_thread_index()],
1242 conn_ = ctx_->c_quic_ctx_id.conn;
1243 if (conn_ && !ctx_->is_listener)
1245 if (quicly_is_destination(conn_, sa, salen, &packet))
1249 QUIC_DBG (2, "connection_found");
1259 quic_receive (quic_ctx_t * ctx, quicly_conn_t * conn,
1260 quicly_decoded_packet_t packet)
1262 quicly_receive (conn, &packet);
1263 // Conn may be set to null if the connection is terminated
1264 if (ctx->c_quic_ctx_id.conn
1265 && ctx->c_quic_ctx_id.conn_state == QUIC_CONN_STATE_HANDSHAKE)
1267 if (quicly_connection_is_ready (conn))
1269 ctx->c_quic_ctx_id.conn_state = QUIC_CONN_STATE_READY;
1270 if (quicly_is_client (conn))
1271 session_send_rpc_evt_to_thread_force (vlib_get_thread_index (),
1272 &quic_delayed_notify_app_connected,
1277 if (quic_send_packets (ctx))
1279 quic_connection_closed (ctx->c_c_index);
1285 quic_delayed_create_app_session (void *ctx_index)
1287 quic_ctx_t *lctx, *ctx;
1288 session_t *app_session, *app_listen_session;
1289 app_worker_t *app_wrk;
1292 ctx = quic_ctx_get ((u32) (u64) ctx_index);
1293 app_session = session_alloc (ctx->c_thread_index);
1294 app_session->session_state = SESSION_STATE_LISTENING;
1295 ctx->c_s_index = app_session->session_index;
1297 lctx = quic_ctx_get (ctx->c_quic_ctx_id.listener_ctx_id);
1299 app_listen_session =
1300 listen_session_get_from_handle (lctx->c_quic_ctx_id.app_session);
1301 app_session->app_wrk_index = lctx->c_quic_ctx_id.parent_app_wrk_idx;
1302 app_session->connection_index = ctx->c_c_index;
1303 app_session->session_type = app_listen_session->session_type;
1304 app_session->listener_index = app_listen_session->session_index;
1305 app_session->app_index = quic_main.app_index;
1307 // TODO: don't alloc fifos when we don't transfer data on this session
1308 if ((rv = app_worker_init_accepted (app_session)))
1310 QUIC_DBG (1, "failed to allocate fifos");
1311 session_free (app_session);
1314 ctx->c_quic_ctx_id.app_session = session_handle (app_session);
1315 ctx->c_quic_ctx_id.parent_app_id = lctx->c_quic_ctx_id.parent_app_id;
1316 ctx->c_quic_ctx_id.udp_is_ip4 = lctx->c_quic_ctx_id.udp_is_ip4;
1317 ctx->c_quic_ctx_id.parent_app_wrk_idx = app_session->app_wrk_index;
1318 session_lookup_add_connection (&ctx->connection,
1319 session_handle (app_session));
1320 app_wrk = app_worker_get (app_session->app_wrk_index);
1321 rv = app_worker_accept_notify (app_wrk, app_session);
1324 QUIC_DBG (1, "failed to notify accept worker app");
1331 quic_create_connection (quicly_context_t * quicly_ctx,
1332 u64 quic_session_handle, u32 lctx_index,
1333 quicly_conn_t * conn, struct sockaddr *sa,
1334 socklen_t salen, quicly_decoded_packet_t packet)
1339 /* new connection, accept and create context if packet is valid */
1340 // TODO: check if socket is actually listening?
1341 QUIC_DBG (2, "New connection created");
1342 if (quicly_accept (&conn, quicly_ctx, sa, salen,
1343 &packet, ptls_iovec_init (NULL, 0),
1344 &quic_main.next_cid, NULL) != 0)
1346 // Invalid packet, pass
1347 assert (conn == NULL);
1348 QUIC_DBG (2, "Accept failed");
1351 assert (conn != NULL);
1353 ++quic_main.next_cid.master_id;
1355 ctx_index = quic_ctx_alloc ();
1356 ctx = quic_ctx_get (ctx_index);
1357 // Save ctx handle in quicly connection
1358 *quicly_get_data (conn) = (void *) (u64) ctx_index;
1360 ctx->c_thread_index = vlib_get_thread_index ();
1361 ctx->c_c_index = ctx_index;
1362 ctx->c_quic_ctx_id.quic_session = quic_session_handle;
1363 ctx->c_quic_ctx_id.listener_ctx_id = lctx_index;
1364 ctx->c_quic_ctx_id.timer_handle = QUIC_TIMER_HANDLE_INVALID;
1365 ctx->c_quic_ctx_id.conn = conn;
1367 session_send_rpc_evt_to_thread_force (vlib_get_thread_index (),
1368 &quic_delayed_create_app_session,
1369 (void *) (u64) ctx_index);
1370 if (quic_send_packets (ctx))
1372 quic_connection_closed (ctx_index);
1378 quic_reset_connection (quicly_context_t * quicly_ctx, u64 quic_session_handle,
1379 struct sockaddr *sa, socklen_t salen,
1380 quicly_decoded_packet_t packet)
1382 /* short header packet; potentially a dead connection. No need to check the length of the incoming packet,
1383 * because loop is prevented by authenticating the CID (by checking node_id and thread_id). If the peer is also
1384 * sending a reset, then the next CID is highly likely to contain a non-authenticating CID, ... */
1385 QUIC_DBG (2, "Sending stateless reset");
1386 quicly_datagram_t *dgram;
1387 session_t *quic_session;
1388 if (packet.cid.dest.plaintext.node_id == 0
1389 && packet.cid.dest.plaintext.thread_id == 0)
1391 dgram = quicly_send_stateless_reset (quicly_ctx, sa, salen,
1392 &packet.cid.dest.plaintext);
1393 quic_session = session_get_from_handle (quic_session_handle);
1394 if (quic_send_datagram (quic_session, dgram)) // TODO : missing event on fifo
1395 QUIC_DBG (2, "Send reset failed");
1401 quic_app_rx_callback (session_t * quic_session)
1403 // Read data from UDP rx_fifo and pass it to the quicly conn.
1404 QUIC_DBG (2, "Called quic_app_rx_callback");
1406 quicly_decoded_packet_t packet;
1407 session_dgram_hdr_t ph;
1409 quicly_conn_t *conn = NULL;
1410 quic_ctx_t *lctx, *ctx = NULL;
1413 struct sockaddr_in6 sa6;
1414 struct sockaddr *sa = (struct sockaddr *) &sa6;
1418 u32 lctx_index = quic_session->opaque;
1419 u64 quic_session_handle = session_handle (quic_session);
1421 f = quic_session->rx_fifo;
1426 max_deq = svm_fifo_max_dequeue (f);
1427 if (max_deq < sizeof (session_dgram_hdr_t))
1429 svm_fifo_unset_event (f);
1432 QUIC_DBG (2, "Processing one packet at %ld", quic_get_time (NULL));
1434 svm_fifo_unset_event (f);
1435 svm_fifo_peek (f, 0, sizeof (ph), (u8 *) & ph);
1436 ASSERT (ph.data_length >= ph.data_offset);
1437 len = ph.data_length - ph.data_offset;
1439 quic_build_sockaddr (sa, &salen, &ph.rmt_ip, ph.rmt_port, ph.is_ip4);
1441 // Quicly can read len bytes from the fifo at offset:
1442 // ph.data_offset + SESSION_CONN_HDR_LEN
1443 data = svm_fifo_head (f) + ph.data_offset + SESSION_CONN_HDR_LEN;
1445 lctx = quic_ctx_get (lctx_index);
1446 app = application_get (lctx->c_quic_ctx_id.parent_app_id);
1449 quicly_decode_packet ((quicly_context_t *) app->quicly_ctx, &packet,
1451 if (plen != SIZE_MAX)
1453 quic_find_packet_ctx (&ctx, &conn, sa, salen, packet);
1455 quic_receive (ctx, conn, packet);
1456 else if (QUICLY_PACKET_IS_LONG_HEADER (packet.octets.base[0]))
1457 quic_create_connection ((quicly_context_t *) app->quicly_ctx,
1458 quic_session_handle, lctx_index, conn,
1460 else if (((quicly_context_t *) app->quicly_ctx)->encrypt_cid)
1461 quic_reset_connection ((quicly_context_t *) app->quicly_ctx,
1462 quic_session_handle, sa, salen, packet);
1464 svm_fifo_dequeue_drop (f,
1465 ph.data_length + ph.data_offset +
1466 SESSION_CONN_HDR_LEN);
1472 /*****************************************************************************
1473 * END TRANSPORT PROTO FUNCTIONS
1474 *****************************************************************************/
1477 static session_cb_vft_t quic_app_cb_vft = {
1478 .session_accept_callback = NULL,
1479 .session_disconnect_callback = quic_session_disconnect_callback,
1480 .session_connected_callback = quic_session_connected_callback,
1481 .session_reset_callback = quic_session_reset_callback,
1482 .add_segment_callback = quic_add_segment_callback,
1483 .del_segment_callback = quic_del_segment_callback,
1484 .builtin_app_rx_callback = quic_app_rx_callback,
1487 const static transport_proto_vft_t quic_proto = {
1488 .connect = quic_connect,
1489 .close = quic_disconnect,
1490 .start_listen = quic_start_listen,
1491 .stop_listen = quic_stop_listen,
1492 .get_connection = quic_connection_get,
1493 .get_listener = quic_listener_get,
1494 .update_time = quic_update_time,
1495 .custom_tx = quic_custom_tx_callback,
1496 .tx_type = TRANSPORT_TX_INTERNAL,
1497 .service_type = TRANSPORT_SERVICE_APP,
1498 .format_connection = format_quic_connection,
1499 .format_half_open = format_quic_half_open,
1500 .format_listener = format_quic_listener,
1504 static clib_error_t *
1505 quic_init (vlib_main_t * vm)
1507 QUIC_DBG (2, "Called quic_init");
1508 vlib_thread_main_t *vtm = vlib_get_thread_main ();
1509 vnet_app_attach_args_t _a, *a = &_a;
1510 u64 options[APP_OPTIONS_N_OPTIONS];
1511 u32 segment_size = 512 << 20;
1512 quic_main_t *qm = &quic_main;
1513 u32 fifo_size = 64 << 10;
1516 num_threads = 1 /* main thread */ + vtm->n_threads;
1518 memset (a, 0, sizeof (*a));
1519 memset (options, 0, sizeof (options));
1521 a->session_cb_vft = &quic_app_cb_vft;
1522 a->api_client_index = APP_INVALID_INDEX;
1523 a->options = options;
1524 a->name = format (0, "quic");
1525 a->options[APP_OPTIONS_SEGMENT_SIZE] = segment_size;
1526 a->options[APP_OPTIONS_RX_FIFO_SIZE] = fifo_size;
1527 a->options[APP_OPTIONS_TX_FIFO_SIZE] = fifo_size;
1528 a->options[APP_OPTIONS_FLAGS] = APP_OPTIONS_FLAGS_IS_BUILTIN;
1529 a->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_USE_GLOBAL_SCOPE;
1530 a->options[APP_OPTIONS_FLAGS] |= APP_OPTIONS_FLAGS_IS_TRANSPORT_APP;
1532 if (vnet_application_attach (a))
1534 clib_warning ("failed to attach quic app");
1535 return clib_error_return (0, "failed to attach quic app");
1538 vec_validate (qm->ctx_pool, num_threads - 1);
1539 vec_validate (qm->wrk_ctx, num_threads - 1);
1540 // Timers, one per thread.
1541 tw_timer_wheel_1t_3w_1024sl_ov_t *tw;
1543 foreach_vlib_main (({
1544 tw = &qm->wrk_ctx[ii].timer_wheel;
1545 tw_timer_wheel_init_1t_3w_1024sl_ov (tw, quic_expired_timers_dispatch,
1546 10e-3 /* timer period 1ms */ , ~0);
1547 tw->last_run_time = vlib_time_now (this_vlib_main);
1551 if (!qm->ca_cert_path)
1552 qm->ca_cert_path = QUIC_DEFAULT_CA_CERT_PATH;
1554 qm->app_index = a->app_index;
1555 clib_rwlock_init (&qm->half_open_rwlock);
1556 qm->tstamp_ticks_per_clock = vm->clib_time.seconds_per_clock
1557 / QUIC_TSTAMP_RESOLUTION;
1559 transport_register_protocol (TRANSPORT_PROTO_QUIC, &quic_proto,
1560 FIB_PROTOCOL_IP4, ~0);
1561 transport_register_protocol (TRANSPORT_PROTO_QUIC, &quic_proto,
1562 FIB_PROTOCOL_IP6, ~0);
1569 vnet_quic_get_main (void)
1574 VLIB_INIT_FUNCTION (quic_init);
1577 VLIB_PLUGIN_REGISTER () =
1579 .version = VPP_BUILD_VER,
1580 .description = "Quic transport protocol",
1585 * fd.io coding-style-patch-verification: ON
1588 * eval: (c-set-style "gnu")