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.
---
 include/common/epoll.h     |   2 +
 include/proto/fd.h         |   8 ++++
 include/types/connection.h |   2 +
 include/types/fd.h         |   1 +
 include/types/global.h     |   1 +
 src/cfgparse.c             |  10 +++++
 src/ev_epoll.c             |  11 +++++
 src/fd.c                   |  13 ++++++
 src/haproxy.c              |   1 +
 src/ssl_sock.c             | 104 +++++++++++++++++++++++++++++++++++++++++++++
 10 files changed, 153 insertions(+)

diff --git a/include/common/epoll.h b/include/common/epoll.h
index cc395cd..dd1c7d6 100644
--- a/include/common/epoll.h
+++ b/include/common/epoll.h
@@ -70,6 +70,8 @@ struct epoll_event {
        } data;
 };
 
+int remove_fd_from_epoll(int fd);
+
 #if defined(CONFIG_HAP_LINUX_VSYSCALL) && defined(__linux__) && 
defined(__i386__)
 /* Those are our self-defined functions */
 extern int epoll_create(int size);
diff --git a/include/proto/fd.h b/include/proto/fd.h
index 87309bf..922fee5 100644
--- a/include/proto/fd.h
+++ b/include/proto/fd.h
@@ -40,6 +40,7 @@ extern int fd_nbupdt;               // number of updates in 
the list
  * The file descriptor is also closed.
  */
 void fd_delete(int fd);
+void fd_async_delete(int fd);
 
 /* disable the specified poller */
 void disable_poller(const char *poller_name);
@@ -339,6 +340,13 @@ static inline void fd_insert(int fd)
                maxfd = fd + 1;
 }
 
+/* Prepares async <fd> for being polled */
+static inline void fd_async_insert(int fd)
+{
+       fdtab[fd].state = 0;
+       fdtab[fd].async = 1;
+       fd_insert(fd);
+}
 
 #endif /* _PROTO_FD_H */
 
diff --git a/include/types/connection.h b/include/types/connection.h
index 8b732ff..3233f25 100644
--- a/include/types/connection.h
+++ b/include/types/connection.h
@@ -288,6 +288,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/include/types/global.h b/include/types/global.h
index 9a6e2c9..37727b9 100644
--- a/include/types/global.h
+++ b/include/types/global.h
@@ -85,6 +85,7 @@ struct global {
        char *crt_base;             /* base directory path for certificates */
        char *ca_base;              /* base directory path for CAs and CRLs */
        char *ssl_engine;           /* openssl engine to use */
+       int ssl_async;              /* whether we use ssl async mode */
 #endif
        int uid;
        int gid;
diff --git a/src/cfgparse.c b/src/cfgparse.c
index f8ad855..7eb0593 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -652,6 +652,16 @@ int cfg_parse_global(const char *file, int linenum, char 
**args, int kwm)
                goto out;
 #endif
        }
