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. + + +
+ <varname>try_use_ktls</varname> (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 <varname>try_use_ktls</varname> 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);