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