ssl_async is a global configuration parameter which enables asynchronous
processing in OPENSSL for all SSL connections haproxy handles. With
SSL_MODE_ASYNC mode set, TLS I/O operations may indicate a retry with
SSL_ERROR_WANT_ASYNC with this mode set if an asynchronous capable
engine is used to perform cryptographic operations.
---
 doc/configuration.txt      |   5 ++
 include/types/connection.h |   2 +
 include/types/fd.h         |   1 +
 src/ssl_sock.c             | 112 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 120 insertions(+)

diff --git a/doc/configuration.txt b/doc/configuration.txt
index ecd1769..8190920 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -590,6 +590,7 @@ The following keywords are supported in the "global" 
section :
    - server-state-base
    - server-state-file
    - ssl-engine
+   - ssl-async
    - tune.buffers.limit
    - tune.buffers.reserve
    - tune.bufsize
@@ -1256,6 +1257,10 @@ ssl-engine <name> [algo <comma-seperated list of 
algorithms>]
   openssl configuration file uses:
   https://www.openssl.org/docs/man1.0.2/apps/config.html
 
+ssl-async
+  Adds SSL_MODE_ASYNC mode to the SSL context. This enables asynchronous TLS
+  I/O operations if an asynchronous capable SSL engine is used.
+
 tune.buffers.limit <number>
   Sets a hard limit on the number of buffers which may be allocated per 
process.
   The default value is zero which means unlimited. The minimum non-zero value
diff --git a/include/types/connection.h b/include/types/connection.h
index c644dd5..1c8e1a8 100644
--- a/include/types/connection.h
+++ b/include/types/connection.h
@@ -303,6 +303,8 @@ struct connection {
                struct sockaddr_storage from;   /* client address, or address 
to spoof when connecting to the server */
                struct sockaddr_storage to;     /* address reached by the 
client, or address to connect to */
        } addr; /* addresses of the remote side, client for producer and server 
for consumer */
+
+       OSSL_ASYNC_FD async_fd;
 };
 
 /* proxy protocol v2 definitions */
diff --git a/include/types/fd.h b/include/types/fd.h
index 7f63093..f3d03f8 100644
--- a/include/types/fd.h
+++ b/include/types/fd.h
@@ -100,6 +100,7 @@ struct fdtab {
        unsigned char updated:1;             /* 1 if this fd is already in the 
update list */
        unsigned char linger_risk:1;         /* 1 if we must kill lingering 
before closing */
        unsigned char cloned:1;              /* 1 if a cloned socket, requires 
EPOLL_CTL_DEL on close */
+       unsigned char async:1;               /* 1 if this fd is async ssl fd */
 };
 
 /* less often used information */
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index b173d77..ec5777f 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -53,6 +53,7 @@
 #include <openssl/dh.h>
 #endif
 #include <openssl/engine.h>
+#include <openssl/async.h>
 
 #include <import/lru.h>
 #include <import/xxhash.h>
@@ -136,6 +137,7 @@ static struct xprt_ops ssl_sock;
 static struct {
        char *crt_base;             /* base directory path for certificates */
        char *ca_base;              /* base directory path for CAs and CRLs */
+       int  async;                 /* whether we use ssl async mode */
 
        char *listen_default_ciphers;
        char *connect_default_ciphers;
@@ -315,6 +317,54 @@ static int ssl_init_engines(void)
        return err_code;
 }
 
+#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
+void ssl_async_fd_handler(int fd)
+{
+       struct connection *conn = fdtab[fd].owner;
+       int conn_fd = conn->t.sock.fd;
+
+       /* crypto engine is available, let's notify the associated
+        * connection that it can pursue its processing.
+        */
+       conn_fd_handler(conn_fd);
+}
+
+/*
+ * openssl async fd handler
+ */
+void ssl_async_process_fds(struct connection *conn)
+{
+       OSSL_ASYNC_FD add_fd;
+       OSSL_ASYNC_FD del_fd;
+       size_t num_add_fds = 0;
+       size_t num_del_fds = 0;
+       SSL *ssl = conn->xprt_ctx;
+
+       SSL_get_changed_async_fds(ssl, &add_fd, &num_add_fds, &del_fd,
+                       &num_del_fds);
+
+       if (num_add_fds == 0 && num_del_fds == 0)
+               return;
+
+       /* we don't support more than 1 async fds */
+       if (num_add_fds > 1 || num_del_fds > 1)
+               return;
+
+       if (num_del_fds)
+               fd_stop_both(del_fd);
+
+       if (num_add_fds) {
+               conn->async_fd = add_fd;
+               fdtab[add_fd].async = 1;
+               fdtab[add_fd].state = 0;
+               fdtab[add_fd].owner = conn;
+               fdtab[add_fd].iocb = ssl_async_fd_handler;
+               fd_insert(add_fd);
+               fd_want_recv(add_fd);
+       }
+}
+#endif
+
 /*
  *  This function returns the number of seconds  elapsed
  *  since the Epoch, 1970-01-01 00:00:00 +0000 (UTC) and the
@@ -2930,6 +2980,10 @@ int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, 
struct ssl_bind_conf *ssl_
                Alert("OpenSSL random data generator initialization failed.\n");
                cfgerr++;
        }
+#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
+       if (global_ssl.async)
+               sslmode |= SSL_MODE_ASYNC;
+#endif
 
        if (conf_ssl_options & BC_SSL_O_NO_SSLV3)
                ssloptions |= SSL_OP_NO_SSLv3;
@@ -3365,6 +3419,11 @@ int ssl_sock_prepare_srv_ctx(struct server *srv)
 #endif
 #endif
        SSL_CTX_set_options(srv->ssl_ctx.ctx, options);
+
+#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
+       if (global_ssl.async)
+               mode |= SSL_MODE_ASYNC;
+#endif
        SSL_CTX_set_mode(srv->ssl_ctx.ctx, mode);
 
        if (global.ssl_server_verify == SSL_SERVER_VERIFY_REQUIRED)
@@ -3821,6 +3880,14 @@ int ssl_sock_handshake(struct connection *conn, unsigned 
int flag)
                if (ret <= 0) {
                        /* handshake may have not been completed, let's find 
why */
                        ret = SSL_get_error(conn->xprt_ctx, ret);