+       else if (!strcmp(args[0], "ssl_async")) {
+#ifdef USE_OPENSSL
+               global.ssl_async = 1;
+               qfprintf(stdout, "parsing [%s:%d] : turns on SSL async 
mode.\n", file, linenum);
+#else
+               Alert("parsing [%s:%d] : ssl_async is not implemented.\n", 
file, linenum);
+               err_code |= ERR_ALERT | ERR_FATAL;
+               goto out;
+#endif
+       }
        else if (!strcmp(args[0], "daemon")) {
                if (alertif_too_many_args(0, file, linenum, args, &err_code))
                        goto out;
diff --git a/src/ev_epoll.c b/src/ev_epoll.c
index ccb7c33..27d630e 100644
--- a/src/ev_epoll.c
+++ b/src/ev_epoll.c
@@ -41,6 +41,17 @@ static struct epoll_event ev;
 #define EPOLLRDHUP 0x2000
 #endif
 
+int remove_fd_from_epoll(int fd)
+{
+       struct epoll_event ee;
+       int ret;
+
+       ee.events = 0;
+       ee.data.ptr = NULL;
+       ret = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &ee);
+       return ret;
+}
+
 /*
  * Immediately remove file descriptor from epoll set upon close.
  * Since we forked, some fds share inodes with the other process, and epoll may
diff --git a/src/fd.c b/src/fd.c
index aeee602..483f3eb 100644
--- a/src/fd.c
+++ b/src/fd.c
@@ -198,6 +198,19 @@ void fd_delete(int fd)
                maxfd--;
 }
 
+void fd_async_delete(int fd)
+{
+       fd_release_cache_entry(fd);
+
+       fdtab[fd].state = 0;
+       fdtab[fd].async = 0;
+       fdtab[fd].owner = NULL;
+       fdtab[fd].new = 0;
+
+       while ((maxfd-1 >= 0) && !fdtab[maxfd-1].owner)
+               maxfd--;
+}
+
 /* Scan and process the cached events. This should be called right after
  * the poller. The loop may cause new entries to be created, for example
  * if a listener causes an accept() to initiate a new incoming connection
diff --git a/src/haproxy.c b/src/haproxy.c
index 69a4551..8c316c3 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -183,6 +183,7 @@ struct global global = {
 #endif
        },
 #ifdef USE_OPENSSL
+       .ssl_async = 0, /* SSL_MODE_ASYNC is off by default */
 #ifdef DEFAULT_MAXSSLCONN
        .maxsslconn = DEFAULT_MAXSSLCONN,
 #endif
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 0b3cee5..6b31432 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -57,6 +57,7 @@
 #include <import/xxhash.h>
 
 #include <openssl/engine.h>
+#include <openssl/async.h>
 
 #include <common/buffer.h>
 #include <common/compat.h>
@@ -68,6 +69,7 @@
 #include <common/time.h>
 #include <common/cfgparse.h>
 #include <common/base64.h>
+#include <common/epoll.h>
 
 #include <ebsttree.h>
 
@@ -267,6 +269,74 @@ void ssl_init_engine(const char *engine_id)
 }
 
 /*
+ * openssl async fd handler
+ */
+int ssl_async_process_fds(struct connection *conn)
+{
+       OSSL_ASYNC_FD *add_fds = NULL;
+       OSSL_ASYNC_FD *del_fds = NULL;
+       size_t        num_add_fds = 0;
+       size_t        num_del_fds = 0;
+       unsigned int  loop = 0;
+
+       SSL *ssl = conn->xprt_ctx;
+
+       SSL_get_changed_async_fds(ssl, NULL, &num_add_fds, NULL, &num_del_fds);
+
+       if (num_add_fds == 0 && num_del_fds == 0)
+               return 0;
+
+       if (num_add_fds) {
+               add_fds = OPENSSL_malloc(num_add_fds * sizeof(OSSL_ASYNC_FD));
+               if (add_fds == NULL) {
+                       Alert("Memory Allocation Error");
+                       return 0;
+               }
+       }
+
+       if (num_del_fds) {
+               del_fds = OPENSSL_malloc(num_del_fds * sizeof(OSSL_ASYNC_FD));
+               if (del_fds == NULL) {
+                       Alert("Memory Allocation Error");
+                       if (add_fds)
+                               OPENSSL_free(add_fds);
+                       return 0;
+               }
+       }
+
+       SSL_get_changed_async_fds(ssl, add_fds, &num_add_fds, del_fds,
+                       &num_del_fds);
+
+       if (num_del_fds) {
+               for (loop = 0; loop < num_del_fds; loop++) {
+                       int async_fd = del_fds[loop];
+
+                       fd_stop_recv(async_fd);
+                       fd_async_delete(async_fd);
+               }
+       }
+
+       if (num_add_fds) {
+               for (loop = 0; loop < num_add_fds; loop++) {
+                       int async_fd = add_fds[loop];
+
+                       fd_async_insert(async_fd);
+                       fdtab[async_fd].iocb = conn_fd_handler;
+                       conn->async_fd = async_fd;
+                       fdtab[async_fd].owner = conn;
+                       fd_want_recv(async_fd);
+               }
+       }
+
+       if (add_fds)
+               OPENSSL_free(add_fds);
+       if (del_fds)
+               OPENSSL_free(del_fds);
+
+       return 1;
+}
+
+/*
  *  This function returns the number of seconds  elapsed
  *  since the Epoch, 1970-01-01 00:00:00 +0000 (UTC) and the
  *  date presented un ASN1_GENERALIZEDTIME.
@@ -2743,6 +2813,9 @@ int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, 
SSL_CTX *ctx, struct proxy
                cfgerr++;
        }
 
+       if (global.ssl_async)
+               sslmode |= SSL_MODE_ASYNC;
+
        if (bind_conf->ssl_options & BC_SSL_O_NO_SSLV3)
                ssloptions |= SSL_OP_NO_SSLv3;
        if (bind_conf->ssl_options & BC_SSL_O_NO_TLSV10)
@@ -3149,6 +3222,10 @@ int ssl_sock_prepare_srv_ctx(struct server *srv, struct 
proxy *curproxy)
 #endif
 
        SSL_CTX_set_options(srv->ssl_ctx.ctx, options);
+
+       if (global.ssl_async)
+               mode |= SSL_MODE_ASYNC;
+
        SSL_CTX_set_mode(srv->ssl_ctx.ctx, mode);
 
        if (global.ssl_server_verify == SSL_SERVER_VERIFY_REQUIRED)
@@ -3533,6 +3610,12 @@ 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 (ret == SSL_ERROR_WANT_ASYNC) {
+                               ssl_async_process_fds(conn);
+                               return 0;
+                       }
+
                        if (ret == SSL_ERROR_WANT_WRITE) {
                                /* SSL handshake needs to write, L4 connection 
may not be ready */
                                __conn_sock_stop_recv(conn);
