ssl: Linux TLS Tx Offload

This patch adds support for the Linux TLS Tx socket option.
If the socket option is successful, then the data-path of the TCP socket
is implemented by the kernel.
We choose to set this option at the earliest - just after CCS is complete.

Signed-off-by: Boris Pismenny <borisp@mellanox.com>

Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Paul Yang <yang.yang@baishancloud.com>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/5253)
This commit is contained in:
Boris Pismenny 2017-06-01 09:25:47 +03:00 committed by Matt Caswell
parent ecd1557fb4
commit 50ec750567
10 changed files with 235 additions and 50 deletions

View file

@ -5,7 +5,7 @@
BIO_ctrl, BIO_callback_ctrl, BIO_ptr_ctrl, BIO_int_ctrl, BIO_reset,
BIO_seek, BIO_tell, BIO_flush, BIO_eof, BIO_set_close, BIO_get_close,
BIO_pending, BIO_wpending, BIO_ctrl_pending, BIO_ctrl_wpending,
BIO_get_info_callback, BIO_set_info_callback, BIO_info_cb
BIO_get_info_callback, BIO_set_info_callback, BIO_info_cb, BIO_get_ktls_send
- BIO control operations
=head1 SYNOPSIS
@ -34,6 +34,8 @@ BIO_get_info_callback, BIO_set_info_callback, BIO_info_cb
int BIO_get_info_callback(BIO *b, BIO_info_cb **cbp);
int BIO_set_info_callback(BIO *b, BIO_info_cb *cb);
int BIO_get_ktls_send(BIO *b);
=head1 DESCRIPTION
BIO_ctrl(), BIO_callback_ctrl(), BIO_ptr_ctrl() and BIO_int_ctrl()
@ -72,6 +74,9 @@ Not all BIOs support these calls. BIO_ctrl_pending() and BIO_ctrl_wpending()
return a size_t type and are functions, BIO_pending() and BIO_wpending() are
macros which call BIO_ctrl().
BIO_get_ktls_send() return 1 if the BIO is using the Kernel TLS data-path for
sending. Otherwise, it returns zero.
=head1 RETURN VALUES
BIO_reset() normally returns 1 for success and 0 or -1 for failure. File
@ -92,6 +97,9 @@ BIO_get_close() returns the close flag value: BIO_CLOSE or BIO_NOCLOSE.
BIO_pending(), BIO_ctrl_pending(), BIO_wpending() and BIO_ctrl_wpending()
return the amount of pending data.
BIO_get_ktls_send() return 1 if the BIO is using the Kernel TLS data-path for
sending. Otherwise, it returns zero.
=head1 NOTES
BIO_flush(), because it can write data may return 0 or -1 indicating
@ -124,6 +132,10 @@ particular a return value of 0 can be returned if an operation is not
supported, if an error occurred, if EOF has not been reached and in
the case of BIO_seek() on a file BIO for a successful operation.
=head1 HISTORY
The BIO_get_ktls_send() function was added in OpenSSL 3.0.0.
=head1 COPYRIGHT
Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.

View file

@ -105,6 +105,22 @@ Enable asynchronous processing. 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. See L<SSL_get_error(3)>.
=item SSL_MODE_NO_KTLS_TX
Disable the use of the kernel TLS egress data-path.
By default kernel TLS is enabled if it is supported by the negotiated ciphersuites
and extensions and OpenSSL has been compiled with support for it.
The kernel TLS data-path implements the record layer,
and the crypto algorithm. The kernel will utilize the best hardware
available for crypto. Using the kernel data-path should reduce the memory
footprint of OpenSSL because no buffering is required. Also, the throughput
should improve because data copy is avoided when user data is encrypted into
kernel memory instead of the usual encrypt than copy to kernel.
Kernel TLS might not support all the features of OpenSSL. For instance,
renegotiation, and setting the maximum fragment size is not possible as of
Linux 4.20.
=back
All modes are off by default except for SSL_MODE_AUTO_RETRY which is on by
@ -125,6 +141,7 @@ L<SSL_write(3)>, L<SSL_get_error(3)>
=head1 HISTORY
SSL_MODE_ASYNC was first added to OpenSSL 1.1.0.
SSL_MODE_NO_KTLS_TX was first added to OpenSSL 3.0.0.
=head1 COPYRIGHT

View file