+
+#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
+                       if (ret == SSL_ERROR_WANT_ASYNC) {
+                               ssl_async_process_fds(conn);
+                               return 0;
+                       }
+#endif
+
                        if (ret == SSL_ERROR_WANT_WRITE) {
                                /* SSL handshake needs to write, L4 connection 
may not be ready */
                                __conn_sock_stop_recv(conn);
@@ -3906,6 +3973,12 @@ int ssl_sock_handshake(struct connection *conn, unsigned 
int flag)
                /* handshake did not complete, let's find why */
                ret = SSL_get_error(conn->xprt_ctx, ret);
 
+#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
+               if (ret == SSL_ERROR_WANT_ASYNC) {
+                       ssl_async_process_fds(conn);
+                       return 0;
+               }
+#endif
                if (ret == SSL_ERROR_WANT_WRITE) {
                        /* SSL handshake needs to write, L4 connection may not 
be ready */
                        __conn_sock_stop_recv(conn);
@@ -4091,6 +4164,12 @@ static int ssl_sock_to_buf(struct connection *conn, 
struct buffer *buf, int coun
                }
                else {
                        ret =  SSL_get_error(conn->xprt_ctx, ret);
+#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
+                       if (ret == SSL_ERROR_WANT_ASYNC) {
+                               ssl_async_process_fds(conn);
+                               break;
+                       }
+#endif
                        if (ret == SSL_ERROR_WANT_WRITE) {
                                /* handshake is running, and it needs to enable 
write */
                                conn->flags |= CO_FL_SSL_WAIT_HS;
@@ -4192,6 +4271,13 @@ static int ssl_sock_from_buf(struct connection *conn, 
struct buffer *buf, int fl
                }
                else {
                        ret = SSL_get_error(conn->xprt_ctx, ret);
+
+#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
+                       if (ret == SSL_ERROR_WANT_ASYNC) {
+                               ssl_async_process_fds(conn);
+                               break;
+                       }
+#endif
                        if (ret == SSL_ERROR_WANT_WRITE) {
                                if (SSL_renegotiate_pending(conn->xprt_ctx)) {
                                        /* handshake is running, and it may 
need to re-enable write */
@@ -4226,6 +4312,15 @@ static int ssl_sock_from_buf(struct connection *conn, 
struct buffer *buf, int fl
 static void ssl_sock_close(struct connection *conn) {
 
        if (conn->xprt_ctx) {
+               if (global_ssl.async) {
+                       /* the async fd is created and owned by the SSL engine, 
which is
+                        * responsible for fd closure. Here we are done with 
the async fd
+                        * thus disable the polling on it, as well as clean up 
fdtab entry.
+                        */
+                       fd_stop_both(conn->async_fd);
+                       fdtab[conn->async_fd].async = 0;
+                       fdtab[conn->async_fd].state = 0;
+               }
                SSL_free(conn->xprt_ctx);
                conn->xprt_ctx = NULL;
                sslconns--;
@@ -6439,6 +6534,22 @@ static int ssl_parse_global_ca_crt_base(char **args, int 
section_type, struct pr
        return 0;
 }
 
+/* parse the "ssl-async" keyword in global section.
+ * Returns <0 on alert, >0 on warning, 0 on success.
+ */
+static int ssl_parse_global_ssl_async(char **args, int section_type, struct 
proxy *curpx,
+                                       struct proxy *defpx, const char *file, 
int line,
+                                       char **err)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
+       global_ssl.async = 1;
+       return 0;
+#else
+       memprintf(err, "'%s': openssl library does not support async mode", 
args[0]);
+       return -1;
+#endif
+}
+
 /* parse the "ssl-engine" keyword in global section.
  * Returns <0 on alert, >0 on warning, 0 on success.
  */
@@ -7054,6 +7165,7 @@ static struct cfg_kw_list cfg_kws = {ILH, {
 #ifndef OPENSSL_NO_DH
        { CFG_GLOBAL, "ssl-dh-param-file", ssl_parse_global_dh_param_file },
 #endif
+       { CFG_GLOBAL, "ssl-async",  ssl_parse_global_ssl_async },
        { CFG_GLOBAL, "ssl-engine",  ssl_parse_global_ssl_engine },
        { CFG_GLOBAL, "tune.ssl.cachesize", ssl_parse_global_int },
 #ifndef OPENSSL_NO_DH
-- 
1.9.1


Reply via email to