@@ -3615,6 +3698,11 @@ 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 (ret == SSL_ERROR_WANT_ASYNC) {
+                       ssl_async_process_fds(conn);
+                       return 0;
+               }
+
                if (ret == SSL_ERROR_WANT_WRITE) {
                        /* SSL handshake needs to write, L4 connection may not 
be ready */
                        __conn_sock_stop_recv(conn);
@@ -3796,6 +3884,11 @@ static int ssl_sock_to_buf(struct connection *conn, 
struct buffer *buf, int coun
                }
                else {
                        ret =  SSL_get_error(conn->xprt_ctx, ret);
+                       if (ret == SSL_ERROR_WANT_ASYNC) {
+                               ssl_async_process_fds(conn);
+                               break;
+                       }
+
                        if (ret == SSL_ERROR_WANT_WRITE) {
                                /* handshake is running, and it needs to enable 
write */
                                conn->flags |= CO_FL_SSL_WAIT_HS;
@@ -3897,6 +3990,12 @@ static int ssl_sock_from_buf(struct connection *conn, 
struct buffer *buf, int fl
                }
                else {
                        ret = SSL_get_error(conn->xprt_ctx, ret);
+
+                       if (ret == SSL_ERROR_WANT_ASYNC) {
+                               ssl_async_process_fds(conn);
+                               break;
+                       }
+
                        if (ret == SSL_ERROR_WANT_WRITE) {
                                if (SSL_renegotiate_pending(conn->xprt_ctx)) {
                                        /* handshake is running, and it may 
need to re-enable write */
@@ -3931,6 +4030,11 @@ 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) {
+                       remove_fd_from_epoll(conn->async_fd);
+                       fd_stop_recv(conn->async_fd);
+                       fd_async_delete(conn->async_fd);
+               }
                SSL_free(conn->xprt_ctx);
                conn->xprt_ctx = NULL;
                sslconns--;
-- 
1.9.1


Reply via email to