aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeale Ranns <nranns@cisco.com>2019-04-08 07:36:50 +0000
committerDave Wallace <dwallacelf@gmail.com>2019-04-10 18:35:13 +0000
commit92e93844826fc080ea7f3495ba3e06de3f4d03f1 (patch)
tree0725acaacebcec816db24be35143824e4326bb68
parent9847792e18f1fd9f3b8fedea4fc53eed1096ab75 (diff)
crypto: Intel IPSEC-MB engine
A plugin to use Intel IPSec MB library as a VPP crypto engine This changes uses concepts from: https://gerrit.fd.io/r/#/c/17301/ hence that author's work is acknowledge below Change-Id: I2bf3beeb10f3c9706fa5efbdc9bc023e310f5a92 Signed-off-by: Neale Ranns <nranns@cisco.com> Signed-off-by: Klement Sekera <ksekera@cisco.com>
-rw-r--r--src/plugins/crypto_ipsecmb/CMakeLists.txt35
-rw-r--r--src/plugins/crypto_ipsecmb/ipsecmb.c381
-rw-r--r--src/vnet/ipsec/esp_decrypt.c13
-rw-r--r--src/vnet/ipsec/esp_encrypt.c2
-rw-r--r--test/test_ipsec_ah.py86
-rw-r--r--test/test_ipsec_esp.py4
6 files changed, 478 insertions, 43 deletions
diff --git a/src/plugins/crypto_ipsecmb/CMakeLists.txt b/src/plugins/crypto_ipsecmb/CMakeLists.txt
new file mode 100644
index 00000000000..a6bb8dc7dcd
--- /dev/null
+++ b/src/plugins/crypto_ipsecmb/CMakeLists.txt
@@ -0,0 +1,35 @@
+
+# Copyright (c) 2019 Cisco Systems
+# 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.
+
+find_path(IPSECMB_INCLUDE_DIR NAMES intel-ipsec-mb.h HINTS ${IPSECMB_INCLUDE_DIR_HINT})
+find_library(IPSECMB_LIB NAMES libIPSec_MB.a HINTS ${IPSECMB_LIB_DIR_HINT})
+
+if(IPSECMB_INCLUDE_DIR AND IPSECMB_LIB)
+
+ get_filename_component(IPSECMB_LIB_DIR ${IPSECMB_LIB} DIRECTORY)
+ set(IPSECMB_LINK_FLAGS "${IPSECMB_LINK_FLAGS} -L${IPSECMB_LIB_DIR} -Wl,--whole-archive ${IPSECMB_LIB} -Wl,--no-whole-archive")
+ set(IPSECMB_LINK_FLAGS "${IPSECMB_LINK_FLAGS} -Wl,--exclude-libs,libIPSec_MB.a,-l:libIPSec_MB.a")
+ include_directories(${IPSECMB_INCLUDE_DIR})
+ add_vpp_plugin(crypto_ipsecmb
+ SOURCES
+ ipsecmb.c
+
+ LINK_FLAGS
+ ${IPSECMB_LINK_FLAGS}
+ )
+
+ message(STATUS "Intel IPSecMB found: ${IPSECMB_INCLUDE_DIR}")
+else()
+ message(STATUS "Intel IPSecMB not found")
+endif()
diff --git a/src/plugins/crypto_ipsecmb/ipsecmb.c b/src/plugins/crypto_ipsecmb/ipsecmb.c
new file mode 100644
index 00000000000..309623ec5e4
--- /dev/null
+++ b/src/plugins/crypto_ipsecmb/ipsecmb.c
@@ -0,0 +1,381 @@
+/*
+ * 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:
+ */
diff --git a/src/vnet/ipsec/esp_decrypt.c b/src/vnet/ipsec/esp_decrypt.c
index 9b24e5aaeaa..de951d1dc85 100644
--- a/src/vnet/ipsec/esp_decrypt.c
+++ b/src/vnet/ipsec/esp_decrypt.c
@@ -232,7 +232,9 @@ esp_decrypt_inline (vlib_main_t * vm,
vec_add2_aligned (ptd->crypto_ops, op, 1, CLIB_CACHE_LINE_BYTES);
vnet_crypto_op_init (op, sa0->crypto_dec_op_id);
op->key = sa0->crypto_key.data;
+ op->key_len = sa0->crypto_key.len;
op->iv = payload;
+ op->iv_len = cpd.iv_sz;
op->src = op->dst = payload += cpd.iv_sz;
op->len = len;
op->user_data = b - bufs;
@@ -280,8 +282,15 @@ esp_decrypt_inline (vlib_main_t * vm,
ASSERT (op - ptd->crypto_ops < vec_len (ptd->crypto_ops));
if (op->status != VNET_CRYPTO_OP_STATUS_COMPLETED)
{
- u32 bi = op->user_data;
- u32 err = ESP_DECRYPT_ERROR_CRYPTO_ENGINE_ERROR;
+ u32 err, bi;
+
+ bi = op->user_data;
+
+ if (op->status == VNET_CRYPTO_OP_STATUS_FAIL_BAD_HMAC)
+ err = ESP_DECRYPT_ERROR_INTEG_ERROR;
+ else
+ err = ESP_DECRYPT_ERROR_CRYPTO_ENGINE_ERROR;
+
bufs[bi]->error = node->errors[err];
nexts[bi] = ESP_DECRYPT_NEXT_DROP;
n--;
diff --git a/src/vnet/ipsec/esp_encrypt.c b/src/vnet/ipsec/esp_encrypt.c
index bb1effda68b..c8018594bd1 100644
--- a/src/vnet/ipsec/esp_encrypt.c
+++ b/src/vnet/ipsec/esp_encrypt.c
@@ -431,8 +431,10 @@ esp_encrypt_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
vec_add2_aligned (ptd->crypto_ops, op, 1, CLIB_CACHE_LINE_BYTES);
vnet_crypto_op_init (op, sa0->crypto_enc_op_id);
op->iv = payload - iv_sz;
+ op->iv_len = iv_sz;
op->src = op->dst = payload;
op->key = sa0->crypto_key.data;
+ op->key_len = sa0->crypto_key.len;
op->len = payload_len - icv_sz;
op->flags = VNET_CRYPTO_OP_FLAG_INIT_IV;
op->user_data = b - bufs;
diff --git a/test/test_ipsec_ah.py b/test/test_ipsec_ah.py
index ec0c5586c2b..38d97d584c5 100644
--- a/test/test_ipsec_ah.py
+++ b/test/test_ipsec_ah.py
@@ -313,7 +313,10 @@ class TestIpsecAhAll(ConfigIpsecAH,
super(TestIpsecAhAll, self).tearDown()
def test_integ_algs(self):
- """SHA1_96, SHA256, SHA394, SHA512 w/ & w/o ESN"""
+ """All Engines SHA[1_96, 256, 394, 512] w/ & w/o ESN"""
+ # foreach VPP crypto engine
+ engines = ["ia32", "ipsecmb", "openssl"]
+
algos = [{'vpp': VppEnum.vl_api_ipsec_integ_alg_t.
IPSEC_API_INTEG_ALG_SHA1_96,
'scapy': "HMAC-SHA1-96"},
@@ -331,45 +334,50 @@ class TestIpsecAhAll(ConfigIpsecAH,
IPSEC_API_SAD_FLAG_USE_ESN)]
#
- # loop through each of the algorithms
+ # loop through the VPP engines
#
- for algo in algos:
- # with self.subTest(algo=algo['scapy']):
- for flag in flags:
- #
- # setup up the config paramters
- #
- self.ipv4_params = IPsecIPv4Params()
- self.ipv6_params = IPsecIPv6Params()
-
- self.params = {self.ipv4_params.addr_type:
- self.ipv4_params,
- self.ipv6_params.addr_type:
- self.ipv6_params}
-
- for _, p in self.params.items():
- p.auth_algo_vpp_id = algo['vpp']
- p.auth_algo = algo['scapy']
- p.flags = p.flags | flag
-
- #
- # configure the SPDs. SAs, etc
- #
- self.config_network(self.params.values())
-
- #
- # run some traffic.
- # An exhautsive 4o6, 6o4 is not necessary for each algo
- #
- self.verify_tra_basic6(count=17)
- self.verify_tra_basic4(count=17)
- self.verify_tun_66(self.params[socket.AF_INET6], count=17)
- self.verify_tun_44(self.params[socket.AF_INET], count=17)
-
- #
- # remove the SPDs, SAs, etc
- #
- self.unconfig_network()
+ for engine in engines:
+ self.vapi.cli("set crypto engine all %s" % engine)
+ #
+ # loop through each of the algorithms
+ #
+ for algo in algos:
+ # with self.subTest(algo=algo['scapy']):
+ for flag in flags:
+ #
+ # setup up the config paramters
+ #
+ self.ipv4_params = IPsecIPv4Params()
+ self.ipv6_params = IPsecIPv6Params()
+
+ self.params = {self.ipv4_params.addr_type:
+ self.ipv4_params,
+ self.ipv6_params.addr_type:
+ self.ipv6_params}
+
+ for _, p in self.params.items():
+ p.auth_algo_vpp_id = algo['vpp']
+ p.auth_algo = algo['scapy']
+ p.flags = p.flags | flag
+
+ #
+ # configure the SPDs. SAs, etc
+ #
+ self.config_network(self.params.values())
+
+ #
+ # run some traffic.
+ # An exhautsive 4o6, 6o4 is not necessary for each algo
+ #
+ self.verify_tra_basic6(count=17)
+ self.verify_tra_basic4(count=17)
+ self.verify_tun_66(self.params[socket.AF_INET6], count=17)
+ self.verify_tun_44(self.params[socket.AF_INET], count=17)
+
+ #
+ # remove the SPDs, SAs, etc
+ #
+ self.unconfig_network()
if __name__ == '__main__':
diff --git a/test/test_ipsec_esp.py b/test/test_ipsec_esp.py
index 283914071ca..a8b28c53ee6 100644
--- a/test/test_ipsec_esp.py
+++ b/test/test_ipsec_esp.py
@@ -322,7 +322,7 @@ class TemplateIpsecEspUdp(ConfigIpsecESP):
self.tun_if).add_vpp_config()
self.config_esp_tun(p)
- self.logger.info(self.vapi.ppcli("show ipsec"))
+ self.logger.info(self.vapi.ppcli("show ipsec all"))
d = DpoProto.DPO_PROTO_IP4
VppIpRoute(self, p.remote_tun_if_host, p.addr_len,
@@ -356,7 +356,7 @@ class TestIpsecEspAll(ConfigIpsecESP,
"""All engines AES-CBC-[128, 192, 256] w/o ESN"""
# foreach VPP crypto engine
- engines = ["ia32", "openssl"]
+ engines = ["ia32", "ipsecmb", "openssl"]
# foreach crypto algorithm
algos = [{'vpp': VppEnum.vl_api_ipsec_crypto_alg_t.