From 5b8b1aec213baf65d3051c849f5f3deff37d06b5 Mon Sep 17 00:00:00 2001 From: Florin Coras Date: Wed, 16 Dec 2020 17:05:56 -0800 Subject: tls: add custom openssl bio The bio interacts directly with the session so it avoids using an intermediary mem bio and, implicitly, higher memory consumption and an extra memcpy. Type: improvement Signed-off-by: Florin Coras Change-Id: Ifb675cfd12df86396a7a738a6cd4d0882c69ad2f --- src/plugins/tlsopenssl/CMakeLists.txt | 3 +- src/plugins/tlsopenssl/tls_bio.c | 182 +++++++++++++++++++++++++++++ src/plugins/tlsopenssl/tls_bio.h | 31 +++++ src/plugins/tlsopenssl/tls_openssl.c | 209 +++++----------------------------- src/plugins/tlsopenssl/tls_openssl.h | 3 + src/vnet/tls/tls.c | 1 + 6 files changed, 245 insertions(+), 184 deletions(-) create mode 100644 src/plugins/tlsopenssl/tls_bio.c create mode 100644 src/plugins/tlsopenssl/tls_bio.h diff --git a/src/plugins/tlsopenssl/CMakeLists.txt b/src/plugins/tlsopenssl/CMakeLists.txt index 8ac36cfe3fd..61d3638a39a 100644 --- a/src/plugins/tlsopenssl/CMakeLists.txt +++ b/src/plugins/tlsopenssl/CMakeLists.txt @@ -12,10 +12,11 @@ # limitations under the License. include (CheckFunctionExists) -if(OPENSSL_FOUND) +if(OPENSSL_FOUND AND OPENSSL_VERSION VERSION_GREATER_EQUAL "1.1.0") include_directories(${OPENSSL_INCLUDE_DIR}) add_vpp_plugin(tlsopenssl SOURCES + tls_bio.c tls_openssl.c tls_openssl_api.c tls_async.c diff --git a/src/plugins/tlsopenssl/tls_bio.c b/src/plugins/tlsopenssl/tls_bio.c new file mode 100644 index 00000000000..5cdedf649a7 --- /dev/null +++ b/src/plugins/tlsopenssl/tls_bio.c @@ -0,0 +1,182 @@ + +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include + +static inline session_t * +bio_session (BIO * bio) +{ + return session_get_from_handle (pointer_to_uword (BIO_get_data (bio))); +} + +static int +bio_tls_alloc (BIO * bio) +{ + BIO_set_init (bio, 0); + BIO_set_data (bio, 0); + BIO_set_flags (bio, 0); + BIO_set_shutdown (bio, 0); + return 1; +} + +static int +bio_tls_free (BIO * bio) +{ + if (!bio) + return 0; + + if (BIO_get_shutdown (bio)) + { + if (BIO_get_init (bio)) + session_close (bio_session (bio)); + BIO_set_init (bio, 0); + BIO_set_flags (bio, 0); + } + + return 1; +} + +static int +bio_tls_read (BIO * b, char *out, int outl) +{ + session_t *s; + int rv; + + if (PREDICT_FALSE (!out)) + return 0; + + s = bio_session (b); + if (!s) + { + clib_warning ("no session"); + errno = EBADFD; + return -1; + } + + rv = app_recv_stream_raw (s->rx_fifo, (u8 *) out, outl, + 0 /* clear evt */ , 0 /* peek */ ); + if (rv < 0) + { + BIO_set_retry_read (b); + errno = EAGAIN; + return -1; + } + + if (svm_fifo_is_empty_cons (s->rx_fifo)) + svm_fifo_unset_event (s->rx_fifo); + + BIO_clear_retry_flags (b); + + return rv; +} + +static int +bio_tls_write (BIO * b, const char *in, int inl) +{ + svm_msg_q_t *mq; + session_t *s; + int rv; + + if (PREDICT_FALSE (!in)) + return 0; + + s = bio_session (b); + if (!s) + { + clib_warning ("no session"); + errno = EBADFD; + return -1; + } + + mq = session_main_get_vpp_event_queue (s->thread_index); + rv = app_send_stream_raw (s->tx_fifo, mq, (u8 *) in, inl, + SESSION_IO_EVT_TX, 1 /* do_evt */ , + 0 /* noblock */ ); + if (rv < 0) + { + BIO_set_retry_write (b); + errno = EAGAIN; + return -1; + } + + BIO_clear_retry_flags (b); + + return rv; +} + +long +bio_tls_ctrl (BIO * b, int cmd, long larg, void *ptr) +{ + long ret = 1; + + switch (cmd) + { + case BIO_C_SET_FD: + ASSERT (0); + break; + case BIO_C_GET_FD: + ASSERT (0); + break; + case BIO_CTRL_GET_CLOSE: + ret = BIO_get_shutdown (b); + break; + case BIO_CTRL_SET_CLOSE: + BIO_set_shutdown (b, (int) larg); + break; + case BIO_CTRL_DUP: + case BIO_CTRL_FLUSH: + ret = 1; + break; + case BIO_CTRL_PENDING: + ret = 0; + break; + default: + ret = 0; + break; + } + return ret; +} + +BIO * +BIO_new_tls (session_handle_t sh) +{ + static BIO_METHOD *tls_bio_method; + BIO *b; + if (!tls_bio_method) + { + tls_bio_method = BIO_meth_new (BIO_TYPE_SOCKET, "tls_bio"); + BIO_meth_set_write (tls_bio_method, bio_tls_write); + BIO_meth_set_read (tls_bio_method, bio_tls_read); + BIO_meth_set_create (tls_bio_method, bio_tls_alloc); + BIO_meth_set_destroy (tls_bio_method, bio_tls_free); + BIO_meth_set_ctrl (tls_bio_method, bio_tls_ctrl); + } + b = BIO_new (tls_bio_method); + /* Initialize the BIO */ + BIO_set_data (b, uword_to_pointer (sh, void *)); + BIO_set_init (b, 1); + return b; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/tlsopenssl/tls_bio.h b/src/plugins/tlsopenssl/tls_bio.h new file mode 100644 index 00000000000..b9f3c5384b9 --- /dev/null +++ b/src/plugins/tlsopenssl/tls_bio.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SRC_PLUGINS_TLSOPENSSL_TLS_BIO_H_ +#define SRC_PLUGINS_TLSOPENSSL_TLS_BIO_H_ + +#include + +BIO *BIO_new_tls (session_handle_t sh); + +#endif /* SRC_PLUGINS_TLSOPENSSL_TLS_BIO_H_ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/tlsopenssl/tls_openssl.c b/src/plugins/tlsopenssl/tls_openssl.c index 669a503487f..5e49dbfe6c9 100644 --- a/src/plugins/tlsopenssl/tls_openssl.c +++ b/src/plugins/tlsopenssl/tls_openssl.c @@ -26,6 +26,7 @@ #include #include #include +#include #define MAX_CRYPTO_LEN 64 @@ -109,39 +110,6 @@ openssl_lctx_get (u32 lctx_index) return pool_elt_at_index (openssl_main.lctx_pool, lctx_index); } -static int -openssl_read_from_bio_into_fifo (svm_fifo_t * f, BIO * bio, u32 enq_max) -{ - svm_fifo_chunk_t *c; - int read, rv; - u32 enq_now; - - svm_fifo_fill_chunk_list (f); - - enq_now = clib_min (svm_fifo_max_write_chunk (f), enq_max); - if (!enq_now) - return 0; - - read = BIO_read (bio, svm_fifo_tail (f), enq_now); - if (read <= 0) - return 0; - - c = svm_fifo_tail_chunk (f); - while ((c = c->next) && read < enq_max) - { - enq_now = clib_min (c->length, enq_max - read); - rv = BIO_read (bio, c->data, enq_now); - read += rv > 0 ? rv : 0; - - if (rv < enq_now) - break; - } - - svm_fifo_enqueue_nocopy (f, read); - - return read; -} - static int openssl_read_from_ssl_into_fifo (svm_fifo_t * f, SSL * ssl) { @@ -179,34 +147,6 @@ openssl_read_from_ssl_into_fifo (svm_fifo_t * f, SSL * ssl) return read; } -static int -openssl_write_from_fifo_into_bio (svm_fifo_t * f, BIO * bio, u32 len) -{ - svm_fifo_chunk_t *c; - int wrote, rv; - u32 deq_now; - - deq_now = clib_min (svm_fifo_max_read_chunk (f), len); - wrote = BIO_write (bio, svm_fifo_head (f), deq_now); - if (wrote <= 0) - return 0; - - c = svm_fifo_head_chunk (f); - while ((c = c->next) && wrote < len) - { - deq_now = clib_min (c->length, len - wrote); - rv = BIO_write (bio, c->data, deq_now); - wrote += rv > 0 ? rv : 0; - - if (rv < deq_now) - break; - } - - svm_fifo_dequeue_drop (f, wrote); - - return wrote; -} - static int openssl_write_from_fifo_into_ssl (svm_fifo_t * f, SSL * ssl, u32 len) { @@ -235,40 +175,6 @@ openssl_write_from_fifo_into_ssl (svm_fifo_t * f, SSL * ssl, u32 len) return wrote; } -static int -openssl_try_handshake_read (openssl_ctx_t * oc, session_t * tls_session) -{ - svm_fifo_t *f; - u32 deq_max; - - f = tls_session->rx_fifo; - deq_max = svm_fifo_max_dequeue_cons (f); - if (!deq_max) - return 0; - - return openssl_write_from_fifo_into_bio (f, oc->wbio, deq_max); -} - -static int -openssl_try_handshake_write (openssl_ctx_t * oc, session_t * tls_session) -{ - u32 read, enq_max; - - if (BIO_ctrl_pending (oc->rbio) <= 0) - return 0; - - enq_max = svm_fifo_max_enqueue_prod (tls_session->tx_fifo); - if (!enq_max) - return 0; - - read = openssl_read_from_bio_into_fifo (tls_session->tx_fifo, oc->rbio, - enq_max); - if (read) - tls_add_vpp_q_tx_evt (tls_session); - - return read; -} - #ifdef HAVE_OPENSSL_ASYNC static int openssl_check_async_status (tls_ctx_t * ctx, openssl_resume_handler * handler, @@ -334,7 +240,7 @@ openssl_ctx_handshake_rx (tls_ctx_t * ctx, session_t * tls_session) { ctx->resume = 0; } - else if (!openssl_try_handshake_read (oc, tls_session)) + else if (!svm_fifo_max_dequeue_cons (tls_session->rx_fifo)) break; rv = SSL_do_handshake (oc->ssl); @@ -357,9 +263,7 @@ openssl_ctx_handshake_rx (tls_ctx_t * ctx, session_t * tls_session) return -1; } - openssl_try_handshake_write (oc, tls_session); - - if (err != SSL_ERROR_WANT_WRITE) + if (err != SSL_ERROR_WANT_WRITE && err != SSL_ERROR_WANT_READ) break; } TLS_DBG (2, "tls state for %u is %s", oc->openssl_ctx_index, @@ -418,25 +322,26 @@ openssl_ctx_write (tls_ctx_t * ctx, session_t * app_session, transport_send_params_t * sp) { openssl_ctx_t *oc = (openssl_ctx_t *) ctx; - int wrote = 0, read, max_buf = 4 * TLS_CHUNK_SIZE, max_space, n_pending; - u32 deq_max, to_write, enq_max; - session_t *tls_session; + u32 deq_max, space, enq_buf; + session_t *ts; + int wrote = 0; svm_fifo_t *f; + ts = session_get_from_handle (ctx->tls_session_handle); + space = svm_fifo_max_enqueue_prod (ts->tx_fifo); + /* Leave a bit of extra space for tls ctrl data, if any needed */ + space = clib_max ((int) space - TLSO_CTRL_BYTES, 0); + f = app_session->tx_fifo; deq_max = svm_fifo_max_dequeue_cons (f); + deq_max = clib_min (deq_max, space); if (!deq_max) goto check_tls_fifo; deq_max = clib_min (deq_max, sp->max_burst_size); - /* Figure out how much data to write */ - max_space = max_buf - BIO_ctrl_pending (oc->rbio); - max_space = (max_space < 0) ? 0 : max_space; - to_write = clib_min (deq_max, (u32) max_space); - - wrote = openssl_write_from_fifo_into_ssl (f, oc->ssl, to_write); + wrote = openssl_write_from_fifo_into_ssl (f, oc->ssl, deq_max); if (!wrote) goto check_tls_fifo; @@ -445,31 +350,11 @@ openssl_ctx_write (tls_ctx_t * ctx, session_t * app_session, check_tls_fifo: - if ((n_pending = BIO_ctrl_pending (oc->rbio)) <= 0) - return wrote; - - tls_session = session_get_from_handle (ctx->tls_session_handle); - - enq_max = svm_fifo_max_enqueue_prod (tls_session->tx_fifo); - if (!enq_max) - goto maybe_reschedule; - - read = openssl_read_from_bio_into_fifo (tls_session->tx_fifo, oc->rbio, - enq_max); - if (!read) - goto maybe_reschedule; - - tls_add_vpp_q_tx_evt (tls_session); - - if (PREDICT_FALSE (ctx->app_closed && !BIO_ctrl_pending (oc->rbio))) - openssl_confirm_app_close (ctx); - -maybe_reschedule: - - if (!svm_fifo_max_enqueue_prod (tls_session->tx_fifo)) + /* Deschedule and wait for deq notification if fifo is almost full */ + enq_buf = clib_min (svm_fifo_size (ts->tx_fifo) / 2, TLSO_MIN_ENQ_SPACE); + if (space < wrote + enq_buf) { - svm_fifo_add_want_deq_ntf (tls_session->tx_fifo, - SVM_FIFO_WANT_DEQ_NOTIF); + svm_fifo_add_want_deq_ntf (ts->tx_fifo, SVM_FIFO_WANT_DEQ_NOTIF); transport_connection_deschedule (&ctx->connection); sp->flags |= TRANSPORT_SND_F_DESCHED; } @@ -483,57 +368,26 @@ maybe_reschedule: static inline int openssl_ctx_read (tls_ctx_t * ctx, session_t * tls_session) { - int read, wrote = 0, max_space, max_buf = 4 * TLS_CHUNK_SIZE; openssl_ctx_t *oc = (openssl_ctx_t *) ctx; - u32 deq_max, to_write; session_t *app_session; + int read, wrote = 0; svm_fifo_t *f; if (PREDICT_FALSE (SSL_in_init (oc->ssl))) { if (openssl_ctx_handshake_rx (ctx, tls_session) < 0) return 0; - else - goto check_app_fifo; - } - - f = tls_session->rx_fifo; - - deq_max = svm_fifo_max_dequeue_cons (f); - max_space = max_buf - BIO_ctrl_pending (oc->wbio); - max_space = max_space < 0 ? 0 : max_space; - to_write = clib_min (deq_max, max_space); - if (!to_write) - goto check_app_fifo; - - wrote = openssl_write_from_fifo_into_bio (f, oc->wbio, to_write); - if (!wrote) - { - tls_add_vpp_q_builtin_rx_evt (tls_session); - goto check_app_fifo; } - if (svm_fifo_max_dequeue_cons (f)) - tls_add_vpp_q_builtin_rx_evt (tls_session); - -check_app_fifo: - - if (BIO_ctrl_pending (oc->wbio) <= 0) - return wrote; - app_session = session_get_from_handle (ctx->app_session_handle); f = app_session->rx_fifo; read = openssl_read_from_ssl_into_fifo (f, oc->ssl); - if (!read) - { - tls_add_vpp_q_builtin_rx_evt (tls_session); - return wrote; - } /* If handshake just completed, session may still be in accepting state */ - if (app_session->session_state >= SESSION_STATE_READY) + if (read && app_session->session_state >= SESSION_STATE_READY) tls_notify_app_enqueue (ctx, app_session); + if (SSL_pending (oc->ssl) > 0) tls_add_vpp_q_builtin_rx_evt (tls_session); @@ -546,7 +400,6 @@ openssl_ctx_init_client (tls_ctx_t * ctx) long flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION; openssl_ctx_t *oc = (openssl_ctx_t *) ctx; openssl_main_t *om = &openssl_main; - session_t *tls_session; const SSL_METHOD *method; int rv, err; @@ -587,11 +440,8 @@ openssl_ctx_init_client (tls_ctx_t * ctx) return -1; } - oc->rbio = BIO_new (BIO_s_mem ()); - oc->wbio = BIO_new (BIO_s_mem ()); - - BIO_set_mem_eof_return (oc->rbio, -1); - BIO_set_mem_eof_return (oc->wbio, -1); + oc->rbio = BIO_new_tls (ctx->tls_session_handle); + oc->wbio = BIO_new_tls (ctx->tls_session_handle); SSL_set_bio (oc->ssl, oc->wbio, oc->rbio); SSL_set_connect_state (oc->ssl); @@ -609,16 +459,14 @@ openssl_ctx_init_client (tls_ctx_t * ctx) TLS_DBG (1, "Initiating handshake for [%u]%u", ctx->c_thread_index, oc->openssl_ctx_index); - tls_session = session_get_from_handle (ctx->tls_session_handle); - #ifdef HAVE_OPENSSL_ASYNC + session_t *tls_session = session_get_from_handle (ctx->tls_session_handle); vpp_tls_async_init_event (ctx, openssl_ctx_handshake_rx, tls_session); #endif while (1) { rv = SSL_do_handshake (oc->ssl); err = SSL_get_error (oc->ssl, rv); - openssl_try_handshake_write (oc, tls_session); #ifdef HAVE_OPENSSL_ASYNC if (err == SSL_ERROR_WANT_ASYNC) { @@ -751,7 +599,6 @@ openssl_ctx_init_server (tls_ctx_t * ctx) openssl_ctx_t *oc = (openssl_ctx_t *) ctx; u32 olc_index = ctx->tls_ssl_ctx; openssl_listen_ctx_t *olc; - session_t *tls_session; int rv, err; /* Start a new connection */ @@ -764,11 +611,8 @@ openssl_ctx_init_server (tls_ctx_t * ctx) return -1; } - oc->rbio = BIO_new (BIO_s_mem ()); - oc->wbio = BIO_new (BIO_s_mem ()); - - BIO_set_mem_eof_return (oc->rbio, -1); - BIO_set_mem_eof_return (oc->wbio, -1); + oc->rbio = BIO_new_tls (ctx->tls_session_handle); + oc->wbio = BIO_new_tls (ctx->tls_session_handle); SSL_set_bio (oc->ssl, oc->wbio, oc->rbio); SSL_set_accept_state (oc->ssl); @@ -776,15 +620,14 @@ openssl_ctx_init_server (tls_ctx_t * ctx) TLS_DBG (1, "Initiating handshake for [%u]%u", ctx->c_thread_index, oc->openssl_ctx_index); - tls_session = session_get_from_handle (ctx->tls_session_handle); #ifdef HAVE_OPENSSL_ASYNC + session_t *tls_session = session_get_from_handle (ctx->tls_session_handle); vpp_tls_async_init_event (ctx, openssl_ctx_handshake_rx, tls_session); #endif while (1) { rv = SSL_do_handshake (oc->ssl); err = SSL_get_error (oc->ssl, rv); - openssl_try_handshake_write (oc, tls_session); #ifdef HAVE_OPENSSL_ASYNC if (err == SSL_ERROR_WANT_ASYNC) { diff --git a/src/plugins/tlsopenssl/tls_openssl.h b/src/plugins/tlsopenssl/tls_openssl.h index 5744dcadb3a..16b256d0ceb 100644 --- a/src/plugins/tlsopenssl/tls_openssl.h +++ b/src/plugins/tlsopenssl/tls_openssl.h @@ -21,6 +21,9 @@ #include #include +#define TLSO_CTRL_BYTES 1000 +#define TLSO_MIN_ENQ_SPACE (1 << 16) + typedef struct tls_ctx_openssl_ { tls_ctx_t ctx; /**< First */ diff --git a/src/vnet/tls/tls.c b/src/vnet/tls/tls.c index 5b89057f498..59dae88fd1f 100644 --- a/src/vnet/tls/tls.c +++ b/src/vnet/tls/tls.c @@ -739,6 +739,7 @@ tls_custom_tx_callback (void *session, transport_send_params_t * sp) >= SESSION_STATE_TRANSPORT_CLOSED)) return 0; + sp->flags = 0; ctx = tls_ctx_get (app_session->connection_index); return tls_ctx_write (ctx, app_session, sp); } -- cgit 1.2.3-korg