@ -493,6 +493,10 @@ typedef int (*SSL_verify_cb)(int preverify_ok, X509_STORE_CTX *x509_ctx);
* Support Asynchronous operation
*/
# define SSL_MODE_ASYNC 0x00000100U
/*
* Use the kernel TLS transmission data-path.
*/
# define SSL_MODE_NO_KTLS_TX 0x00000200U
/* Cert related flags */
/*

View file

@ -743,6 +743,18 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
s->s3->empty_fragment_done = 1;
}
if (BIO_get_ktls_send(s->wbio)) {
/*
* ktls doesn't modify the buffer, but to avoid a warning we need to
* discard the const qualifier.
* This doesn't leak memory because the buffers have been released when
* switching to ktls.
*/
SSL3_BUFFER_set_buf(&s->rlayer.wbuf[0], (unsigned char *)buf);
SSL3_BUFFER_set_offset(&s->rlayer.wbuf[0], 0);
goto wpacket_init_complete;
}
if (create_empty_fragment) {
wb = &s->rlayer.wbuf[0];
#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD!=0
@ -812,6 +824,8 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
}
}
wpacket_init_complete:
totlen = 0;
/* Clear our SSL3_RECORD structures */
memset(wr, 0, sizeof(wr));
@ -853,15 +867,19 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
if (s->compress != NULL)
maxcomplen += SSL3_RT_MAX_COMPRESSED_OVERHEAD;
/* write the header */
if (!WPACKET_put_bytes_u8(thispkt, rectype)
/*
* When using offload kernel will write the header.
* Otherwise write the header now
*/
if (!BIO_get_ktls_send(s->wbio)
&& (!WPACKET_put_bytes_u8(thispkt, rectype)
|| !WPACKET_put_bytes_u16(thispkt, version)
|| !WPACKET_start_sub_packet_u16(thispkt)
|| (eivlen > 0
&& !WPACKET_allocate_bytes(thispkt, eivlen, NULL))
|| (maxcomplen > 0
&& !WPACKET_reserve_bytes(thispkt, maxcomplen,
&compressdata))) {
&compressdata)))) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
ERR_R_INTERNAL_ERROR);
goto err;
@ -887,12 +905,16 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
goto err;
}
} else {
if (!WPACKET_memcpy(thispkt, thiswr->input, thiswr->length)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
ERR_R_INTERNAL_ERROR);
goto err;
if (BIO_get_ktls_send(s->wbio)) {
SSL3_RECORD_reset_data(&wr[j]);
} else {
if (!WPACKET_memcpy(thispkt, thiswr->input, thiswr->length)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
ERR_R_INTERNAL_ERROR);
goto err;
}
SSL3_RECORD_reset_input(&wr[j]);
}
SSL3_RECORD_reset_input(&wr[j]);
}
if (SSL_TREAT_AS_TLS13(s)
@ -967,24 +989,26 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
* This will be at most one cipher block or the tag length if using
* AEAD. SSL_RT_MAX_CIPHER_BLOCK_SIZE covers either case.
*/
if (!WPACKET_reserve_bytes(thispkt, SSL_RT_MAX_CIPHER_BLOCK_SIZE,
NULL)
/*
* We also need next the amount of bytes written to this
* sub-packet
*/
if (!BIO_get_ktls_send(s->wbio)) {
if (!WPACKET_reserve_bytes(thispkt,
SSL_RT_MAX_CIPHER_BLOCK_SIZE,
NULL)
/*
* We also need next the amount of bytes written to this
* sub-packet
*/
|| !WPACKET_get_length(thispkt, &len)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
ERR_R_INTERNAL_ERROR);
goto err;
}
/* Get a pointer to the start of this record excluding header */
recordstart = WPACKET_get_curr(thispkt) - len;
SSL3_RECORD_set_data(thiswr, recordstart);
SSL3_RECORD_reset_input(thiswr);
SSL3_RECORD_set_length(thiswr, len);
}
/* Get a pointer to the start of this record excluding header */
recordstart = WPACKET_get_curr(thispkt) - len;
SSL3_RECORD_set_data(thiswr, recordstart);
SSL3_RECORD_reset_input(thiswr);
SSL3_RECORD_set_length(thiswr, len);
}
if (s->statem.enc_write_state == ENC_WRITE_STATE_WRITE_PLAIN_ALERTS) {
@ -1000,12 +1024,14 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
goto err;
}
} else {
if (s->method->ssl3_enc->enc(s, wr, numpipes, 1) < 1) {
if (!ossl_statem_in_error(s)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
ERR_R_INTERNAL_ERROR);
if (!BIO_get_ktls_send(s->wbio)) {
if (s->method->ssl3_enc->enc(s, wr, numpipes, 1) < 1) {
if (!ossl_statem_in_error(s)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
ERR_R_INTERNAL_ERROR);
}
goto err;
}
goto err;
}
}
@ -1015,13 +1041,17 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
thispkt = &pkt[j];
thiswr = &wr[j];
if (BIO_get_ktls_send(s->wbio))
goto mac_done;
/* Allocate bytes for the encryption overhead */
if (!WPACKET_get_length(thispkt, &origlen)
/* Encryption should never shrink the data! */
|| origlen > thiswr->length
|| (thiswr->length > origlen
&& !WPACKET_allocate_bytes(thispkt,
thiswr->length - origlen, NULL))) {
thiswr->length - origlen,
NULL))) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
ERR_R_INTERNAL_ERROR);
goto err;
@ -1066,13 +1096,8 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
goto err;
}
/*
* we should now have thiswr->data pointing to the encrypted data, which
* is thiswr->length long
*/
SSL3_RECORD_set_type(thiswr, type); /* not needed but helps for
* debugging */
SSL3_RECORD_add_length(thiswr, SSL3_RT_HEADER_LENGTH);
/* header is added by the kernel when using offload */
SSL3_RECORD_add_length(&wr[j], SSL3_RT_HEADER_LENGTH);
if (create_empty_fragment) {
/*
@ -1089,6 +1114,14 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
return 1;
}
mac_done:
/*
* we should now have thiswr->data pointing to the encrypted data, which
* is thiswr->length long
*/
SSL3_RECORD_set_type(thiswr, type); /* not needed but helps for
* debugging */
/* now let's set up wb */
SSL3_BUFFER_set_left(&s->rlayer.wbuf[j],
prefix_len + SSL3_RECORD_get_length(thiswr));
@ -1142,6 +1175,21 @@ int ssl3_write_pending(SSL *s, int type, const unsigned char *buf, size_t len,
clear_sys_error();
if (s->wbio != NULL) {
s->rwstate = SSL_WRITING;
/*
* To prevent coalescing of control and data messages,
* such as in buffer_write, we flush the BIO
*/
if (BIO_get_ktls_send(s->wbio) && type != SSL3_RT_APPLICATION_DATA) {
i = BIO_flush(s->wbio);
if (i <= 0)
return i;
}
if (BIO_get_ktls_send(s->wbio)
&& type != SSL3_RT_APPLICATION_DATA) {
BIO_set_ktls_ctrl_msg(s->wbio, type);
}
/* TODO(size_t): Convert this call */
i = BIO_write(s->wbio, (char *)
&(SSL3_BUFFER_get_buf(&wb[currbuf])

View file

@ -88,6 +88,7 @@ int ssl3_release_write_buffer(SSL *s);
#define SSL3_RECORD_get_input(r) ((r)->input)
#define SSL3_RECORD_set_input(r, i) ((r)->input = (i))
#define SSL3_RECORD_reset_input(r) ((r)->input = (r)->data)
#define SSL3_RECORD_reset_data(r) ((r)->data = (r)->input)
#define SSL3_RECORD_get_seq_num(r) ((r)->seq_num)
#define SSL3_RECORD_get_off(r) ((r)->off)
#define SSL3_RECORD_set_off(r, o) ((r)->off = (o))

View file

@ -111,23 +111,27 @@ int ssl3_setup_write_buffer(SSL *s, size_t numwpipes, size_t len)
for (currpipe = 0; currpipe < numwpipes; currpipe++) {
SSL3_BUFFER *thiswb = &wb[currpipe];
if (thiswb->buf != NULL && thiswb->len != len) {
if (thiswb->len != len) {
OPENSSL_free(thiswb->buf);
thiswb->buf = NULL; /* force reallocation */
}
if (thiswb->buf == NULL) {
p = OPENSSL_malloc(len);
if (p == NULL) {
s->rlayer.numwpipes = currpipe;
/*
* We've got a malloc failure, and we're still initialising
* buffers. We assume we're so doomed that we won't even be able
* to send an alert.
*/
SSLfatal(s, SSL_AD_NO_ALERT,
SSL_F_SSL3_SETUP_WRITE_BUFFER, ERR_R_MALLOC_FAILURE);
return 0;
if (s->wbio == NULL || !BIO_get_ktls_send(s->wbio)) {
p = OPENSSL_malloc(len);
if (p == NULL) {
s->rlayer.numwpipes = currpipe;
/*
* We've got a malloc failure, and we're still initialising
* buffers. We assume we're so doomed that we won't even be able
* to send an alert.
*/
SSLfatal(s, SSL_AD_NO_ALERT,
SSL_F_SSL3_SETUP_WRITE_BUFFER, ERR_R_MALLOC_FAILURE);
return 0;
}
} else {
p = NULL;
}
memset(thiswb, 0, sizeof(SSL3_BUFFER));
thiswb->buf = p;
@ -160,7 +164,8 @@ int ssl3_release_write_buffer(SSL *s)
while (pipes > 0) {
wb = &RECORD_LAYER_get_wbuf(&s->rlayer)[pipes - 1];
OPENSSL_free(wb->buf);
if (s->wbio == NULL || !BIO_get_ktls_send(s->wbio))
OPENSSL_free(wb->buf);
wb->buf = NULL;
pipes--;
}

View file

@ -22,6 +22,7 @@
#include <openssl/ct.h>
#include "internal/cryptlib.h"
#include "internal/refcount.h"
#include "internal/ktls.h"
static int ssl_undefined_function_1(SSL *ssl, SSL3_RECORD *r, size_t s, int t)
{
@ -1146,11 +1147,15 @@ void SSL_free(SSL *s)
dane_final(&s->dane);
CRYPTO_free_ex_data(CRYPTO_EX_INDEX_SSL, s, &s->ex_data);
RECORD_LAYER_release(&s->rlayer);
/* Ignore return value */
ssl_free_wbio_buffer(s);
BIO_free_all(s->wbio);
s->wbio = NULL;
BIO_free_all(s->rbio);
s->rbio = NULL;
BUF_MEM_free(s->init_buf);
@ -1201,8 +1206,6 @@ void SSL_free(SSL *s)
if (s->method != NULL)
s->method->ssl_free(s);
RECORD_LAYER_release(&s->rlayer);
SSL_CTX_free(s->ctx);
ASYNC_WAIT_CTX_free(s->waitctx);
@ -1342,6 +1345,15 @@ int SSL_set_fd(SSL *s, int fd)
}
BIO_set_fd(bio, fd, BIO_NOCLOSE);
SSL_set_bio(s, bio, bio);
#ifndef OPENSSL_NO_KTLS
/*
* The new socket is created successfully regardless of ktls_enable.
* ktls_enable doesn't change any functionality of the socket, except
* changing the setsockopt to enable the processing of ktls_start.
* Thus, it is not a problem to call it for non-TLS sockets.
*/
ktls_enable(fd);
#endif /* OPENSSL_NO_KTLS */
ret = 1;
err:
return ret;
@ -1361,6 +1373,15 @@ int SSL_set_wfd(SSL *s, int fd)
}
BIO_set_fd(bio, fd, BIO_NOCLOSE);
SSL_set0_wbio(s, bio);
#ifndef OPENSSL_NO_KTLS
/*
* The new socket is created successfully regardless of ktls_enable.
* ktls_enable doesn't change any functionality of the socket, except
* changing the setsockopt to enable the processing of ktls_start.
* Thus, it is not a problem to call it for non-TLS sockets.
*/
ktls_enable(fd);
#endif /* OPENSSL_NO_KTLS */
} else {
BIO_up_ref(rbio);
SSL_set0_wbio(s, rbio);
@ -2186,6 +2207,10 @@ long SSL_ctrl(SSL *s, int cmd, long larg, void *parg)
case SSL_CTRL_SET_MAX_SEND_FRAGMENT:
if (larg < 512 || larg > SSL3_RT_MAX_PLAIN_LENGTH)
return 0;
#ifndef OPENSSL_NO_KTLS
if (s->wbio != NULL && BIO_get_ktls_send(s->wbio))
return 0;
#endif /* OPENSSL_NO_KTLS */
s->max_send_fragment = larg;
if (s->max_send_fragment < s->split_send_fragment)
s->split_send_fragment = s->max_send_fragment;

View file

@ -34,6 +34,7 @@
# include "internal/dane.h"
# include "internal/refcount.h"
# include "internal/tsan_assist.h"
# include "internal/bio.h"
# ifdef OPENSSL_BUILD_SHLIBSSL
# undef OPENSSL_EXTERN

View file

@ -10,10 +10,14 @@
#include <stdio.h>
#include "ssl_locl.h"
#include "record/record_locl.h"
#include "internal/ktls.h"
#include "internal/cryptlib.h"
#include <openssl/comp.h>
#include <openssl/evp.h>
#include <openssl/kdf.h>
#include <openssl/rand.h>
#include <openssl/obj_mac.h>
/* seed1 through seed5 are concatenated */
static int tls1_PRF(SSL *s,
@ -98,6 +102,11 @@ int tls1_change_cipher_state(SSL *s, int which)
EVP_PKEY *mac_key;
size_t n, i, j, k, cl;
int reuse_dd = 0;
#ifndef OPENSSL_NO_KTLS
struct tls12_crypto_info_aes_gcm_128 crypto_info;
BIO *wbio;
unsigned char geniv[12];
#endif
c = s->s3->tmp.new_sym_enc;
m = s->s3->tmp.new_hash;
@ -319,6 +328,68 @@ int tls1_change_cipher_state(SSL *s, int which)
ERR_R_INTERNAL_ERROR);
goto err;
}
#ifndef OPENSSL_NO_KTLS
if (s->compress)
goto skip_ktls;
if ((which & SSL3_CC_READ) ||
((which & SSL3_CC_WRITE) && (s->mode & SSL_MODE_NO_KTLS_TX)))
goto skip_ktls;
/* ktls supports only the maximum fragment size */
if (ssl_get_max_send_fragment(s) != SSL3_RT_MAX_PLAIN_LENGTH)
goto skip_ktls;
/* check that cipher is AES_GCM_128 */
if (EVP_CIPHER_nid(c) != NID_aes_128_gcm
|| EVP_CIPHER_mode(c) != EVP_CIPH_GCM_MODE
|| EVP_CIPHER_key_length(c) != TLS_CIPHER_AES_GCM_128_KEY_SIZE)
goto skip_ktls;
/* check version is 1.2 */
if (s->version != TLS1_2_VERSION)
goto skip_ktls;
wbio = s->wbio;
if (!ossl_assert(wbio != NULL)) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS1_CHANGE_CIPHER_STATE,
ERR_R_INTERNAL_ERROR);
goto err;
}
/* All future data will get encrypted by ktls. Flush the BIO or skip ktls */
if (BIO_flush(wbio) <= 0)
goto skip_ktls;
/* ktls doesn't support renegotiation */
if (BIO_get_ktls_send(s->wbio)) {
SSLfatal(s, SSL_AD_NO_RENEGOTIATION, SSL_F_TLS1_CHANGE_CIPHER_STATE,
ERR_R_INTERNAL_ERROR);
goto err;
}
memset(&crypto_info, 0, sizeof(crypto_info));
crypto_info.info.cipher_type = TLS_CIPHER_AES_GCM_128;
crypto_info.info.version = s->version;
EVP_CIPHER_CTX_ctrl(dd, EVP_CTRL_GET_IV,
EVP_GCM_TLS_FIXED_IV_LEN + EVP_GCM_TLS_EXPLICIT_IV_LEN,
geniv);
memcpy(crypto_info.iv, geniv + EVP_GCM_TLS_FIXED_IV_LEN,
TLS_CIPHER_AES_GCM_128_IV_SIZE);
memcpy(crypto_info.salt, geniv, TLS_CIPHER_AES_GCM_128_SALT_SIZE);
memcpy(crypto_info.key, key, EVP_CIPHER_key_length(c));
memcpy(crypto_info.rec_seq, &s->rlayer.write_sequence,
TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE);
/* ktls works with user provided buffers directly */
if (BIO_set_ktls(wbio, &crypto_info, which & SSL3_CC_WRITE)) {
ssl3_release_write_buffer(s);
SSL_set_options(s, SSL_OP_NO_RENEGOTIATION);
}
skip_ktls:
#endif /* OPENSSL_NO_KTLS */
s->statem.enc_write_state = ENC_WRITE_STATE_VALID;
#ifdef SSL_DEBUG

View file

@ -108,6 +108,7 @@ BIO_get_buffer_num_lines define
BIO_get_cipher_ctx define
BIO_get_cipher_status define
BIO_get_close define
BIO_get_ktls_send define
BIO_get_conn_address define
BIO_get_conn_hostname define
BIO_get_conn_port define