blob: 309623ec5e4036592f9e31d5b5a4d8264b96549d [file] [log] [blame]
/*
* ipsecmb.c - Intel IPSec Multi-buffer library Crypto Engine
*
* Copyright (c) 2019 Cisco Systemss
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <intel-ipsec-mb.h>
#include <vnet/vnet.h>
#include <vnet/plugin/plugin.h>
#include <vpp/app/version.h>
#include <vnet/crypto/crypto.h>
#include <vppinfra/cpu.h>
typedef struct
{
MB_MGR *mgr;
} ipsecmb_per_thread_data_t;
typedef struct ipsecmb_main_t_
{
ipsecmb_per_thread_data_t *per_thread_data;
} ipsecmb_main_t;
static ipsecmb_main_t ipsecmb_main;
#define foreach_ipsecmb_hmac_op \
_(SHA1, SHA1, sha1) \
_(SHA256, SHA_256, sha256) \
_(SHA384, SHA_384, sha384) \
_(SHA512, SHA_512, sha512)
#define foreach_ipsecmb_cipher_op \
_(AES_128_CBC, 128) \
_(AES_192_CBC, 192) \
_(AES_256_CBC, 256)
always_inline void
hash_expand_keys (const MB_MGR * mgr,
const u8 * key,
u32 length,
u8 block_size,
u8 ipad[256], u8 opad[256], hash_one_block_t fn)
{
u8 buf[block_size];
int i = 0;
if (length > block_size)
{
return;
}
memset (buf, 0x36, sizeof (buf));
for (i = 0; i < length; i++)
{
buf[i] ^= key[i];
}
fn (buf, ipad);
memset (buf, 0x5c, sizeof (buf));
for (i = 0; i < length; i++)
{
buf[i] ^= key[i];
}
fn (buf, opad);
}
always_inline void
ipsecmb_retire_hmac_job (JOB_AES_HMAC * job, u32 * n_fail)
{
vnet_crypto_op_t *op = job->user_data;
if (STS_COMPLETED != job->status)
{
op->status = VNET_CRYPTO_OP_STATUS_FAIL_BAD_HMAC;
*n_fail = *n_fail + 1;
}
else
op->status = VNET_CRYPTO_OP_STATUS_COMPLETED;
if (op->flags & VNET_CRYPTO_OP_FLAG_HMAC_CHECK)
{
if ((memcmp (op->digest, job->auth_tag_output, op->digest_len)))
{
*n_fail = *n_fail + 1;
op->status = VNET_CRYPTO_OP_STATUS_FAIL_BAD_HMAC;
}
}
else
clib_memcpy_fast (op->digest, job->auth_tag_output, op->digest_len);
}
static_always_inline u32
ipsecmb_ops_hmac_inline (vlib_main_t * vm,
const ipsecmb_per_thread_data_t * ptd,
vnet_crypto_op_t * ops[],
u32 n_ops,
u32 block_size,
hash_one_block_t fn, JOB_HASH_ALG alg)
{
JOB_AES_HMAC *job;
u32 i, n_fail = 0;
u8 scratch[n_ops][64];
/*
* queue all the jobs first ...
*/
for (i = 0; i < n_ops; i++)
{
vnet_crypto_op_t *op = ops[i];
u8 ipad[256], opad[256];
hash_expand_keys (ptd->mgr, op->key, op->key_len,
block_size, ipad, opad, fn);
job = IMB_GET_NEXT_JOB (ptd->mgr);
job->src = op->src;
job->hash_start_src_offset_in_bytes = 0;
job->msg_len_to_hash_in_bytes = op->len;
job->hash_alg = alg;
job->auth_tag_output_len_in_bytes = op->digest_len;
job->auth_tag_output = scratch[i];
job->cipher_mode = NULL_CIPHER;
job->cipher_direction = DECRYPT;
job->chain_order = HASH_CIPHER;
job->aes_key_len_in_bytes = op->key_len;
job->u.HMAC._hashed_auth_key_xor_ipad = ipad;
job->u.HMAC._hashed_auth_key_xor_opad = opad;
job->user_data = op;
job = IMB_SUBMIT_JOB (ptd->mgr);
if (job)
ipsecmb_retire_hmac_job (job, &n_fail);
}
/*
* .. then flush (i.e. complete) them
* We will have queued enough to satisfy the 'multi' buffer
*/
while ((job = IMB_FLUSH_JOB (ptd->mgr)))
{
ipsecmb_retire_hmac_job (job, &n_fail);
}
return n_ops - n_fail;
}
#define _(a, b, c) \
static_always_inline u32 \
ipsecmb_ops_hmac_##a (vlib_main_t * vm, \
vnet_crypto_op_t * ops[], \
u32 n_ops) \
{ \
ipsecmb_per_thread_data_t *ptd; \
ipsecmb_main_t *imbm; \
\
imbm = &ipsecmb_main; \
ptd = vec_elt_at_index (imbm->per_thread_data, vm->thread_index); \
\
return ipsecmb_ops_hmac_inline (vm, ptd, ops, n_ops, \
b##_BLOCK_SIZE, \
ptd->mgr->c##_one_block, \
b); \
}
foreach_ipsecmb_hmac_op;
#undef _
#define EXPANDED_KEY_N_BYTES (16 * 15)
always_inline void
ipsecmb_retire_cipher_job (JOB_AES_HMAC * job, u32 * n_fail)
{
vnet_crypto_op_t *op = job->user_data;
if (STS_COMPLETED != job->status)
{
op->status = VNET_CRYPTO_OP_STATUS_FAIL_BAD_HMAC;
*n_fail = *n_fail + 1;
}
else
op->status = VNET_CRYPTO_OP_STATUS_COMPLETED;
}
static_always_inline u32
ipsecmb_ops_cipher_inline (vlib_main_t * vm,
const ipsecmb_per_thread_data_t * ptd,
vnet_crypto_op_t * ops[],
u32 n_ops,
keyexp_t fn, JOB_CIPHER_DIRECTION direction)
{
JOB_AES_HMAC *job;
u32 i, n_fail = 0;
/*
* queue all the jobs first ...
*/
for (i = 0; i < n_ops; i++)
{
u8 aes_enc_key_expanded[EXPANDED_KEY_N_BYTES];
u8 aes_dec_key_expanded[EXPANDED_KEY_N_BYTES];
vnet_crypto_op_t *op = ops[i];
fn (op->key, aes_enc_key_expanded, aes_dec_key_expanded);
job = IMB_GET_NEXT_JOB (ptd->mgr);
job->src = op->src;
job->dst = op->dst;
job->msg_len_to_cipher_in_bytes = op->len;
job->cipher_start_src_offset_in_bytes = 0;
job->hash_alg = NULL_HASH;
job->cipher_mode = CBC;
job->cipher_direction = direction;
job->chain_order = (direction == ENCRYPT ? CIPHER_HASH : HASH_CIPHER);
job->aes_key_len_in_bytes = op->key_len;
job->aes_enc_key_expanded = aes_enc_key_expanded;
job->aes_dec_key_expanded = aes_dec_key_expanded;
job->iv = op->iv;
job->iv_len_in_bytes = op->iv_len;
job->user_data = op;
job = IMB_SUBMIT_JOB (ptd->mgr);
if (job)
ipsecmb_retire_cipher_job (job, &n_fail);
}
/*
* .. then flush (i.e. complete) them
* We will have queued enough to satisfy the 'multi' buffer
*/
while ((job = IMB_FLUSH_JOB (ptd->mgr)))
{
ipsecmb_retire_cipher_job (job, &n_fail);
}
return n_ops - n_fail;
}
#define _(a, b) \
static_always_inline u32 \
ipsecmb_ops_cipher_enc_##a (vlib_main_t * vm, \
vnet_crypto_op_t * ops[], \
u32 n_ops) \
{ \
ipsecmb_per_thread_data_t *ptd; \
ipsecmb_main_t *imbm; \
\
imbm = &ipsecmb_main; \
ptd = vec_elt_at_index (imbm->per_thread_data, vm->thread_index); \
\
return ipsecmb_ops_cipher_inline (vm, ptd, ops, n_ops, \
ptd->mgr->keyexp_##b, \
ENCRYPT); \
}
foreach_ipsecmb_cipher_op;
#undef _
#define _(a, b) \
static_always_inline u32 \
ipsecmb_ops_cipher_dec_##a (vlib_main_t * vm, \
vnet_crypto_op_t * ops[], \
u32 n_ops) \
{ \
ipsecmb_per_thread_data_t *ptd; \
ipsecmb_main_t *imbm; \
\
imbm = &ipsecmb_main; \
ptd = vec_elt_at_index (imbm->per_thread_data, vm->thread_index); \
\
return ipsecmb_ops_cipher_inline (vm, ptd, ops, n_ops, \
ptd->mgr->keyexp_##b, \
DECRYPT); \
}
foreach_ipsecmb_cipher_op;
#undef _
static clib_error_t *
crypto_ipsecmb_init (vlib_main_t * vm)
{
ipsecmb_main_t *imbm = &ipsecmb_main;
ipsecmb_per_thread_data_t *ptd;
vlib_thread_main_t *tm = vlib_get_thread_main ();
clib_error_t *error;
u32 eidx;
if ((error = vlib_call_init_function (vm, vnet_crypto_init)))
return error;
/*
* A priority that is better than OpenSSL but worse than VPP natvie
*/
eidx = vnet_crypto_register_engine (vm, "ipsecmb", 80,
"Intel IPSEC multi-buffer");
vec_validate (imbm->per_thread_data, tm->n_vlib_mains - 1);
if (clib_cpu_supports_avx512f ())
{
vec_foreach (ptd, imbm->per_thread_data)
{
ptd->mgr = alloc_mb_mgr (0);
init_mb_mgr_avx512 (ptd->mgr);
}
}
else if (clib_cpu_supports_avx2 ())
{
vec_foreach (ptd, imbm->per_thread_data)
{
ptd->mgr = alloc_mb_mgr (0);
init_mb_mgr_avx2 (ptd->mgr);
}
}
else
{
vec_foreach (ptd, imbm->per_thread_data)
{
ptd->mgr = alloc_mb_mgr (0);
init_mb_mgr_sse (ptd->mgr);
}
}
#define _(a, b, c) \
vnet_crypto_register_ops_handler (vm, eidx, VNET_CRYPTO_OP_##a##_HMAC, \
ipsecmb_ops_hmac_##a); \
foreach_ipsecmb_hmac_op;
#undef _
#define _(a, b) \
vnet_crypto_register_ops_handler (vm, eidx, VNET_CRYPTO_OP_##a##_ENC, \
ipsecmb_ops_cipher_enc_##a); \
foreach_ipsecmb_cipher_op;
#undef _
#define _(a, b) \
vnet_crypto_register_ops_handler (vm, eidx, VNET_CRYPTO_OP_##a##_DEC, \
ipsecmb_ops_cipher_dec_##a); \
foreach_ipsecmb_cipher_op;
#undef _
return 0;
}
VLIB_INIT_FUNCTION (crypto_ipsecmb_init);
/* *INDENT-OFF* */
VLIB_PLUGIN_REGISTER () =
{
.version = VPP_BUILD_VER,
.description = "Intel IPSEC multi-buffer",
};
/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/