diff --git a/modules/tls_wolfssl/doc/tls_wolfssl_admin.xml b/modules/tls_wolfssl/doc/tls_wolfssl_admin.xml
index 84b207908f..8b3be756bc 100644
--- a/modules/tls_wolfssl/doc/tls_wolfssl_admin.xml
+++ b/modules/tls_wolfssl/doc/tls_wolfssl_admin.xml
@@ -76,4 +76,33 @@
+
+ &osips; Exported parameters
+
+ All these parameters can be used from the opensips.cfg file,
+ to configure the behavior of &osips;-TLS.
+
+
+
+ try_use_ktls (integer)
+
+ Try to use KTLS for RX and TX ( dependent on Kernel support and loaded modules https://docs.kernel.org/networking/tls-offload.htm )
+ If kernel support is not found, or if the cypher attempted to be used is not supported ( only AES-GCM for now ), then SSL operations will continue to be done in user-space.
+ IF NIC supports SSL offloading, that can also be enabled without any changes needed to the module https://docs.nvidia.com/doca/sdk/ktls-offloads/index.html
+
+
+ Default value is 0.
+
+
+ Set try_use_ktls variable
+
+...
+modparam("tls_wolfssl", "try_use_ktls", "0")
+...
+
+
+
+
+
+
diff --git a/modules/tls_wolfssl/wolfssl.c b/modules/tls_wolfssl/wolfssl.c
index 242db15eaf..6bb8d21c08 100644
--- a/modules/tls_wolfssl/wolfssl.c
+++ b/modules/tls_wolfssl/wolfssl.c
@@ -35,6 +35,7 @@
#include "../../dprint.h"
#include "../../mem/shm_mem.h"
#include "../../sr_module.h"
+#include "../../modparam.h"
#include "../../locking.h"
#include "../../pt.h"
#include "../../net/tcp_conn_defs.h"
@@ -92,6 +93,7 @@ int _wolfssl_tls_var_check_cert(int ind, void *ssl, str *str_res, int *int_res);
int _wolfssl_tls_var_validity(int ind, void *ssl, str *res);
int ssl_versions[SSL_VERSIONS_SIZE];
+int wolfssl_try_use_ktls;
static const cmd_export_t cmds[] = {
{"load_tls_wolfssl", (cmd_function)load_tls_wolfssl,
@@ -99,16 +101,21 @@ static const cmd_export_t cmds[] = {
{0,0,{{0,0,0}},0}
};
+static param_export_t mod_params[] = {
+ {"try_use_ktls", INT_PARAM, &wolfssl_try_use_ktls},
+ {0, 0, 0}
+};
+
struct module_exports exports = {
"tls_wolfssl", /* module name*/
MOD_TYPE_DEFAULT,/* class of this module */
MODULE_VERSION,
DEFAULT_DLFLAGS, /* dlopen flags */
- 0, /* load function */
+ 0, /* load function */
0, /* OpenSIPS module dependencies */
- cmds, /* exported functions */
+ cmds, /* exported functions */
0, /* exported async functions */
- 0, /* module parameters */
+ mod_params, /* module parameters */
0, /* exported statistics */
0, /* exported MI functions */
0, /* exported pseudo-variables */
@@ -190,6 +197,9 @@ static int mod_init(void)
_wolfssl_show_ciphers();
+ if (wolfssl_try_use_ktls)
+ LM_INFO("KTLS requested: will try to offload TLS TX to the kernel when possible\n");
+
#ifdef __WOLFSSL_ON_EXIT
on_exit(_wolfssl_on_exit, NULL);
#endif
diff --git a/modules/tls_wolfssl/wolfssl.h b/modules/tls_wolfssl/wolfssl.h
index 8513a622f3..5969288733 100644
--- a/modules/tls_wolfssl/wolfssl.h
+++ b/modules/tls_wolfssl/wolfssl.h
@@ -27,6 +27,9 @@
struct _WOLFSSL {
WOLFSSL *read_ssl;
WOLFSSL *write_ssl;
+ unsigned char ktls_tx;
+ unsigned char ktls_rx;
+ unsigned char ktls_ulp;
};
#define _WOLFSSL_READ_SSL(_ssl) \
@@ -39,3 +42,4 @@ struct _WOLFSSL {
#define SSL_VERSIONS_SIZE 4
extern int ssl_versions[SSL_VERSIONS_SIZE];
+extern int wolfssl_try_use_ktls;
diff --git a/modules/tls_wolfssl/wolfssl_conn_ops.c b/modules/tls_wolfssl/wolfssl_conn_ops.c
index bf8a75f828..82c7167068 100644
--- a/modules/tls_wolfssl/wolfssl_conn_ops.c
+++ b/modules/tls_wolfssl/wolfssl_conn_ops.c
@@ -26,10 +26,18 @@
#include
#include
+#include
+#include
+#include
+#include
#include
#include
#include
#include
+#if defined(__linux__)
+#include
+#include
+#endif
#include "../../net/tcp_conn_defs.h"
#include "../../net/proto_tcp/tcp_common_defs.h"
@@ -155,6 +163,155 @@ static void tls_dump_verification_failure(long verification_result)
}
}
+static int _wolfssl_enable_ktls_ulp(struct tcp_connection *c, int fd)
+{
+ struct _WOLFSSL *w;
+
+ w = (struct _WOLFSSL *)c->extra_data;
+ if (!w)
+ return -1;
+
+ if (w->ktls_ulp)
+ return 0;
+
+ if (setsockopt(fd, SOL_TCP, TCP_ULP, "tls", sizeof("tls")) < 0) {
+ LM_DBG("failed to enable TCP_ULP tls on fd %d: %s\n",
+ fd, strerror(errno));
+ return -1;
+ }
+
+ w->ktls_ulp = 1;
+ return 0;
+}
+
+static int _wolfssl_enable_ktls_tx(struct tcp_connection *c, WOLFSSL *ssl)
+{
+#if defined(__linux__) && defined(SOL_TLS) && defined(TLS_TX) && defined(TCP_ULP)
+ struct _WOLFSSL *w = (struct _WOLFSSL *)c->extra_data;
+ struct tls_crypto_info *crypto_info;
+ struct tls12_crypto_info_aes_gcm_128 crypto_128;
+ struct tls12_crypto_info_aes_gcm_256 crypto_256;
+ const unsigned char *key, *iv;
+ unsigned long seq;
+ unsigned int rand_hi, rand_lo;
+ int key_size, crypto_size;
+ int tls_version;
+ int fd;
+
+ if (!wolfssl_try_use_ktls || !w || w->ktls_tx)
+ return 0;
+
+ fd = (c->fd >= 0) ? c->fd : c->s;
+ if (fd < 0) {
+ LM_WARN("cannot enable KTLS, invalid socket\n");
+ return -1;
+ }
+
+ if ((wolfSSL_GetCipherType(ssl) != WOLFSSL_AEAD_TYPE) ||
+ (wolfSSL_GetBulkCipher(ssl) != wolfssl_aes_gcm)) {
+ LM_DBG("KTLS requires AES-GCM cipher, skipping\n");
+ return -1;
+ }
+
+ key_size = wolfSSL_GetKeySize(ssl);
+ if ((key_size != TLS_CIPHER_AES_GCM_128_KEY_SIZE) &&
+ (key_size != TLS_CIPHER_AES_GCM_256_KEY_SIZE)) {
+ LM_DBG("KTLS requires 128/256 bit AES-GCM keys, got %d\n", key_size);
+ return -1;
+ }
+
+ if (_wolfssl_enable_ktls_ulp(c, fd) < 0)
+ return -1;
+
+ memset(&crypto_128, 0, sizeof(crypto_128));
+ memset(&crypto_256, 0, sizeof(crypto_256));
+ if (key_size == TLS_CIPHER_AES_GCM_128_KEY_SIZE) {
+ crypto_info = &crypto_128.info;
+ crypto_size = sizeof(crypto_128);
+ } else {
+ crypto_info = &crypto_256.info;
+ crypto_size = sizeof(crypto_256);
+ }
+
+ tls_version = wolfSSL_version(ssl);
+ if (tls_version == TLS1_2_VERSION)
+ crypto_info->version = TLS_1_2_VERSION;
+ else if (tls_version == TLS1_3_VERSION)
+ crypto_info->version = TLS_1_3_VERSION;
+ else {
+ LM_DBG("KTLS supported only for TLS 1.2/1.3 (got %x)\n", tls_version);
+ return -1;
+ }
+
+ crypto_info->cipher_type = (key_size == TLS_CIPHER_AES_GCM_128_KEY_SIZE)
+ ? TLS_CIPHER_AES_GCM_128
+ : TLS_CIPHER_AES_GCM_256;
+
+ key = (wolfSSL_GetSide(ssl) == WOLFSSL_CLIENT_END)
+ ? wolfSSL_GetClientWriteKey(ssl)
+ : wolfSSL_GetServerWriteKey(ssl);
+ iv = (wolfSSL_GetSide(ssl) == WOLFSSL_CLIENT_END)
+ ? wolfSSL_GetClientWriteIV(ssl)
+ : wolfSSL_GetServerWriteIV(ssl);
+
+ if (!key || !iv) {
+ LM_DBG("KTLS secrets not available, skipping\n");
+ return -1;
+ }
+
+ wolfSSL_GetSequenceNumber(ssl, &seq);
+ seq = htobe64(seq);
+
+ if (key_size == TLS_CIPHER_AES_GCM_128_KEY_SIZE) {
+ memcpy(crypto_128.key, key, key_size);
+ memcpy(crypto_128.salt, iv, 4);
+ if (crypto_info->version == TLS_1_2_VERSION) {
+ srand((unsigned int)time(NULL));
+ rand_hi = rand();
+ rand_lo = rand();
+ memcpy(crypto_128.iv, &rand_hi, 4);
+ memcpy((crypto_128.iv + 4), &rand_lo, 4);
+ } else {
+ memcpy(crypto_128.iv, (iv + 4), 8);
+ }
+ memcpy(crypto_128.rec_seq, &seq, sizeof(seq));
+ } else {
+ memcpy(crypto_256.key, key, key_size);
+ memcpy(crypto_256.salt, iv, 4);
+ if (crypto_info->version == TLS_1_2_VERSION) {
+ srand((unsigned int)time(NULL));
+ rand_hi = rand();
+ rand_lo = rand();
+ memcpy(crypto_256.iv, &rand_hi, 4);
+ memcpy((crypto_256.iv + 4), &rand_lo, 4);
+ } else {
+ memcpy(crypto_256.iv, (iv + 4), 8);
+ }
+ memcpy(crypto_256.rec_seq, &seq, sizeof(seq));
+ }
+
+ if (setsockopt(fd, SOL_TLS, TLS_TX, crypto_info, crypto_size) < 0) {
+ LM_WARN("failed to enable KTLS TX on fd %d: %s\n",
+ fd, strerror(errno));
+ return -1;
+ }
+
+ w->ktls_tx = 1;
+ LM_INFO("KTLS TX enabled for %s:%d\n",
+ ip_addr2a(&c->rcv.src_ip), c->rcv.src_port);
+
+ return 0;
+#else
+ (void)c;
+ (void)ssl;
+
+ if (wolfssl_try_use_ktls)
+ LM_WARN("try_use_ktls set but KTLS not supported on this platform\n");
+
+ return -1;
+#endif
+}
+
int _wolfssl_tls_update_fd(struct tcp_connection *c, int fd)
{
if (wolfSSL_set_fd(_WOLFSSL_READ_SSL(c->extra_data), fd) !=
@@ -290,6 +447,134 @@ static int _wolfssl_tls_conn_shutdown(struct tcp_connection *c)
return -1;
}
+static int _wolfssl_enable_ktls_rx(struct tcp_connection *c, WOLFSSL *ssl)
+{
+#if defined(__linux__) && defined(SOL_TLS) && defined(TLS_RX) && defined(TCP_ULP)
+ struct _WOLFSSL *w = (struct _WOLFSSL *)c->extra_data;
+ struct tls_crypto_info *crypto_info;
+ struct tls12_crypto_info_aes_gcm_128 crypto_128;
+ struct tls12_crypto_info_aes_gcm_256 crypto_256;
+ const unsigned char *key, *iv;
+ unsigned long seq;
+ unsigned int rand_hi, rand_lo;
+ int key_size, crypto_size;
+ int tls_version;
+ int fd;
+
+ if (!wolfssl_try_use_ktls || !w || w->ktls_rx)
+ return 0;
+
+ fd = (c->fd >= 0) ? c->fd : c->s;
+ if (fd < 0) {
+ LM_WARN("cannot enable RX KTLS, invalid socket\n");
+ return -1;
+ }
+
+ if ((wolfSSL_GetCipherType(ssl) != WOLFSSL_AEAD_TYPE) ||
+ (wolfSSL_GetBulkCipher(ssl) != wolfssl_aes_gcm)) {
+ LM_DBG("KTLS RX requires AES-GCM cipher, skipping\n");
+ return -1;
+ }
+
+ key_size = wolfSSL_GetKeySize(ssl);
+ if ((key_size != TLS_CIPHER_AES_GCM_128_KEY_SIZE) &&
+ (key_size != TLS_CIPHER_AES_GCM_256_KEY_SIZE)) {
+ LM_DBG("KTLS RX requires 128/256 bit AES-GCM keys, got %d\n", key_size);
+ return -1;
+ }
+
+ if (_wolfssl_enable_ktls_ulp(c, fd) < 0)
+ return -1;
+
+ memset(&crypto_128, 0, sizeof(crypto_128));
+ memset(&crypto_256, 0, sizeof(crypto_256));
+ if (key_size == TLS_CIPHER_AES_GCM_128_KEY_SIZE) {
+ crypto_info = &crypto_128.info;
+ crypto_size = sizeof(crypto_128);
+ } else {
+ crypto_info = &crypto_256.info;
+ crypto_size = sizeof(crypto_256);
+ }
+
+ tls_version = wolfSSL_version(ssl);
+ if (tls_version == TLS1_2_VERSION)
+ crypto_info->version = TLS_1_2_VERSION;
+ else if (tls_version == TLS1_3_VERSION)
+ crypto_info->version = TLS_1_3_VERSION;
+ else {
+ LM_DBG("KTLS RX supported only for TLS 1.2/1.3 (got %x)\n", tls_version);
+ return -1;
+ }
+
+ crypto_info->cipher_type = (key_size == TLS_CIPHER_AES_GCM_128_KEY_SIZE)
+ ? TLS_CIPHER_AES_GCM_128
+ : TLS_CIPHER_AES_GCM_256;
+
+ key = (wolfSSL_GetSide(ssl) == WOLFSSL_CLIENT_END)
+ ? wolfSSL_GetServerWriteKey(ssl)
+ : wolfSSL_GetClientWriteKey(ssl);
+ iv = (wolfSSL_GetSide(ssl) == WOLFSSL_CLIENT_END)
+ ? wolfSSL_GetServerWriteIV(ssl)
+ : wolfSSL_GetClientWriteIV(ssl);
+
+ if (!key || !iv) {
+ LM_DBG("KTLS RX secrets not available, skipping\n");
+ return -1;
+ }
+
+ wolfSSL_GetPeerSequenceNumber(ssl, &seq);
+ seq = htobe64(seq);
+
+ if (key_size == TLS_CIPHER_AES_GCM_128_KEY_SIZE) {
+ memcpy(crypto_128.key, key, key_size);
+ memcpy(crypto_128.salt, iv, 4);
+ if (crypto_info->version == TLS_1_2_VERSION) {
+ srand((unsigned int)time(NULL));
+ rand_hi = rand();
+ rand_lo = rand();
+ memcpy(crypto_128.iv, &rand_hi, 4);
+ memcpy((crypto_128.iv + 4), &rand_lo, 4);
+ } else {
+ memcpy(crypto_128.iv, (iv + 4), 8);
+ }
+ memcpy(crypto_128.rec_seq, &seq, sizeof(seq));
+ } else {
+ memcpy(crypto_256.key, key, key_size);
+ memcpy(crypto_256.salt, iv, 4);
+ if (crypto_info->version == TLS_1_2_VERSION) {
+ srand((unsigned int)time(NULL));
+ rand_hi = rand();
+ rand_lo = rand();
+ memcpy(crypto_256.iv, &rand_hi, 4);
+ memcpy((crypto_256.iv + 4), &rand_lo, 4);
+ } else {
+ memcpy(crypto_256.iv, (iv + 4), 8);
+ }
+ memcpy(crypto_256.rec_seq, &seq, sizeof(seq));
+ }
+
+ if (setsockopt(fd, SOL_TLS, TLS_RX, crypto_info, crypto_size) < 0) {
+ LM_WARN("failed to enable KTLS RX on fd %d: %s\n",
+ fd, strerror(errno));
+ return -1;
+ }
+
+ w->ktls_rx = 1;
+ LM_INFO("KTLS RX enabled for %s:%d\n",
+ ip_addr2a(&c->rcv.src_ip), c->rcv.src_port);
+
+ return 0;
+#else
+ (void)c;
+ (void)ssl;
+
+ if (wolfssl_try_use_ktls)
+ LM_WARN("try_use_ktls set but KTLS not supported on this platform\n");
+
+ return -1;
+#endif
+}
+
void _wolfssl_tls_conn_clean(struct tcp_connection* c,
struct tls_domain **tls_dom)
{
@@ -399,6 +684,8 @@ int _wolfssl_tls_async_connect(struct tcp_connection *con, int fd,
}
_wolfssl_enforce_max_version(con);
+ _wolfssl_enable_ktls_tx(con, ssl);
+ _wolfssl_enable_ktls_rx(con, ssl);
return 1;
}
@@ -504,9 +791,36 @@ int _wolfssl_tls_write(struct tcp_connection *c, int fd, const void *buf,
int err;
char err_buf[_WOLFSSL_ERR_BUFLEN];
WOLFSSL *ssl;
+ struct _WOLFSSL *w;
+ w = (struct _WOLFSSL *)c->extra_data;
ssl = _WOLFSSL_WRITE_SSL(c->extra_data);
+ if (w && w->ktls_tx) {
+ ret = send(fd, buf, len, 0);
+ if (ret >= 0) {
+ LM_DBG("KTLS write was successful (%d bytes)\n", ret);
+ return ret;
+ }
+
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ if (poll_events)
+ *poll_events = POLLOUT;
+
+ return 0;
+ }
+
+ if (errno == EINTR)
+ return 0;
+
+ LM_ERR("KTLS connection to %s:%d write failed (%d:%d)\n",
+ ip_addr2a(&c->rcv.src_ip), c->rcv.src_port, ret, errno);
+ LM_ERR("KTLS write error: %s\n", strerror(errno));
+ c->state = S_CONN_BAD;
+
+ return -1;
+ }
+
ret = wolfSSL_write(ssl, buf, len);
if (ret > 0) {
LM_DBG("write was successful (%d bytes)\n", ret);
@@ -578,6 +892,8 @@ static int _wolfssl_tls_accept(struct tcp_connection *c, short *poll_events)
}
_wolfssl_enforce_max_version(c);
+ _wolfssl_enable_ktls_tx(c, ssl);
+ _wolfssl_enable_ktls_rx(c, ssl);
LM_DBG("new TLS connection from %s:%d using %s\n",
ip_addr2a(&c->rcv.src_ip), c->rcv.src_port,
@@ -686,6 +1002,8 @@ static int _wolfssl_tls_connect(struct tcp_connection *c, short *poll_events,
}
_wolfssl_enforce_max_version(c);
+ _wolfssl_enable_ktls_tx(c, ssl);
+ _wolfssl_enable_ktls_rx(c, ssl);
LM_DBG("new TLS connection to %s:%d using %s\n",
ip_addr2a(&c->rcv.src_ip), c->rcv.src_port,
@@ -904,6 +1222,36 @@ static int _wolfssl_read(struct tcp_connection *c, void *buf, size_t len)
int ret, err;
WOLFSSL *ssl;
char err_buf[_WOLFSSL_ERR_BUFLEN];
+ struct _WOLFSSL *w = (struct _WOLFSSL *)c->extra_data;
+
+ if (w && w->ktls_rx) {
+ int fd = (c->fd >= 0) ? c->fd : c->s;
+
+ if (fd < 0) {
+ LM_ERR("KTLS RX enabled but invalid socket\n");
+ return -1;
+ }
+
+ ret = recv(fd, buf, len, 0);
+ if (ret > 0) {
+ LM_DBG("KTLS RX read %d bytes\n", ret);
+ return ret;
+ } else if (ret == 0) {
+ c->state = S_CONN_EOF;
+ LM_DBG("KTLS RX connection to %s:%d closed\n",
+ ip_addr2a(&c->rcv.src_ip), c->rcv.src_port);
+ return 0;
+ } else {
+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+ return 0;
+
+ LM_ERR("KTLS RX connection to %s:%d read failed (%d)\n",
+ ip_addr2a(&c->rcv.src_ip), c->rcv.src_port, errno);
+ LM_ERR("KTLS RX read error: %s\n", strerror(errno));
+ c->state = S_CONN_BAD;
+ return -1;
+ }
+ }
ssl = _WOLFSSL_READ_SSL(c->extra_data);