[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