[openssl-dev] [RFC 4/4] ssl: Linux TLS Tx Offload
Boris Pismenny
borisp at mellanox.com
Wed Jun 7 12:35:49 UTC 2017
This patch adds support for Linux TLS Tx offload.
The data-path of the TLS socket is offloaded to
the kernel after CCS is complete.
Change-Id: Ia966192a6704d1a57b74b2640ac04d55fb74c1c7
Signed-off-by: Boris Pismenny <borisp at mellanox.com>
---
ssl/record/rec_layer_s3.c | 95 ++++++++++++++++++++++++++++++++++-------------
ssl/t1_enc.c | 69 ++++++++++++++++++++++++++++++++++
2 files changed, 139 insertions(+), 25 deletions(-)
diff --git a/ssl/record/rec_layer_s3.c b/ssl/record/rec_layer_s3.c
index 46870c0..a36be02 100644
--- a/ssl/record/rec_layer_s3.c
+++ b/ssl/record/rec_layer_s3.c
@@ -750,24 +750,31 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
/* Clear our SSL3_RECORD structures */
memset(wr, 0, sizeof wr);
for (j = 0; j < numpipes; j++) {
- /* write the header */
- *(outbuf[j]++) = type & 0xff;
- SSL3_RECORD_set_type(&wr[j], type);
+ if (BIO_get_offload_tx(s->wbio)) {
+ /* IV will be generated by the kernel if offload is used */
+ eivlen = 0;
- *(outbuf[j]++) = (s->version >> 8);
- /*
- * Some servers hang if initial client hello is larger than 256 bytes
- * and record version number > TLS 1.0
- */
- if (SSL_get_state(s) == TLS_ST_CW_CLNT_HELLO
- && !s->renegotiate && TLS1_get_version(s) > TLS1_VERSION)
- *(outbuf[j]++) = 0x1;
- else
- *(outbuf[j]++) = s->version & 0xff;
+ SSL3_RECORD_set_type(&wr[j], type);
+ } else {
+ /* write the header */
+ *(outbuf[j]++) = type & 0xff;
+ SSL3_RECORD_set_type(&wr[j], type);
- /* field where we are to write out packet length */
- plen[j] = outbuf[j];
- outbuf[j] += 2;
+ *(outbuf[j]++) = (s->version >> 8);
+ /*
+ * Some servers hang if initial client hello is larger
+ * than 256 bytes and record version number > TLS 1.0
+ */
+ if (SSL_get_state(s) == TLS_ST_CW_CLNT_HELLO
+ && !s->renegotiate && TLS1_get_version(s) > TLS1_VERSION)
+ *(outbuf[j]++) = 0x1;
+ else
+ *(outbuf[j]++) = s->version & 0xff;
+
+ /* field where we are to write out packet length */
+ plen[j] = outbuf[j];
+ outbuf[j] += 2;
+ }
/* lets setup the record stuff. */
SSL3_RECORD_set_data(&wr[j], outbuf[j] + eivlen);
@@ -815,8 +822,13 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
}
}
- if (s->method->ssl3_enc->enc(s, wr, numpipes, 1) < 1)
- goto err;
+ /* record sequence number is incremented inside ssl3_enc->enc() */
+ if (!BIO_get_offload_tx(s->wbio)) {
+ if (s->method->ssl3_enc->enc(s, wr, numpipes, 1) < 1) {
+ goto err;
+ }
+ }
+
for (j = 0; j < numpipes; j++) {
if (SSL_USE_ETM(s) && mac_size != 0) {
@@ -826,12 +838,30 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
SSL3_RECORD_add_length(&wr[j], mac_size);
}
- /* record length after mac and block padding */
- s2n(SSL3_RECORD_get_length(&wr[j]), plen[j]);
-
- if (s->msg_callback)
- s->msg_callback(1, 0, SSL3_RT_HEADER, plen[j] - 5, 5, s,
- s->msg_callback_arg);
+ if (!BIO_get_offload_tx(s->wbio)) {
+ /* record length after mac and block padding */
+ s2n(SSL3_RECORD_get_length(&wr[j]), plen[j]);
+ if (s->msg_callback)
+ s->msg_callback(1, 0, SSL3_RT_HEADER, plen[j] - 5, 5, s,
+ s->msg_callback_arg);
+ } else {
+ /* Create a fake SSL3_RT_HEADER msg to maintain compatibility */
+ if (s->msg_callback) {
+ unsigned char header[5] = {0}, *pheader = header + 3;
+
+ header[0] = type & 0xff;
+ header[1] = (s->version >> 8);
+ if (SSL_get_state(s) == TLS_ST_CW_CLNT_HELLO
+ && !s->renegotiate
+ && TLS1_get_version(s) > TLS1_VERSION)
+ header[2] = 0x1;
+ else
+ header[2] = s->version & 0xff;
+ s2n(SSL3_RECORD_get_length(&wr[j]), pheader);
+ s->msg_callback(1, 0, SSL3_RT_HEADER, header, 5, s,
+ s->msg_callback_arg);
+ }
+ }
/*
* we should now have wr->data pointing to the encrypted data, which is
@@ -839,7 +869,11 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
*/
SSL3_RECORD_set_type(&wr[j], type); /* not needed but helps for
* debugging */
- SSL3_RECORD_add_length(&wr[j], SSL3_RT_HEADER_LENGTH);
+
+ /* No header when using offload */
+ if (!BIO_get_offload_tx(s->wbio)) {
+ SSL3_RECORD_add_length(&wr[j], SSL3_RT_HEADER_LENGTH);
+ }
if (create_empty_fragment) {
/*
@@ -901,10 +935,21 @@ int ssl3_write_pending(SSL *s, int type, const unsigned char *buf,
clear_sys_error();
if (s->wbio != NULL) {
s->rwstate = SSL_WRITING;
+ if (BIO_get_offload_tx(s->wbio) && type !=
+ SSL3_RT_APPLICATION_DATA) {
+ BIO_set_offload_tx_ctrl_msg(s->wbio, type);
+ }
i = BIO_write(s->wbio, (char *)
&(SSL3_BUFFER_get_buf(&wb[currbuf])
[SSL3_BUFFER_get_offset(&wb[currbuf])]),
(unsigned int)SSL3_BUFFER_get_left(&wb[currbuf]));
+ /* To prevent coalescing of control and data messages,
+ * such as in buffer_write, we flush the BIO
+ */
+ if (BIO_get_offload_tx(s->wbio) &&
+ type != SSL3_RT_APPLICATION_DATA) {
+ (void)BIO_flush(s->wbio);
+ }
} else {
SSLerr(SSL_F_SSL3_WRITE_PENDING, SSL_R_BIO_NOT_SET);
i = -1;
diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c
index 4aa5ddd..6b85c5c 100644
--- a/ssl/t1_enc.c
+++ b/ssl/t1_enc.c
@@ -41,6 +41,10 @@
#include <openssl/kdf.h>
#include <openssl/rand.h>
+#ifdef OPENSSL_LINUX_TLS
+# include "netinet/tcp.h" // Add TLS stuff here..
+#endif
+
/* seed1 through seed5 are concatenated */
static int tls1_PRF(SSL *s,
const void *seed1, int seed1_len,
@@ -121,6 +125,11 @@ int tls1_change_cipher_state(SSL *s, int which)
EVP_PKEY *mac_key;
int n, i, j, k, cl;
int reuse_dd = 0;
+#ifdef OPENSSL_LINUX_TLS
+ 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;
@@ -303,6 +312,66 @@ int tls1_change_cipher_state(SSL *s, int which)
SSLerr(SSL_F_TLS1_CHANGE_CIPHER_STATE, ERR_R_INTERNAL_ERROR);
goto err2;
}
+
+#ifdef OPENSSL_LINUX_TLS
+ if (!(which & SSL3_CC_WRITE)) {
+#ifdef SSL_DEBUG
+ printf("\nSkipping offload for non-write context\n");
+#endif // SSL_DEBUG
+ goto skip_offload;
+ }
+ memset(&crypto_info, 0, sizeof(crypto_info));
+
+ /* check that cipher is AES_GCM_128 */
+ if (EVP_CIPHER_mode(c) != EVP_CIPH_GCM_MODE) {
+#ifdef SSL_DEBUG
+ printf("\nGot mode %08lx != GCM, skipping offload\n",
+ EVP_CIPHER_mode(c));
+#endif // SSL_DEBUG
+ goto skip_offload;
+ }
+ crypto_info.info.cipher_type = TLS_CIPHER_AES_GCM_128;
+
+ /* check version is 1.2 */
+ if (s->version != TLS1_2_VERSION) {
+#ifdef SSL_DEBUG
+ printf("\nVersion mismatch got %08x, skipping offload\n", s->version);
+#endif // SSL_DEBUG
+ goto skip_offload;
+ }
+
+ if (EVP_CIPHER_key_length(c) != TLS_CIPHER_AES_GCM_128_KEY_SIZE) {
+#ifdef SSL_DEBUG
+ printf("\nUnexpected key length %d, skipping offload\n",
+ EVP_CIPHER_key_length(c));
+#endif // SSL_DEBUG
+ goto skip_offload;
+ }
+
+ crypto_info.info.version = s->version;
+
+ /* This is not the IV that is used by OpenSSL, but rather the sequence
+ * number. We should actually provide both iv and sequence.
+ */
+ EVP_CIPHER_CTX_ctrl(dd, EVP_CTRL_GCM_GET_IV, 12, geniv);
+ memcpy(crypto_info.iv, geniv + 4, 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);
+
+ wbio = s->wbio;
+ if (!wbio) {
+ goto skip_offload;
+ }
+
+ (void)BIO_flush(wbio);
+
+ BIO_set_offload_tx(wbio, &crypto_info);
+
+skip_offload:
+#endif // OPENSSL_LINUX_TLS
+
#ifdef OPENSSL_SSL_TRACE_CRYPTO
if (s->msg_callback) {
int wh = which & SSL3_CC_WRITE ? TLS1_RT_CRYPTO_WRITE : 0;
--
1.8.3.1
More information about the openssl-dev
mailing list