crypto: introduce async crypto infra 36/18036/54
authorFan Zhang <roy.fan.zhang@intel.com>
Wed, 29 Apr 2020 13:00:03 +0000 (14:00 +0100)
committerDamjan Marion <dmarion@me.com>
Thu, 30 Apr 2020 14:38:33 +0000 (14:38 +0000)
Type: feature

Signed-off-by: Damjan Marion <damarion@cisco.com>
Signed-off-by: Filip Tehlar <ftehlar@cisco.com>
Signed-off-by: Fan Zhang <roy.fan.zhang@intel.com>
Signed-off-by: Piotr Bronowski <piotrx.bronowski@intel.com>
Signed-off-by: Dariusz Kazimierski <dariuszx.kazimierski@intel.com>
Signed-off-by: Piotr Kleski <piotrx.kleski@intel.com>
Change-Id: I4c3fcccf55c36842b7b48aed260fef2802b5c54b

21 files changed:
src/plugins/crypto_ipsecmb/ipsecmb.c
src/plugins/crypto_native/main.c
src/plugins/dpdk/CMakeLists.txt
src/plugins/dpdk/cryptodev/cryptodev.c [new file with mode: 0644]
src/plugins/dpdk/cryptodev/cryptodev.h [new file with mode: 0644]
src/plugins/dpdk/device/init.c
src/plugins/dpdk/ipsec/ipsec.c
src/vnet/CMakeLists.txt
src/vnet/crypto/cli.c
src/vnet/crypto/crypto.c
src/vnet/crypto/crypto.h
src/vnet/crypto/format.c
src/vnet/crypto/node.c [new file with mode: 0644]
src/vnet/ipsec/esp.h
src/vnet/ipsec/esp_decrypt.c
src/vnet/ipsec/esp_encrypt.c
src/vnet/ipsec/ipsec.c
src/vnet/ipsec/ipsec.h
src/vnet/ipsec/ipsec_cli.c
src/vnet/ipsec/ipsec_sa.c
src/vnet/ipsec/ipsec_sa.h

index 14d618c..3c5495a 100644 (file)
@@ -459,6 +459,10 @@ crypto_ipsecmb_key_handler (vlib_main_t * vm, vnet_crypto_key_op_t kop,
       clib_mem_free_s (imbm->key_data[idx]);
     }
 
+  /** TODO: add linked alg support **/
+  if (key->type == VNET_CRYPTO_KEY_TYPE_LINK)
+    return;
+
   kd = imbm->key_data[idx] = clib_mem_alloc_aligned (ad->data_size,
                                                     CLIB_CACHE_LINE_BYTES);
 
index 45d3d8d..7a42e4b 100644 (file)
@@ -45,6 +45,10 @@ crypto_native_key_handler (vlib_main_t * vm, vnet_crypto_key_op_t kop,
       return;
     }
 
+  /** TODO: add linked alg support **/
+  if (key->type == VNET_CRYPTO_KEY_TYPE_LINK)
+    return;
+
   vec_validate_aligned (cm->key_data, idx, CLIB_CACHE_LINE_BYTES);
 
   if (kop == VNET_CRYPTO_KEY_OP_MODIFY && cm->key_data[idx])
index af8c80d..e51d66b 100644 (file)
@@ -130,6 +130,7 @@ add_vpp_plugin(dpdk
   ipsec/esp_decrypt.c
   ipsec/esp_encrypt.c
   ipsec/ipsec.c
+  cryptodev/cryptodev.c
 
   MULTIARCH_SOURCES
   buffer.c
diff --git a/src/plugins/dpdk/cryptodev/cryptodev.c b/src/plugins/dpdk/cryptodev/cryptodev.c
new file mode 100644 (file)
index 0000000..0506ab8
--- /dev/null
@@ -0,0 +1,1377 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2020 Intel and/or its affiliates.
+ * 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 <vlib/vlib.h>
+#include <vnet/plugin/plugin.h>
+#include <vnet/crypto/crypto.h>
+#include <vnet/vnet.h>
+#include <vpp/app/version.h>
+
+#include <dpdk/buffer.h>
+#include <dpdk/device/dpdk.h>
+#include <dpdk/device/dpdk_priv.h>
+#include <rte_bus_vdev.h>
+#include <rte_cryptodev.h>
+#include <rte_crypto_sym.h>
+#include <rte_crypto.h>
+#include <rte_cryptodev_pmd.h>
+#include <rte_config.h>
+
+#define CRYPTODEV_NB_CRYPTO_OPS 1024
+#define CRYPTODEV_NB_SESSION    10240
+#define CRYPTODEV_DEF_DRIVE    crypto_aesni_mb
+
+#define CRYPTODEV_IV_OFFSET (offsetof (cryptodev_op_t, iv))
+#define CRYPTODEV_AAD_OFFSET (offsetof (cryptodev_op_t, aad))
+#define CRYPTODEV_DIGEST_OFFSET (offsetof (cryptodev_op_t, digest))
+
+/* VNET_CRYPTO_ALGO, TYPE, DPDK_CRYPTO_ALGO, IV_LEN, TAG_LEN, AAD_LEN */
+#define foreach_vnet_aead_crypto_conversion \
+  _(AES_128_GCM, AEAD, AES_GCM, 12, 16, 8)  \
+  _(AES_128_GCM, AEAD, AES_GCM, 12, 16, 12) \
+  _(AES_192_GCM, AEAD, AES_GCM, 12, 16, 8)  \
+  _(AES_192_GCM, AEAD, AES_GCM, 12, 16, 12) \
+  _(AES_256_GCM, AEAD, AES_GCM, 12, 16, 8)  \
+  _(AES_256_GCM, AEAD, AES_GCM, 12, 16, 12)
+
+/**
+ * crypto (alg, cryptodev_alg), hash (alg, digest-size)
+ **/
+#define foreach_cryptodev_link_async_alg       \
+  _ (AES_128_CBC, AES_CBC, SHA1, 12)           \
+  _ (AES_192_CBC, AES_CBC, SHA1, 12)           \
+  _ (AES_256_CBC, AES_CBC, SHA1, 12)           \
+  _ (AES_128_CBC, AES_CBC, SHA224, 14)         \
+  _ (AES_192_CBC, AES_CBC, SHA224, 14)         \
+  _ (AES_256_CBC, AES_CBC, SHA224, 14)         \
+  _ (AES_128_CBC, AES_CBC, SHA256, 16)         \
+  _ (AES_192_CBC, AES_CBC, SHA256, 16)         \
+  _ (AES_256_CBC, AES_CBC, SHA256, 16)         \
+  _ (AES_128_CBC, AES_CBC, SHA384, 24)         \
+  _ (AES_192_CBC, AES_CBC, SHA384, 24)         \
+  _ (AES_256_CBC, AES_CBC, SHA384, 24)         \
+  _ (AES_128_CBC, AES_CBC, SHA512, 32)         \
+  _ (AES_192_CBC, AES_CBC, SHA512, 32)         \
+  _ (AES_256_CBC, AES_CBC, SHA512, 32)
+
+#define foreach_vnet_crypto_status_conversion \
+  _(SUCCESS, COMPLETED)                       \
+  _(NOT_PROCESSED, WORK_IN_PROGRESS)          \
+  _(AUTH_FAILED, FAIL_BAD_HMAC)               \
+  _(INVALID_SESSION, FAIL_ENGINE_ERR)         \
+  _(INVALID_ARGS, FAIL_ENGINE_ERR)            \
+  _(ERROR, FAIL_ENGINE_ERR)
+
+static const vnet_crypto_op_status_t cryptodev_status_conversion[] = {
+#define _(a, b) VNET_CRYPTO_OP_STATUS_##b,
+  foreach_vnet_crypto_status_conversion
+#undef _
+};
+
+typedef struct
+{
+  CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
+  struct rte_crypto_op op;
+  struct rte_crypto_sym_op sop;
+  u8 iv[16];
+  u8 aad[16];
+  vnet_crypto_async_frame_t *frame;
+  u32 n_elts;
+} cryptodev_op_t;
+
+typedef enum
+{
+  CRYPTODEV_OP_TYPE_ENCRYPT = 0,
+  CRYPTODEV_OP_TYPE_DECRYPT,
+  CRYPTODEV_N_OP_TYPES,
+} cryptodev_op_type_t;
+
+typedef struct
+{
+  struct rte_cryptodev_sym_session *keys[CRYPTODEV_N_OP_TYPES];
+} cryptodev_key_t;
+
+typedef struct
+{
+  u32 dev_id;
+  u32 q_id;
+  char *desc;
+} cryptodev_inst_t;
+
+typedef struct
+{
+  struct rte_mempool *cop_pool;
+  struct rte_mempool *sess_pool;
+  struct rte_mempool *sess_priv_pool;
+} cryptodev_numa_data_t;
+
+typedef struct
+{
+  CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
+  u16 cryptodev_id;
+  u16 cryptodev_q;
+  u32 inflight;
+  cryptodev_op_t **cops;
+  struct rte_ring *ring;
+} cryptodev_engine_thread_t;
+
+typedef struct
+{
+  cryptodev_numa_data_t *per_numa_data;
+  cryptodev_key_t *keys;
+  cryptodev_engine_thread_t *per_thread_data;
+  cryptodev_inst_t *cryptodev_inst;
+  clib_bitmap_t *active_cdev_inst_mask;
+  clib_spinlock_t tlock;
+} cryptodev_main_t;
+
+cryptodev_main_t cryptodev_main;
+
+static int
+prepare_aead_xform (struct rte_crypto_sym_xform *xform,
+                   cryptodev_op_type_t op_type,
+                   const vnet_crypto_key_t * key, u32 aad_len)
+{
+  struct rte_crypto_aead_xform *aead_xform = &xform->aead;
+  memset (xform, 0, sizeof (*xform));
+  xform->type = RTE_CRYPTO_SYM_XFORM_AEAD;
+  xform->next = 0;
+
+  if (key->alg != VNET_CRYPTO_ALG_AES_128_GCM &&
+      key->alg != VNET_CRYPTO_ALG_AES_192_GCM &&
+      key->alg != VNET_CRYPTO_ALG_AES_256_GCM)
+    return -1;
+
+  aead_xform->algo = RTE_CRYPTO_AEAD_AES_GCM;
+  aead_xform->op = (op_type == CRYPTODEV_OP_TYPE_ENCRYPT) ?
+    RTE_CRYPTO_AEAD_OP_ENCRYPT : RTE_CRYPTO_AEAD_OP_DECRYPT;
+  aead_xform->aad_length = aad_len;
+  aead_xform->digest_length = 16;
+  aead_xform->iv.offset = CRYPTODEV_IV_OFFSET;
+  aead_xform->iv.length = 12;
+  aead_xform->key.data = key->data;
+  aead_xform->key.length = vec_len (key->data);
+
+  return 0;
+}
+
+static int
+prepare_linked_xform (struct rte_crypto_sym_xform *xforms,
+                     cryptodev_op_type_t op_type,
+                     const vnet_crypto_key_t * key)
+{
+  struct rte_crypto_sym_xform *xform_cipher, *xform_auth;
+  vnet_crypto_key_t *key_cipher, *key_auth;
+  enum rte_crypto_cipher_algorithm cipher_algo = ~0;
+  enum rte_crypto_auth_algorithm auth_algo = ~0;
+  u32 digest_len = ~0;
+
+  key_cipher = vnet_crypto_get_key (key->index_crypto);
+  key_auth = vnet_crypto_get_key (key->index_integ);
+  if (!key_cipher || !key_auth)
+    return -1;
+
+  if (op_type == CRYPTODEV_OP_TYPE_ENCRYPT)
+    {
+      xform_cipher = xforms;
+      xform_auth = xforms + 1;
+      xform_cipher->cipher.op = RTE_CRYPTO_CIPHER_OP_ENCRYPT;
+      xform_auth->auth.op = RTE_CRYPTO_AUTH_OP_GENERATE;
+    }
+  else
+    {
+      xform_cipher = xforms + 1;
+      xform_auth = xforms;
+      xform_cipher->cipher.op = RTE_CRYPTO_CIPHER_OP_DECRYPT;
+      xform_auth->auth.op = RTE_CRYPTO_AUTH_OP_VERIFY;
+    }
+
+  xform_cipher->type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+  xform_auth->type = RTE_CRYPTO_SYM_XFORM_AUTH;
+  xforms->next = xforms + 1;
+
+  switch (key->async_alg)
+    {
+#define _(a, b, c, d) \
+  case VNET_CRYPTO_ALG_##a##_##c##_TAG##d:\
+    cipher_algo = RTE_CRYPTO_CIPHER_##b; \
+    auth_algo = RTE_CRYPTO_AUTH_##c##_HMAC; \
+    digest_len = d; \
+    break;
+
+      foreach_cryptodev_link_async_alg
+#undef _
+    default:
+      return -1;
+    }
+
+  xform_cipher->cipher.algo = cipher_algo;
+  xform_cipher->cipher.key.data = key_cipher->data;
+  xform_cipher->cipher.key.length = vec_len (key_cipher->data);
+  xform_cipher->cipher.iv.length = 16;
+  xform_cipher->cipher.iv.offset = CRYPTODEV_IV_OFFSET;
+
+  xform_auth->auth.algo = auth_algo;
+  xform_auth->auth.digest_length = digest_len;
+  xform_auth->auth.key.data = key_auth->data;
+  xform_auth->auth.key.length = vec_len (key_auth->data);
+
+  return 0;
+}
+
+static int
+cryptodev_session_create (vnet_crypto_key_t * const key,
+                         struct rte_mempool *sess_priv_pool,
+                         cryptodev_key_t * session_pair, u32 aad_len)
+{
+  struct rte_crypto_sym_xform xforms_enc[2] = { {0} };
+  struct rte_crypto_sym_xform xforms_dec[2] = { {0} };
+  cryptodev_main_t *cmt = &cryptodev_main;
+  cryptodev_inst_t *dev_inst;
+  struct rte_cryptodev *cdev;
+  int ret;
+  uint8_t dev_id = 0;
+
+  if (key->type == VNET_CRYPTO_KEY_TYPE_LINK)
+    ret = prepare_linked_xform (xforms_enc, CRYPTODEV_OP_TYPE_ENCRYPT, key);
+  else
+    ret = prepare_aead_xform (xforms_enc, CRYPTODEV_OP_TYPE_ENCRYPT, key,
+                             aad_len);
+  if (ret)
+    return 0;
+
+  if (key->type == VNET_CRYPTO_KEY_TYPE_LINK)
+    prepare_linked_xform (xforms_dec, CRYPTODEV_OP_TYPE_DECRYPT, key);
+  else
+    prepare_aead_xform (xforms_dec, CRYPTODEV_OP_TYPE_DECRYPT, key, aad_len);
+
+  vec_foreach (dev_inst, cmt->cryptodev_inst)
+  {
+    dev_id = dev_inst->dev_id;
+    cdev = rte_cryptodev_pmd_get_dev (dev_id);
+
+    /* if the session is already configured for the driver type, avoid
+       configuring it again to increase the session data's refcnt */
+    if (session_pair->keys[0]->sess_data[cdev->driver_id].data &&
+       session_pair->keys[1]->sess_data[cdev->driver_id].data)
+      continue;
+
+    ret = rte_cryptodev_sym_session_init (dev_id, session_pair->keys[0],
+                                         xforms_enc, sess_priv_pool);
+    ret = rte_cryptodev_sym_session_init (dev_id, session_pair->keys[1],
+                                         xforms_dec, sess_priv_pool);
+    if (ret < 0)
+      return ret;
+  }
+  session_pair->keys[0]->opaque_data = aad_len;
+  session_pair->keys[1]->opaque_data = aad_len;
+
+  return 0;
+}
+
+static void
+cryptodev_session_del (struct rte_cryptodev_sym_session *sess)
+{
+  u32 n_devs, i;
+
+  if (sess == NULL)
+    return;
+
+  n_devs = rte_cryptodev_count ();
+
+  for (i = 0; i < n_devs; i++)
+    rte_cryptodev_sym_session_clear (i, sess);
+
+  rte_cryptodev_sym_session_free (sess);
+}
+
+static int
+cryptodev_check_supported_vnet_alg (vnet_crypto_key_t * key)
+{
+  vnet_crypto_alg_t alg;
+  if (key->type == VNET_CRYPTO_KEY_TYPE_LINK)
+    return 0;
+
+  alg = key->alg;
+
+#define _(a, b, c, d, e, f)    \
+  if (alg == VNET_CRYPTO_ALG_##a) \
+    return 0;
+
+  foreach_vnet_aead_crypto_conversion
+#undef _
+    return -1;
+}
+
+static_always_inline void
+cryptodev_sess_handler (vlib_main_t * vm, vnet_crypto_key_op_t kop,
+                       vnet_crypto_key_index_t idx, u32 aad_len)
+{
+  cryptodev_main_t *cmt = &cryptodev_main;
+  cryptodev_numa_data_t *numa_data;
+  vnet_crypto_key_t *key = vnet_crypto_get_key (idx);
+  struct rte_mempool *sess_pool, *sess_priv_pool;
+  cryptodev_key_t *ckey = 0;
+  int ret = 0;
+
+  if (kop == VNET_CRYPTO_KEY_OP_DEL)
+    {
+      if (idx >= vec_len (cmt->keys))
+       return;
+
+      ckey = pool_elt_at_index (cmt->keys, idx);
+      cryptodev_session_del (ckey->keys[0]);
+      cryptodev_session_del (ckey->keys[1]);
+      ckey->keys[0] = 0;
+      ckey->keys[1] = 0;
+      pool_put (cmt->keys, ckey);
+      return;
+    }
+  else if (kop == VNET_CRYPTO_KEY_OP_MODIFY)
+    {
+      if (idx >= vec_len (cmt->keys))
+       return;
+
+      ckey = pool_elt_at_index (cmt->keys, idx);
+
+      cryptodev_session_del (ckey->keys[0]);
+      cryptodev_session_del (ckey->keys[1]);
+      ckey->keys[0] = 0;
+      ckey->keys[1] = 0;
+    }
+  else                         /* create key */
+    pool_get_zero (cmt->keys, ckey);
+
+  /* do not create session for unsupported alg */
+  if (cryptodev_check_supported_vnet_alg (key))
+    return;
+
+  numa_data = vec_elt_at_index (cmt->per_numa_data, vm->numa_node);
+  sess_pool = numa_data->sess_pool;
+  sess_priv_pool = numa_data->sess_priv_pool;
+
+  ckey->keys[0] = rte_cryptodev_sym_session_create (sess_pool);
+  if (!ckey->keys[0])
+    {
+      ret = -1;
+      goto clear_key;
+    }
+
+  ckey->keys[1] = rte_cryptodev_sym_session_create (sess_pool);
+  if (!ckey->keys[1])
+    {
+      ret = -1;
+      goto clear_key;
+    }
+
+  ret = cryptodev_session_create (key, sess_priv_pool, ckey, aad_len);
+
+clear_key:
+  if (ret != 0)
+    {
+      cryptodev_session_del (ckey->keys[0]);
+      cryptodev_session_del (ckey->keys[1]);
+      memset (ckey, 0, sizeof (*ckey));
+      pool_put (cmt->keys, ckey);
+    }
+}
+
+/*static*/ void
+cryptodev_key_handler (vlib_main_t * vm, vnet_crypto_key_op_t kop,
+                      vnet_crypto_key_index_t idx)
+{
+  cryptodev_sess_handler (vm, kop, idx, 8);
+}
+
+static_always_inline void
+cryptodev_mark_frame_err_status (vnet_crypto_async_frame_t * f,
+                                vnet_crypto_op_status_t s)
+{
+  u32 n_elts = f->n_elts, i;
+
+  for (i = 0; i < n_elts; i++)
+    f->elts[i].status = s;
+  f->state = VNET_CRYPTO_FRAME_STATE_ELT_ERROR;
+}
+
+/* when vlib_buffer in chain is adjusted mbuf is not adjusted along, this
+ * function does that */
+static_always_inline rte_iova_t
+cryptodev_validate_mbuf_chain (vlib_main_t * vm, struct rte_mbuf *mb,
+                              vlib_buffer_t * b, u8 * digest)
+{
+  rte_iova_t digest_iova = 0;
+  struct rte_mbuf *first_mb = mb, *last_mb = mb; /**< last mbuf */
+
+  first_mb->nb_segs = 1;
+
+  while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
+    {
+      b = vlib_get_buffer (vm, b->next_buffer);
+      mb = rte_mbuf_from_vlib_buffer (b);
+      if (PREDICT_FALSE ((b->flags & VLIB_BUFFER_EXT_HDR_VALID) == 0))
+       rte_pktmbuf_reset (mb);
+      last_mb->next = mb;
+      last_mb = mb;
+      mb->data_len = b->current_length;
+      mb->pkt_len = b->current_length;
+      mb->data_off = VLIB_BUFFER_PRE_DATA_SIZE + b->current_data;
+      first_mb->nb_segs++;
+      if (PREDICT_FALSE (b->ref_count > 1))
+       mb->pool =
+         dpdk_no_cache_mempool_by_buffer_pool_index[b->buffer_pool_index];
+
+      if (b->data <= digest &&
+         b->data + b->current_data + b->current_length > digest)
+       digest_iova = rte_pktmbuf_iova (mb) + digest -
+         rte_pktmbuf_mtod (mb, u8 *);
+    }
+
+  return digest_iova;
+}
+
+static_always_inline int
+cryptodev_frame_linked_algs_enqueue (vlib_main_t * vm,
+                                    vnet_crypto_async_frame_t * frame,
+                                    cryptodev_op_type_t op_type,
+                                    u32 digest_len)
+{
+  cryptodev_main_t *cmt = &cryptodev_main;
+  cryptodev_numa_data_t *numa = cmt->per_numa_data + vm->numa_node;
+  cryptodev_engine_thread_t *cet = cmt->per_thread_data + vm->thread_index;
+  vnet_crypto_async_frame_elt_t *fe;
+  cryptodev_op_t **cop;
+  u32 *bi;
+  u32 n_enqueue, n_elts;
+  cryptodev_key_t *key = 0;
+  u32 last_key_index = ~0;
+
+  if (PREDICT_FALSE (frame == 0 || frame->n_elts == 0))
+    return -1;
+  n_elts = frame->n_elts;
+
+  if (PREDICT_FALSE (CRYPTODEV_NB_CRYPTO_OPS - cet->inflight < n_elts))
+    {
+      cryptodev_mark_frame_err_status (frame,
+                                      VNET_CRYPTO_OP_STATUS_FAIL_ENGINE_ERR);
+      return -1;
+    }
+
+  if (PREDICT_FALSE (rte_mempool_get_bulk (numa->cop_pool,
+                                          (void **) cet->cops, n_elts) < 0))
+    {
+      cryptodev_mark_frame_err_status (frame,
+                                      VNET_CRYPTO_OP_STATUS_FAIL_ENGINE_ERR);
+      return -1;
+    }
+
+  cop = cet->cops;
+  fe = frame->elts;
+  bi = frame->buffer_indices;
+  cop[0]->frame = frame;
+  cop[0]->n_elts = n_elts;
+
+  while (n_elts)
+    {
+      vlib_buffer_t *b = vlib_get_buffer (vm, bi[0]);
+      struct rte_crypto_sym_op *sop = &cop[0]->sop;
+      i16 crypto_offset = fe->crypto_start_offset;
+      i16 integ_offset = fe->integ_start_offset;
+      u32 offset_diff = crypto_offset - integ_offset;
+
+      if (n_elts > 2)
+       {
+         CLIB_PREFETCH (cop[1], CLIB_CACHE_LINE_BYTES * 3, STORE);
+         CLIB_PREFETCH (cop[2], CLIB_CACHE_LINE_BYTES * 3, STORE);
+         CLIB_PREFETCH (&fe[1], CLIB_CACHE_LINE_BYTES, LOAD);
+         CLIB_PREFETCH (&fe[2], CLIB_CACHE_LINE_BYTES, LOAD);
+       }
+      if (last_key_index != fe->key_index)
+       {
+         key = pool_elt_at_index (cmt->keys, fe->key_index);
+         last_key_index = fe->key_index;
+       }
+
+      sop->m_src = rte_mbuf_from_vlib_buffer (b);
+      sop->m_dst = 0;
+      /* mbuf prepend happens in the tx, but vlib_buffer happens in the nodes,
+       * so we have to manually adjust mbuf data_off here so cryptodev can
+       * correctly compute the data pointer. The prepend here will be later
+       * rewritten by tx. */
+      if (PREDICT_TRUE (fe->integ_start_offset < 0))
+       {
+         rte_pktmbuf_prepend (sop->m_src, -fe->integ_start_offset);
+         integ_offset = 0;
+         crypto_offset = offset_diff;
+       }
+      sop->session = key->keys[op_type];
+      sop->cipher.data.offset = crypto_offset;
+      sop->cipher.data.length = fe->crypto_total_length;
+      sop->auth.data.offset = integ_offset;
+      sop->auth.data.length = fe->crypto_total_length + fe->integ_length_adj;
+      sop->auth.digest.data = fe->digest;
+      if (PREDICT_TRUE (!(fe->flags & VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS)))
+       sop->auth.digest.phys_addr = rte_pktmbuf_iova (sop->m_src) +
+         fe->digest - rte_pktmbuf_mtod (sop->m_src, u8 *);
+      else
+       sop->auth.digest.phys_addr =
+         cryptodev_validate_mbuf_chain (vm, sop->m_src, b, fe->digest);
+      clib_memcpy_fast (cop[0]->iv, fe->iv, 16);
+      cop++;
+      bi++;
+      fe++;
+      n_elts--;
+    }
+
+  n_enqueue = rte_cryptodev_enqueue_burst (cet->cryptodev_id,
+                                          cet->cryptodev_q,
+                                          (struct rte_crypto_op **)
+                                          cet->cops, frame->n_elts);
+  ASSERT (n_enqueue == frame->n_elts);
+  cet->inflight += n_enqueue;
+
+  return 0;
+}
+
+static_always_inline int
+cryptodev_frame_gcm_enqueue (vlib_main_t * vm,
+                            vnet_crypto_async_frame_t * frame,
+                            cryptodev_op_type_t op_type, u8 aad_len)
+{
+  cryptodev_main_t *cmt = &cryptodev_main;
+  cryptodev_numa_data_t *numa = cmt->per_numa_data + vm->numa_node;
+  cryptodev_engine_thread_t *cet = cmt->per_thread_data + vm->thread_index;
+  vnet_crypto_async_frame_elt_t *fe;
+  cryptodev_op_t **cop;
+  u32 *bi;
+  u32 n_enqueue = 0, n_elts;
+  cryptodev_key_t *key = 0;
+  u32 last_key_index = ~0;
+
+  if (PREDICT_FALSE (frame == 0 || frame->n_elts == 0))
+    return -1;
+  n_elts = frame->n_elts;
+
+  if (PREDICT_FALSE (CRYPTODEV_NB_CRYPTO_OPS - cet->inflight < n_elts))
+    {
+      cryptodev_mark_frame_err_status (frame,
+                                      VNET_CRYPTO_OP_STATUS_FAIL_ENGINE_ERR);
+      return -1;
+    }
+
+  if (PREDICT_FALSE (rte_mempool_get_bulk (numa->cop_pool,
+                                          (void **) cet->cops, n_elts) < 0))
+    {
+      cryptodev_mark_frame_err_status (frame,
+                                      VNET_CRYPTO_OP_STATUS_FAIL_ENGINE_ERR);
+      return -1;
+    }
+
+  cop = cet->cops;
+  fe = frame->elts;
+  bi = frame->buffer_indices;
+  cop[0]->frame = frame;
+  cop[0]->n_elts = n_elts;
+  frame->state = VNET_CRYPTO_OP_STATUS_COMPLETED;
+
+  while (n_elts)
+    {
+      vlib_buffer_t *b = vlib_get_buffer (vm, bi[0]);
+      struct rte_crypto_sym_op *sop = &cop[0]->sop;
+      u16 crypto_offset = fe->crypto_start_offset;
+
+      if (n_elts > 2)
+       {
+         CLIB_PREFETCH (cop[1], CLIB_CACHE_LINE_BYTES * 3, STORE);
+         CLIB_PREFETCH (cop[2], CLIB_CACHE_LINE_BYTES * 3, STORE);
+         CLIB_PREFETCH (&fe[1], CLIB_CACHE_LINE_BYTES, LOAD);
+         CLIB_PREFETCH (&fe[2], CLIB_CACHE_LINE_BYTES, LOAD);
+       }
+      if (last_key_index != fe->key_index)
+       {
+         u8 sess_aad_len;
+         key = pool_elt_at_index (cmt->keys, fe->key_index);
+         sess_aad_len = (u8) key->keys[op_type]->opaque_data;
+         if (PREDICT_FALSE (sess_aad_len != aad_len))
+           {
+             cryptodev_sess_handler (vm, VNET_CRYPTO_KEY_OP_MODIFY,
+                                     fe->key_index, aad_len);
+           }
+         last_key_index = fe->key_index;
+       }
+
+      sop->m_src = rte_mbuf_from_vlib_buffer (b);
+      sop->m_dst = 0;
+      /* mbuf prepend happens in the tx, but vlib_buffer happens in the nodes,
+       * so we have to manually adjust mbuf data_off here so cryptodev can
+       * correctly compute the data pointer. The prepend here will be later
+       * rewritten by tx. */
+      if (PREDICT_FALSE (fe->crypto_start_offset < 0))
+       {
+         rte_pktmbuf_prepend (sop->m_src, -fe->crypto_start_offset);
+         crypto_offset = 0;
+       }
+
+      sop->session = key->keys[op_type];
+      sop->aead.aad.data = cop[0]->aad;
+      sop->aead.aad.phys_addr = cop[0]->op.phys_addr + CRYPTODEV_AAD_OFFSET;
+      sop->aead.data.length = fe->crypto_total_length;
+      sop->aead.data.offset = crypto_offset;
+      sop->aead.digest.data = fe->tag;
+      if (PREDICT_TRUE (!(fe->flags & VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS)))
+       sop->aead.digest.phys_addr = rte_pktmbuf_iova (sop->m_src) +
+         fe->tag - rte_pktmbuf_mtod (sop->m_src, u8 *);
+      else
+       sop->aead.digest.phys_addr =
+         cryptodev_validate_mbuf_chain (vm, sop->m_src, b, fe->tag);
+      clib_memcpy_fast (cop[0]->iv, fe->iv, 12);
+      clib_memcpy_fast (cop[0]->aad, fe->aad, aad_len);
+      cop++;
+      bi++;
+      fe++;
+      n_elts--;
+    }
+
+  n_enqueue = rte_cryptodev_enqueue_burst (cet->cryptodev_id,
+                                          cet->cryptodev_q,
+                                          (struct rte_crypto_op **)
+                                          cet->cops, frame->n_elts);
+  ASSERT (n_enqueue == frame->n_elts);
+  cet->inflight += n_enqueue;
+
+  return 0;
+}
+
+static_always_inline cryptodev_op_t *
+cryptodev_get_ring_head (struct rte_ring * ring)
+{
+  cryptodev_op_t **r = (void *) &ring[1];
+  return r[ring->cons.head & ring->mask];
+}
+
+static_always_inline vnet_crypto_async_frame_t *
+cryptodev_frame_dequeue (vlib_main_t * vm)
+{
+  cryptodev_main_t *cmt = &cryptodev_main;
+  cryptodev_numa_data_t *numa = cmt->per_numa_data + vm->numa_node;
+  cryptodev_engine_thread_t *cet = cmt->per_thread_data + vm->thread_index;
+  cryptodev_op_t *cop0, **cop = cet->cops;
+  vnet_crypto_async_frame_elt_t *fe;
+  vnet_crypto_async_frame_t *frame;
+  u32 n_elts, n_completed_ops = rte_ring_count (cet->ring);
+  u32 ss0 = 0, ss1 = 0, ss2 = 0, ss3 = 0;      /* sum of status */
+
+  if (cet->inflight)
+    {
+      n_elts = clib_min (CRYPTODEV_NB_CRYPTO_OPS - n_completed_ops,
+                        VNET_CRYPTO_FRAME_SIZE);
+      n_elts = rte_cryptodev_dequeue_burst
+       (cet->cryptodev_id, cet->cryptodev_q,
+        (struct rte_crypto_op **) cet->cops, n_elts);
+      cet->inflight -= n_elts;
+      n_completed_ops += n_elts;
+
+      rte_ring_sp_enqueue_burst (cet->ring, (void *) cet->cops, n_elts, NULL);
+    }
+
+  if (PREDICT_FALSE (n_completed_ops == 0))
+    return 0;
+
+  cop0 = cryptodev_get_ring_head (cet->ring);
+  /* not a single frame is finished */
+  if (PREDICT_FALSE (cop0->n_elts > rte_ring_count (cet->ring)))
+    return 0;
+
+  frame = cop0->frame;
+  n_elts = cop0->n_elts;
+  n_elts = rte_ring_sc_dequeue_bulk (cet->ring, (void **) cet->cops,
+                                    n_elts, 0);
+  fe = frame->elts;
+
+  while (n_elts > 4)
+    {
+      ss0 |= fe[0].status = cryptodev_status_conversion[cop[0]->op.status];
+      ss1 |= fe[1].status = cryptodev_status_conversion[cop[1]->op.status];
+      ss2 |= fe[2].status = cryptodev_status_conversion[cop[2]->op.status];
+      ss3 |= fe[3].status = cryptodev_status_conversion[cop[3]->op.status];
+
+      cop += 4;
+      fe += 4;
+      n_elts -= 4;
+    }
+
+  while (n_elts)
+    {
+      ss0 |= fe[0].status = cryptodev_status_conversion[cop[0]->op.status];
+      fe++;
+      cop++;
+      n_elts--;
+    }
+
+  frame->state = (ss0 | ss1 | ss2 | ss3) == VNET_CRYPTO_OP_STATUS_COMPLETED ?
+    VNET_CRYPTO_FRAME_STATE_SUCCESS : VNET_CRYPTO_FRAME_STATE_ELT_ERROR;
+
+  rte_mempool_put_bulk (numa->cop_pool, (void **) cet->cops, frame->n_elts);
+
+  return frame;
+}
+
+/* *INDENT-OFF* */
+
+#define _(a, b, c, d, e, f) \
+static_always_inline int \
+cryptodev_enqueue_##a##_AAD##f##_enc (vlib_main_t * vm, \
+                                     vnet_crypto_async_frame_t * frame) \
+{ \
+  return cryptodev_frame_gcm_enqueue (vm, frame, \
+                                     CRYPTODEV_OP_TYPE_ENCRYPT, f); \
+} \
+static_always_inline int \
+cryptodev_enqueue_##a##_AAD##f##_dec (vlib_main_t * vm, \
+                                     vnet_crypto_async_frame_t * frame) \
+{ \
+  return cryptodev_frame_gcm_enqueue (vm, frame, \
+                                     CRYPTODEV_OP_TYPE_DECRYPT, f); \
+}
+
+foreach_vnet_aead_crypto_conversion
+#undef _
+
+#define _(a, b, c, d) \
+static_always_inline int \
+cryptodev_enqueue_##a##_##c##_TAG##d##_enc (vlib_main_t * vm, \
+                                     vnet_crypto_async_frame_t * frame) \
+{ \
+  return cryptodev_frame_linked_algs_enqueue (vm, frame, \
+                                             CRYPTODEV_OP_TYPE_ENCRYPT, d); \
+} \
+static_always_inline int \
+cryptodev_enqueue_##a##_##c##_TAG##d##_dec (vlib_main_t * vm, \
+                                     vnet_crypto_async_frame_t * frame) \
+{ \
+  return cryptodev_frame_linked_algs_enqueue (vm, frame, \
+                                             CRYPTODEV_OP_TYPE_DECRYPT, d); \
+}
+
+foreach_cryptodev_link_async_alg
+#undef _
+
+typedef enum
+{
+  CRYPTODEV_RESOURCE_ASSIGN_AUTO = 0,
+  CRYPTODEV_RESOURCE_ASSIGN_UPDATE,
+} cryptodev_resource_assign_op_t;
+
+/**
+ *  assign a cryptodev resource to a worker.
+ *  @param cet: the worker thread data
+ *  @param cryptodev_inst_index: if op is "ASSIGN_AUTO" this param is ignored.
+ *  @param op: the assignment method.
+ *  @return: 0 if successfully, negative number otherwise.
+ **/
+static_always_inline int
+cryptodev_assign_resource (cryptodev_engine_thread_t * cet,
+                          u32 cryptodev_inst_index,
+                          cryptodev_resource_assign_op_t op)
+{
+  cryptodev_main_t *cmt = &cryptodev_main;
+  cryptodev_inst_t *cinst = 0;
+  uword idx;
+
+  /* assign resource is only allowed when no inflight op is in the queue */
+  if (cet->inflight)
+    return -EBUSY;
+
+  switch (op)
+    {
+    case CRYPTODEV_RESOURCE_ASSIGN_AUTO:
+      if (clib_bitmap_count_set_bits (cmt->active_cdev_inst_mask) >=
+         vec_len (cmt->cryptodev_inst))
+       return -1;
+
+      clib_spinlock_lock (&cmt->tlock);
+      idx = clib_bitmap_first_clear (cmt->active_cdev_inst_mask);
+      clib_bitmap_set (cmt->active_cdev_inst_mask, idx, 1);
+      cinst = vec_elt_at_index (cmt->cryptodev_inst, idx);
+      cet->cryptodev_id = cinst->dev_id;
+      cet->cryptodev_q = cinst->q_id;
+      clib_spinlock_unlock (&cmt->tlock);
+      break;
+    case CRYPTODEV_RESOURCE_ASSIGN_UPDATE:
+      /* assigning a used cryptodev resource is not allowed */
+      if (clib_bitmap_get (cmt->active_cdev_inst_mask, cryptodev_inst_index)
+         == 1)
+       return -EBUSY;
+      vec_foreach_index (idx, cmt->cryptodev_inst)
+      {
+       cinst = cmt->cryptodev_inst + idx;
+       if (cinst->dev_id == cet->cryptodev_id &&
+           cinst->q_id == cet->cryptodev_q)
+         break;
+      }
+      /* invalid existing worker resource assignment */
+      if (idx == vec_len (cmt->cryptodev_inst))
+       return -EINVAL;
+      clib_spinlock_lock (&cmt->tlock);
+      clib_bitmap_set_no_check (cmt->active_cdev_inst_mask, idx, 0);
+      clib_bitmap_set_no_check (cmt->active_cdev_inst_mask,
+                               cryptodev_inst_index, 1);
+      cinst = cmt->cryptodev_inst + cryptodev_inst_index;
+      cet->cryptodev_id = cinst->dev_id;
+      cet->cryptodev_q = cinst->q_id;
+      clib_spinlock_unlock (&cmt->tlock);
+      break;
+    default:
+      return -EINVAL;
+    }
+  return 0;
+}
+
+static u8 *
+format_cryptodev_inst (u8 * s, va_list * args)
+{
+  cryptodev_main_t *cmt = &cryptodev_main;
+  u32 inst = va_arg (*args, u32);
+  cryptodev_inst_t *cit = cmt->cryptodev_inst + inst;
+  u32 thread_index = 0;
+  struct rte_cryptodev_info info;
+
+  rte_cryptodev_info_get (cit->dev_id, &info);
+  s = format (s, "%-25s%-10u", info.device->name, cit->q_id);
+
+  vec_foreach_index (thread_index, cmt->per_thread_data)
+  {
+    cryptodev_engine_thread_t *cet = cmt->per_thread_data + thread_index;
+    if (vlib_num_workers () > 0 && thread_index == 0)
+      continue;
+
+    if (cet->cryptodev_id == cit->dev_id && cet->cryptodev_q == cit->q_id)
+      {
+       s = format (s, "%u (%v)\n", thread_index,
+                   vlib_worker_threads[thread_index].name);
+       break;
+      }
+  }
+
+  if (thread_index == vec_len (cmt->per_thread_data))
+    s = format (s, "%s\n", "free");
+
+  return s;
+}
+
+static clib_error_t *
+cryptodev_show_assignment_fn (vlib_main_t * vm, unformat_input_t * input,
+                             vlib_cli_command_t * cmd)
+{
+  cryptodev_main_t *cmt = &cryptodev_main;
+  u32 inst;
+
+  vlib_cli_output (vm, "%-5s%-25s%-10s%s\n", "No.", "Name", "Queue-id",
+                  "Assigned-to");
+  if (vec_len (cmt->cryptodev_inst) == 0)
+    {
+      vlib_cli_output (vm, "(nil)\n");
+      return 0;
+    }
+
+  vec_foreach_index (inst, cmt->cryptodev_inst)
+    vlib_cli_output (vm, "%-5u%U", inst, format_cryptodev_inst, inst);
+
+  return 0;
+}
+
+VLIB_CLI_COMMAND (show_cryptodev_assignment, static) = {
+    .path = "show cryptodev assignment",
+    .short_help = "show cryptodev assignment",
+    .function = cryptodev_show_assignment_fn,
+};
+
+static clib_error_t *
+cryptodev_set_assignment_fn (vlib_main_t * vm, unformat_input_t * input,
+                            vlib_cli_command_t * cmd)
+{
+  cryptodev_main_t *cmt = &cryptodev_main;
+  cryptodev_engine_thread_t *cet;
+  unformat_input_t _line_input, *line_input = &_line_input;
+  u32 thread_index, inst_index;
+  u32 thread_present = 0, inst_present = 0;
+  clib_error_t *error = 0;
+  int ret;
+
+  /* Get a line of input. */
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (line_input, "thread %u", &thread_index))
+       thread_present = 1;
+      else if (unformat (line_input, "resource %u", &inst_index))
+       inst_present = 1;
+      else
+       {
+         error = clib_error_return (0, "unknown input `%U'",
+                                    format_unformat_error, line_input);
+         return error;
+       }
+    }
+
+  if (!thread_present || !inst_present)
+    {
+      error = clib_error_return (0, "mandatory argument(s) missing");
+      return error;
+    }
+
+  if (thread_index == 0 && vlib_num_workers () > 0)
+    {
+      error =
+       clib_error_return (0, "assign crypto resource for master thread");
+      return error;
+    }
+
+  if (thread_index > vec_len (cmt->per_thread_data) ||
+      inst_index > vec_len (cmt->cryptodev_inst))
+    {
+      error = clib_error_return (0, "wrong thread id or resource id");
+      return error;
+    }
+
+  cet = cmt->per_thread_data + thread_index;
+  ret = cryptodev_assign_resource (cet, inst_index,
+                                  CRYPTODEV_RESOURCE_ASSIGN_UPDATE);
+  if (ret)
+    {
+      error = clib_error_return (0, "cryptodev_assign_resource returned %i",
+                                ret);
+      return error;
+    }
+
+  return 0;
+}
+
+VLIB_CLI_COMMAND (set_cryptodev_assignment, static) = {
+    .path = "set cryptodev assignment",
+    .short_help = "set cryptodev assignment thread <thread_index> "
+       "resource <inst_index>",
+    .function = cryptodev_set_assignment_fn,
+};
+
+static int
+check_cryptodev_alg_support (u32 dev_id)
+{
+  const struct rte_cryptodev_symmetric_capability *cap;
+  struct rte_cryptodev_sym_capability_idx cap_idx;
+
+#define _(a, b, c, d, e, f) \
+  cap_idx.type = RTE_CRYPTO_SYM_XFORM_##b; \
+  cap_idx.algo.aead = RTE_CRYPTO_##b##_##c; \
+  cap = rte_cryptodev_sym_capability_get (dev_id, &cap_idx); \
+  if (!cap) \
+    return -RTE_CRYPTO_##b##_##c; \
+  else \
+    { \
+      if (cap->aead.digest_size.min > e || cap->aead.digest_size.max < e) \
+       return -RTE_CRYPTO_##b##_##c; \
+      if (cap->aead.aad_size.min > f || cap->aead.aad_size.max < f) \
+       return -RTE_CRYPTO_##b##_##c; \
+      if (cap->aead.iv_size.min > d || cap->aead.iv_size.max < d) \
+       return -RTE_CRYPTO_##b##_##c; \
+    }
+
+  foreach_vnet_aead_crypto_conversion
+#undef _
+
+#define _(a, b, c, d) \
+  cap_idx.type = RTE_CRYPTO_SYM_XFORM_CIPHER; \
+  cap_idx.algo.cipher = RTE_CRYPTO_CIPHER_##b; \
+  cap = rte_cryptodev_sym_capability_get (dev_id, &cap_idx); \
+  if (!cap) \
+    return -RTE_CRYPTO_CIPHER_##b; \
+  cap_idx.type = RTE_CRYPTO_SYM_XFORM_AUTH; \
+  cap_idx.algo.auth = RTE_CRYPTO_AUTH_##c##_HMAC; \
+  cap = rte_cryptodev_sym_capability_get (dev_id, &cap_idx); \
+  if (!cap) \
+    return -RTE_CRYPTO_AUTH_##c;
+
+  foreach_cryptodev_link_async_alg
+#undef _
+    return 0;
+}
+
+static u32
+cryptodev_count_queue (u32 numa)
+{
+  struct rte_cryptodev_info info;
+  u32 n_cryptodev = rte_cryptodev_count ();
+  u32 i, q_count = 0;
+
+  for (i = 0; i < n_cryptodev; i++)
+    {
+      rte_cryptodev_info_get (i, &info);
+      if (rte_cryptodev_socket_id (i) != numa)
+       {
+         clib_warning ("DPDK crypto resource %s is in different numa node "
+             "as %u, ignored", info.device->name, numa);
+         continue;
+       }
+      q_count += info.max_nb_queue_pairs;
+    }
+
+  return q_count;
+}
+
+static int
+cryptodev_configure (vlib_main_t *vm, uint32_t cryptodev_id)
+{
+  struct rte_cryptodev_info info;
+  struct rte_cryptodev *cdev;
+  cryptodev_main_t *cmt = &cryptodev_main;
+  cryptodev_numa_data_t *numa_data = vec_elt_at_index (cmt->per_numa_data,
+                                                      vm->numa_node);
+  u32 i;
+  int ret;
+
+  cdev = rte_cryptodev_pmd_get_dev (cryptodev_id);
+  rte_cryptodev_info_get (cryptodev_id, &info);
+
+  ret = check_cryptodev_alg_support (cryptodev_id);
+  if (ret != 0)
+    return ret;
+
+  /** If the device is already started, we reuse it, otherwise configure
+   *  both the device and queue pair.
+   **/
+  if (!cdev->data->dev_started)
+    {
+      struct rte_cryptodev_config cfg;
+
+      cfg.socket_id = vm->numa_node;
+      cfg.nb_queue_pairs = info.max_nb_queue_pairs;
+
+      rte_cryptodev_configure (cryptodev_id, &cfg);
+
+      for (i = 0; i < info.max_nb_queue_pairs; i++)
+       {
+         struct rte_cryptodev_qp_conf qp_cfg;
+
+         int ret;
+
+         qp_cfg.mp_session = numa_data->sess_pool;
+         qp_cfg.mp_session_private = numa_data->sess_priv_pool;
+         qp_cfg.nb_descriptors = CRYPTODEV_NB_CRYPTO_OPS;
+
+         ret = rte_cryptodev_queue_pair_setup (cryptodev_id, i, &qp_cfg,
+                                               vm->numa_node);
+         if (ret)
+           break;
+       }
+      if (i != info.max_nb_queue_pairs)
+       return -1;
+      /* start the device */
+      rte_cryptodev_start (i);
+    }
+
+  for (i = 0; i < info.max_nb_queue_pairs; i++)
+    {
+      cryptodev_inst_t *cdev_inst;
+      vec_add2(cmt->cryptodev_inst, cdev_inst, 1);
+      cdev_inst->desc = vec_new (char, strlen (info.device->name) + 10);
+      cdev_inst->dev_id = cryptodev_id;
+      cdev_inst->q_id = i;
+
+      snprintf (cdev_inst->desc, strlen (info.device->name) + 9,
+               "%s_q%u", info.device->name, i);
+    }
+
+  return 0;
+}
+
+static int
+cryptodev_create_device (vlib_main_t *vm, u32 n_queues)
+{
+  char name[RTE_CRYPTODEV_NAME_MAX_LEN], args[128];
+  u32 dev_id = 0;
+  int ret;
+
+  /* find an unused name to create the device */
+  while (dev_id < RTE_CRYPTO_MAX_DEVS)
+    {
+      snprintf (name, RTE_CRYPTODEV_NAME_MAX_LEN - 1, "%s%u",
+               RTE_STR (CRYPTODEV_DEF_DRIVE), dev_id);
+      if (rte_cryptodev_get_dev_id (name) < 0)
+       break;
+      dev_id++;
+    }
+
+  if (dev_id == RTE_CRYPTO_MAX_DEVS)
+    return -1;
+
+  snprintf (args, 127, "socket_id=%u,max_nb_queue_pairs=%u",
+           vm->numa_node, n_queues);
+
+  ret = rte_vdev_init(name, args);
+  if (ret < 0)
+    return ret;
+
+  clib_warning ("Created cryptodev device %s (%s)", name, args);
+
+  return 0;
+}
+
+static int
+cryptodev_probe (vlib_main_t *vm, u32 n_workers)
+{
+  u32 n_queues = cryptodev_count_queue (vm->numa_node);
+  u32 i;
+  int ret;
+
+  /* create an AESNI_MB PMD so the service is available */
+  if (n_queues < n_workers)
+    {
+      u32 q_num = max_pow2 (n_workers - n_queues);
+      ret = cryptodev_create_device (vm, q_num);
+      if (ret < 0)
+       return ret;
+    }
+
+  for (i = 0; i < rte_cryptodev_count (); i++)
+    {
+      ret = cryptodev_configure (vm, i);
+      if (ret)
+       return ret;
+    }
+
+  return 0;
+}
+
+static int
+cryptodev_get_session_sz (vlib_main_t *vm, uint32_t n_workers)
+{
+  u32 sess_data_sz = 0, i;
+  int ret;
+
+  if (rte_cryptodev_count () == 0)
+    {
+      clib_warning ("No cryptodev device available, creating...");
+      ret = cryptodev_create_device (vm, max_pow2 (n_workers));
+      if (ret < 0)
+       {
+         clib_warning ("Failed");
+         return ret;
+       }
+    }
+
+  for (i = 0; i < rte_cryptodev_count (); i++)
+    {
+      u32 dev_sess_sz = rte_cryptodev_sym_get_private_session_size (i);
+
+      sess_data_sz = dev_sess_sz > sess_data_sz ? dev_sess_sz : sess_data_sz;
+    }
+
+  return sess_data_sz;
+}
+
+static void
+dpdk_disable_cryptodev_engine (vlib_main_t * vm)
+{
+  cryptodev_main_t *cmt = &cryptodev_main;
+  cryptodev_numa_data_t *numa_data;
+
+  vec_validate (cmt->per_numa_data, vm->numa_node);
+  numa_data = vec_elt_at_index (cmt->per_numa_data, vm->numa_node);
+
+  if (numa_data->sess_pool)
+    rte_mempool_free (numa_data->sess_pool);
+  if (numa_data->sess_priv_pool)
+    rte_mempool_free (numa_data->sess_priv_pool);
+  if (numa_data->cop_pool)
+    rte_mempool_free (numa_data->cop_pool);
+}
+
+static void
+crypto_op_init (struct rte_mempool *mempool,
+               void *_arg __attribute__ ((unused)),
+               void *_obj, unsigned i __attribute__ ((unused)))
+{
+  struct rte_crypto_op *op = _obj;
+
+  op->sess_type = RTE_CRYPTO_OP_WITH_SESSION;
+  op->type = RTE_CRYPTO_OP_TYPE_SYMMETRIC;
+  op->status = RTE_CRYPTO_OP_STATUS_NOT_PROCESSED;
+  op->phys_addr = rte_mempool_virt2iova (_obj);
+  op->mempool = mempool;
+}
+
+
+clib_error_t *
+dpdk_cryptodev_init (vlib_main_t * vm)
+{
+  cryptodev_main_t *cmt = &cryptodev_main;
+  vlib_thread_main_t *tm = vlib_get_thread_main ();
+  cryptodev_engine_thread_t *ptd;
+  cryptodev_numa_data_t *numa_data;
+  struct rte_mempool *mp;
+  u32 skip_master = vlib_num_workers () > 0;
+  u32 n_workers = tm->n_vlib_mains - skip_master;
+  u32 numa = vm->numa_node;
+  i32 sess_sz;
+  u32 n_cop_elts;
+  u32 eidx;
+  u32 i;
+  u8 *name = 0;
+  clib_error_t *error;
+  struct rte_crypto_op_pool_private *priv;
+
+  sess_sz = cryptodev_get_session_sz(vm, n_workers);
+  if (sess_sz < 0)
+    {
+      error = clib_error_return (0, "Not enough cryptodevs");
+      return error;
+    }
+
+  /* A total of 4 times n_worker threads * frame size as crypto ops */
+  n_cop_elts = max_pow2 (n_workers * CRYPTODEV_NB_CRYPTO_OPS);
+
+  vec_validate (cmt->per_numa_data, vm->numa_node);
+  numa_data = vec_elt_at_index (cmt->per_numa_data, numa);
+
+  /* create session pool for the numa node */
+  name = format (0, "vcryptodev_sess_pool_%u", numa);
+  mp = rte_cryptodev_sym_session_pool_create ((char *) name,
+                                             CRYPTODEV_NB_SESSION,
+                                             0, 0, 0, numa);
+  if (!mp)
+    {
+      error = clib_error_return (0, "Not enough memory for mp %s", name);
+      goto err_handling;
+    }
+  vec_free (name);
+
+  numa_data->sess_pool = mp;
+
+  /* create session private pool for the numa node */
+  name = format (0, "cryptodev_sess_pool_%u", numa);
+  mp = rte_mempool_create ((char *) name, CRYPTODEV_NB_SESSION, sess_sz, 0,
+                          0, NULL, NULL, NULL, NULL, numa, 0);
+  if (!mp)
+    {
+      error = clib_error_return (0, "Not enough memory for mp %s", name);
+      vec_free (name);
+      goto err_handling;
+    }
+
+  vec_free (name);
+
+  numa_data->sess_priv_pool = mp;
+
+  /* create cryptodev op pool */
+  name = format (0, "cryptodev_op_pool_%u", numa);
+
+  mp = rte_mempool_create ((char *) name, n_cop_elts,
+                          sizeof (cryptodev_op_t), VLIB_FRAME_SIZE * 2,
+                          sizeof (struct rte_crypto_op_pool_private), NULL,
+                          NULL, crypto_op_init, NULL, numa, 0);
+  if (!mp)
+    {
+      error = clib_error_return (0, "Not enough memory for mp %s", name);
+      vec_free (name);
+      goto err_handling;
+    }
+
+  priv = rte_mempool_get_priv (mp);
+  priv->priv_size = sizeof (struct rte_crypto_op_pool_private);
+  priv->type = RTE_CRYPTO_OP_TYPE_SYMMETRIC;
+  vec_free (name);
+  numa_data->cop_pool = mp;
+
+  /* probe all cryptodev devices and get queue info */
+  if (cryptodev_probe (vm, n_workers) < 0)
+    {
+      error = clib_error_return (0, "Failed to configure cryptodev");
+      goto err_handling;
+    }
+
+  clib_bitmap_vec_validate (cmt->active_cdev_inst_mask, tm->n_vlib_mains);
+  clib_spinlock_init (&cmt->tlock);
+
+  vec_validate_aligned(cmt->per_thread_data, tm->n_vlib_mains - 1,
+                      CLIB_CACHE_LINE_BYTES);
+  for (i = skip_master; i < tm->n_vlib_mains; i++)
+    {
+      ptd = cmt->per_thread_data + i;
+      cryptodev_assign_resource (ptd, 0, CRYPTODEV_RESOURCE_ASSIGN_AUTO);
+      name = format (0, "frames_ring_%u", i);
+      ptd->ring = rte_ring_create((char *) name, CRYPTODEV_NB_CRYPTO_OPS,
+                                  vm->numa_node, RING_F_SP_ENQ|RING_F_SC_DEQ);
+      if (!ptd->ring)
+       {
+         error = clib_error_return (0, "Not enough memory for mp %s", name);
+         vec_free (name);
+         goto err_handling;
+       }
+      vec_validate (ptd->cops, VNET_CRYPTO_FRAME_SIZE - 1);
+      vec_free(name);
+    }
+
+  /* register handler */
+  eidx = vnet_crypto_register_engine (vm, "dpdk_cryptodev", 79,
+                                      "DPDK Cryptodev Engine");
+
+#define _(a, b, c, d, e, f) \
+  vnet_crypto_register_async_handler \
+    (vm, eidx, VNET_CRYPTO_OP_##a##_TAG##e##_AAD##f##_ENC, \
+       cryptodev_enqueue_##a##_AAD##f##_enc, \
+       cryptodev_frame_dequeue); \
+  vnet_crypto_register_async_handler \
+    (vm, eidx, VNET_CRYPTO_OP_##a##_TAG##e##_AAD##f##_DEC, \
+       cryptodev_enqueue_##a##_AAD##f##_dec, \
+       cryptodev_frame_dequeue);
+
+  foreach_vnet_aead_crypto_conversion
+#undef _
+
+#define _(a, b, c, d) \
+  vnet_crypto_register_async_handler \
+    (vm, eidx, VNET_CRYPTO_OP_##a##_##c##_TAG##d##_ENC, \
+       cryptodev_enqueue_##a##_##c##_TAG##d##_enc, \
+       cryptodev_frame_dequeue); \
+  vnet_crypto_register_async_handler \
+    (vm, eidx, VNET_CRYPTO_OP_##a##_##c##_TAG##d##_DEC, \
+       cryptodev_enqueue_##a##_##c##_TAG##d##_dec, \
+       cryptodev_frame_dequeue);
+
+    foreach_cryptodev_link_async_alg
+#undef _
+
+  vnet_crypto_register_key_handler (vm, eidx, cryptodev_key_handler);
+
+  return 0;
+
+err_handling:
+  dpdk_disable_cryptodev_engine (vm);
+
+  return error;
+}
+/* *INDENT-On* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/dpdk/cryptodev/cryptodev.h b/src/plugins/dpdk/cryptodev/cryptodev.h
new file mode 100644 (file)
index 0000000..1f96aed
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2019 Intel and/or its affiliates.
+ * 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.
+ *------------------------------------------------------------------
+ */
+#ifndef included_cryptodev_h
+#define included_cryptodev_h
+
+#include <vnet/crypto/crypto.h>
+
+clib_error_t *
+dpdk_cryptodev_init (vlib_main_t * vm);
+
+#endif
index a9e7393..ae50b7a 100644 (file)
@@ -24,6 +24,7 @@
 #include <vnet/ethernet/ethernet.h>
 #include <dpdk/buffer.h>
 #include <dpdk/device/dpdk.h>
+#include <dpdk/cryptodev/cryptodev.h>
 #include <vlib/pci/pci.h>
 #include <vlib/vmbus/vmbus.h>
 
@@ -1605,6 +1606,10 @@ dpdk_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
 
   error = dpdk_lib_init (dm);
 
+  if (error)
+    clib_error_report (error);
+
+  error = dpdk_cryptodev_init (vm);
   if (error)
     clib_error_report (error);
 
index 8837756..ad638db 100644 (file)
@@ -991,6 +991,24 @@ crypto_disable (void)
   vec_free (dcm->auth_algs);
 }
 
+static clib_error_t *
+dpdk_ipsec_enable_disable (int is_enable)
+{
+  vlib_main_t *vm = vlib_get_main ();
+  vlib_thread_main_t *tm = vlib_get_thread_main ();
+  vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "dpdk-crypto-input");
+  u32 skip_master = vlib_num_workers () > 0;
+  u32 n_mains = tm->n_vlib_mains;
+  u32 i;
+
+  ASSERT (node);
+  for (i = skip_master; i < n_mains; i++)
+    vlib_node_set_state (vlib_mains[i], node->index, is_enable != 0 ?
+                        VLIB_NODE_STATE_POLLING : VLIB_NODE_STATE_DISABLED);
+
+  return 0;
+}
+
 static uword
 dpdk_ipsec_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
                    vlib_frame_t * f)
@@ -1000,7 +1018,7 @@ dpdk_ipsec_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
   vlib_thread_main_t *tm = vlib_get_thread_main ();
   crypto_worker_main_t *cwm;
   clib_error_t *error = NULL;
-  u32 i, skip_master, n_mains;
+  u32 skip_master, n_mains;
 
   n_mains = tm->n_vlib_mains;
   skip_master = vlib_num_workers () > 0;
@@ -1055,14 +1073,14 @@ dpdk_ipsec_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
                                        "dpdk-esp6-decrypt",
                                        "dpdk-esp6-decrypt",
                                        dpdk_ipsec_check_support,
-                                       add_del_sa_session);
-  int rv = ipsec_select_esp_backend (im, idx);
-  ASSERT (rv == 0);
-
-  vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "dpdk-crypto-input");
-  ASSERT (node);
-  for (i = skip_master; i < n_mains; i++)
-    vlib_node_set_state (vlib_mains[i], node->index, VLIB_NODE_STATE_POLLING);
+                                       add_del_sa_session,
+                                       dpdk_ipsec_enable_disable);
+  int rv;
+  if (im->esp_current_backend == ~0)
+    {
+      rv = ipsec_select_esp_backend (im, idx);
+      ASSERT (rv == 0);
+    }
   return 0;
 }
 
index d14d5ab..eb59ab0 100644 (file)
@@ -576,12 +576,15 @@ list(APPEND VNET_SOURCES
   crypto/cli.c
   crypto/crypto.c
   crypto/format.c
+  crypto/node.c
 )
 
 list(APPEND VNET_HEADERS
   crypto/crypto.h
 )
 
+list(APPEND VNET_MULTIARCH_SOURCES crypto/node.c)
+
 ##############################################################################
 # Layer 3 protocol: IPSec
 ##############################################################################
index c338369..f677893 100644 (file)
@@ -58,26 +58,45 @@ format_vnet_crypto_engine_candidates (u8 * s, va_list * args)
 {
   vnet_crypto_engine_t *e;
   vnet_crypto_main_t *cm = &crypto_main;
-
-  vnet_crypto_op_id_t id = va_arg (*args, vnet_crypto_op_id_t);
+  u32 id = va_arg (*args, u32);
   u32 ei = va_arg (*args, u32);
   int is_chained = va_arg (*args, int);
+  int is_async = va_arg (*args, int);
 
-  vec_foreach (e, cm->engines)
+  if (is_async)
     {
-      void * h = is_chained ? (void *) e->chained_ops_handlers[id]
-        : (void *) e->ops_handlers[id];
-
-      if (h)
-        {
-          s = format (s, "%U", format_vnet_crypto_engine, e - cm->engines);
-          if (ei == e - cm->engines)
-            s = format (s, "%c ", '*');
-          else
-            s = format (s, " ");
-        }
+      vec_foreach (e, cm->engines)
+       {
+         if (e->enqueue_handlers[id] && e->dequeue_handlers[id])
+           {
+             s = format (s, "%U", format_vnet_crypto_engine, e - cm->engines);
+             if (ei == e - cm->engines)
+               s = format (s, "%c ", '*');
+             else
+               s = format (s, " ");
+           }
+       }
+
+      return s;
+    }
+  else
+    {
+      vec_foreach (e, cm->engines)
+       {
+         void * h = is_chained ? (void *) e->chained_ops_handlers[id]
+           : (void *) e->ops_handlers[id];
+
+         if (h)
+           {
+             s = format (s, "%U", format_vnet_crypto_engine, e - cm->engines);
+             if (ei == e - cm->engines)
+               s = format (s, "%c ", '*');
+             else
+               s = format (s, " ");
+           }
+       }
+      return s;
     }
-  return s;
 }
 
 static u8 *
@@ -103,9 +122,9 @@ format_vnet_crypto_handlers (u8 * s, va_list * args)
       s = format (s, "%-16U", format_vnet_crypto_op_type, od->type);
 
       s = format (s, "%-28U", format_vnet_crypto_engine_candidates, id,
-          od->active_engine_index_simple, 0);
+          od->active_engine_index_simple, 0, 0);
       s = format (s, "%U", format_vnet_crypto_engine_candidates, id,
-          od->active_engine_index_chained, 1);
+          od->active_engine_index_chained, 1, 0);
       first = 0;
     }
   return s;
@@ -232,6 +251,148 @@ VLIB_CLI_COMMAND (set_crypto_handler_command, static) =
 };
 /* *INDENT-ON* */
 
+static u8 *
+format_vnet_crypto_async_handlers (u8 * s, va_list * args)
+{
+  vnet_crypto_async_alg_t alg = va_arg (*args, vnet_crypto_async_alg_t);
+  vnet_crypto_main_t *cm = &crypto_main;
+  vnet_crypto_async_alg_data_t *d = vec_elt_at_index (cm->async_algs, alg);
+  u32 indent = format_get_indent (s);
+  int i, first = 1;
+
+  for (i = 0; i < VNET_CRYPTO_ASYNC_OP_N_TYPES; i++)
+    {
+      vnet_crypto_async_op_data_t *od;
+      vnet_crypto_async_op_id_t id = d->op_by_type[i];
+
+      if (id == 0)
+       continue;
+
+      od = cm->async_opt_data + id;
+      if (first == 0)
+       s = format (s, "\n%U", format_white_space, indent);
+      s = format (s, "%-16U", format_vnet_crypto_async_op_type, od->type);
+
+      s = format (s, "%U", format_vnet_crypto_engine_candidates, id,
+                 od->active_engine_index_async, 0, 1);
+      first = 0;
+    }
+  return s;
+}
+
+static clib_error_t *
+show_crypto_async_handlers_command_fn (vlib_main_t * vm,
+                                      unformat_input_t * input,
+                                      vlib_cli_command_t * cmd)
+{
+  unformat_input_t _line_input, *line_input = &_line_input;
+  int i;
+
+  if (unformat_user (input, unformat_line_input, line_input))
+    unformat_free (line_input);
+
+  vlib_cli_output (vm, "%-28s%-16s%s", "Algo", "Type", "Handler");
+
+  for (i = 0; i < VNET_CRYPTO_N_ASYNC_ALGS; i++)
+    vlib_cli_output (vm, "%-28U%U", format_vnet_crypto_async_alg, i,
+                    format_vnet_crypto_async_handlers, i);
+
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (show_crypto_async_handlers_command, static) =
+{
+  .path = "show crypto async handlers",
+  .short_help = "show crypto async handlers",
+  .function = show_crypto_async_handlers_command_fn,
+};
+/* *INDENT-ON* */
+
+
+static clib_error_t *
+set_crypto_async_handler_command_fn (vlib_main_t * vm,
+                                    unformat_input_t * input,
+                                    vlib_cli_command_t * cmd)
+{
+  unformat_input_t _line_input, *line_input = &_line_input;
+  vnet_crypto_main_t *cm = &crypto_main;
+  int rc = 0;
+  char **args = 0, *s, **arg, *engine = 0;
+  int all = 0;
+  clib_error_t *error = 0;
+
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (line_input, "all"))
+       all = 1;
+      else if (unformat (line_input, "%s", &s))
+       vec_add1 (args, s);
+      else
+       {
+         error = clib_error_return (0, "invalid params");
+         goto done;
+       }
+    }
+
+  if ((vec_len (args) < 2 && !all) || (vec_len (args) == 0 && all))
+    {
+      error = clib_error_return (0, "missing cipher or engine!");
+      goto done;
+    }
+
+  engine = vec_elt_at_index (args, vec_len (args) - 1)[0];
+  vec_del1 (args, vec_len (args) - 1);
+
+  if (all)
+    {
+      char *key;
+      u8 *value;
+
+      /* *INDENT-OFF* */
+      hash_foreach_mem (key, value, cm->async_alg_index_by_name,
+      ({
+        (void) value;
+        rc += vnet_crypto_set_async_handler2 (key, engine);
+      }));
+      /* *INDENT-ON* */
+
+      if (rc)
+       vlib_cli_output (vm, "failed to set crypto engine!");
+    }
+  else
+    {
+      vec_foreach (arg, args)
+      {
+       rc = vnet_crypto_set_async_handler2 (arg[0], engine);
+       if (rc)
+         {
+           vlib_cli_output (vm, "failed to set engine %s for %s!",
+                            engine, arg[0]);
+         }
+      }
+    }
+
+done:
+  vec_free (engine);
+  vec_foreach (arg, args) vec_free (arg[0]);
+  vec_free (args);
+  unformat_free (line_input);
+  return error;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (set_crypto_async_handler_command, static) =
+{
+  .path = "set crypto async handler",
+  .short_help = "set crypto async handler type [type2 type3 ...] engine",
+  .function = set_crypto_async_handler_command_fn,
+};
+/* *INDENT-ON* */
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index 1caff71..288e227 100644 (file)
@@ -61,7 +61,6 @@ vnet_crypto_process_ops_call_handler (vlib_main_t * vm,
   return rv;
 }
 
-
 static_always_inline u32
 vnet_crypto_process_ops_inline (vlib_main_t * vm, vnet_crypto_op_t ops[],
                                vnet_crypto_op_chunk_t * chunks, u32 n_ops)
@@ -266,6 +265,44 @@ vnet_crypto_register_ops_handlers (vlib_main_t * vm, u32 engine_index,
   vnet_crypto_register_ops_handler_inline (vm, engine_index, opt, fn, cfn);
 }
 
+void
+vnet_crypto_register_async_handler (vlib_main_t * vm, u32 engine_index,
+                                   vnet_crypto_async_op_id_t opt,
+                                   vnet_crypto_frame_enqueue_t * enqueue_hdl,
+                                   vnet_crypto_frame_dequeue_t * dequeue_hdl)
+{
+  vnet_crypto_main_t *cm = &crypto_main;
+  vnet_crypto_engine_t *ae, *e = vec_elt_at_index (cm->engines, engine_index);
+  vnet_crypto_async_op_data_t *otd = cm->async_opt_data + opt;
+  vec_validate_aligned (cm->enqueue_handlers, VNET_CRYPTO_ASYNC_OP_N_IDS - 1,
+                       CLIB_CACHE_LINE_BYTES);
+  vec_validate_aligned (cm->dequeue_handlers, VNET_CRYPTO_ASYNC_OP_N_IDS - 1,
+                       CLIB_CACHE_LINE_BYTES);
+
+  /* both enqueue hdl and dequeue hdl should present */
+  if (!enqueue_hdl && !dequeue_hdl)
+    return;
+
+  e->enqueue_handlers[opt] = enqueue_hdl;
+  e->dequeue_handlers[opt] = dequeue_hdl;
+  if (otd->active_engine_index_async == ~0)
+    {
+      otd->active_engine_index_async = engine_index;
+      cm->enqueue_handlers[opt] = enqueue_hdl;
+      cm->dequeue_handlers[opt] = dequeue_hdl;
+    }
+
+  ae = vec_elt_at_index (cm->engines, otd->active_engine_index_async);
+  if (ae->priority < e->priority)
+    {
+      otd->active_engine_index_async = engine_index;
+      cm->enqueue_handlers[opt] = enqueue_hdl;
+      cm->dequeue_handlers[opt] = dequeue_hdl;
+    }
+
+  return;
+}
+
 void
 vnet_crypto_register_key_handler (vlib_main_t * vm, u32 engine_index,
                                  vnet_crypto_key_handler_t * key_handler)
@@ -318,10 +355,10 @@ vnet_crypto_key_add (vlib_main_t * vm, vnet_crypto_alg_t alg, u8 * data,
 
   pool_get_zero (cm->keys, key);
   index = key - cm->keys;
+  key->type = VNET_CRYPTO_KEY_TYPE_DATA;
   key->alg = alg;
   vec_validate_aligned (key->data, length - 1, CLIB_CACHE_LINE_BYTES);
   clib_memcpy (key->data, data, length);
-
   /* *INDENT-OFF* */
   vec_foreach (engine, cm->engines)
     if (engine->key_op_handler)
@@ -343,11 +380,218 @@ vnet_crypto_key_del (vlib_main_t * vm, vnet_crypto_key_index_t index)
       engine->key_op_handler (vm, VNET_CRYPTO_KEY_OP_DEL, index);
   /* *INDENT-ON* */
 
-  clib_memset (key->data, 0, vec_len (key->data));
-  vec_free (key->data);
+  if (key->type == VNET_CRYPTO_KEY_TYPE_DATA)
+    {
+      clib_memset (key->data, 0, vec_len (key->data));
+      vec_free (key->data);
+    }
+  else if (key->type == VNET_CRYPTO_KEY_TYPE_LINK)
+    {
+      key->index_crypto = key->index_integ = 0;
+    }
+
   pool_put (cm->keys, key);
 }
 
+vnet_crypto_async_alg_t
+vnet_crypto_link_algs (vnet_crypto_alg_t crypto_alg,
+                      vnet_crypto_alg_t integ_alg)
+{
+#define _(c, h, s, k ,d) \
+  if (crypto_alg == VNET_CRYPTO_ALG_##c && \
+      integ_alg == VNET_CRYPTO_ALG_HMAC_##h) \
+    return VNET_CRYPTO_ALG_##c##_##h##_TAG##d;
+  foreach_crypto_link_async_alg
+#undef _
+    return ~0;
+}
+
+u32
+vnet_crypto_key_add_linked (vlib_main_t * vm,
+                           vnet_crypto_key_index_t index_crypto,
+                           vnet_crypto_key_index_t index_integ)
+{
+  u32 index;
+  vnet_crypto_main_t *cm = &crypto_main;
+  vnet_crypto_engine_t *engine;
+  vnet_crypto_key_t *key_crypto, *key_integ, *key;
+  vnet_crypto_async_alg_t linked_alg;
+
+  key_crypto = pool_elt_at_index (cm->keys, index_crypto);
+  key_integ = pool_elt_at_index (cm->keys, index_integ);
+
+  if (!key_crypto || !key_integ)
+    return ~0;
+
+  linked_alg = vnet_crypto_link_algs (key_crypto->alg, key_integ->alg);
+  if (linked_alg == ~0)
+    return ~0;
+
+  pool_get_zero (cm->keys, key);
+  index = key - cm->keys;
+  key->type = VNET_CRYPTO_KEY_TYPE_LINK;
+  key->index_crypto = index_crypto;
+  key->index_integ = index_integ;
+  key->async_alg = linked_alg;
+
+  /* *INDENT-OFF* */
+  vec_foreach (engine, cm->engines)
+    if (engine->key_op_handler)
+      engine->key_op_handler (vm, VNET_CRYPTO_KEY_OP_ADD, index);
+  /* *INDENT-ON* */
+
+  return index;
+}
+
+clib_error_t *
+crypto_dispatch_enable_disable (int is_enable)
+{
+  vlib_main_t *vm = vlib_get_main ();
+  vlib_thread_main_t *tm = vlib_get_thread_main ();
+  vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "crypto-dispatch");
+  vnet_crypto_main_t *cm = &crypto_main;
+  u32 skip_master = vlib_num_workers () > 0, i;
+  u32 state_change = 0;
+  vlib_node_state_t state;
+
+  if (is_enable && cm->async_refcnt > 0)
+    {
+      state_change = 1;
+      state = VLIB_NODE_STATE_POLLING;
+    }
+
+  if (!is_enable && cm->async_refcnt == 0)
+    {
+      state_change = 1;
+      state = VLIB_NODE_STATE_DISABLED;
+    }
+
+  if (state_change)
+    for (i = skip_master; i < tm->n_vlib_mains; i++)
+      vlib_node_set_state (vlib_mains[i], node->index, state);
+
+  return 0;
+}
+
+static_always_inline void
+crypto_set_active_async_engine (vnet_crypto_async_op_data_t * od,
+                               vnet_crypto_async_op_id_t id, u32 ei)
+{
+  vnet_crypto_main_t *cm = &crypto_main;
+  vnet_crypto_engine_t *ce = vec_elt_at_index (cm->engines, ei);
+
+  if (ce->enqueue_handlers[id] && ce->dequeue_handlers[id])
+    {
+      od->active_engine_index_async = ei;
+      cm->enqueue_handlers[id] = ce->enqueue_handlers[id];
+      cm->dequeue_handlers[id] = ce->dequeue_handlers[id];
+    }
+}
+
+int
+vnet_crypto_set_async_handler2 (char *alg_name, char *engine)
+{
+  uword *p;
+  vnet_crypto_main_t *cm = &crypto_main;
+  vnet_crypto_async_alg_data_t *ad;
+  int i;
+
+  p = hash_get_mem (cm->async_alg_index_by_name, alg_name);
+  if (!p)
+    return -1;
+
+  ad = vec_elt_at_index (cm->async_algs, p[0]);
+
+  p = hash_get_mem (cm->engine_index_by_name, engine);
+  if (!p)
+    return -1;
+
+  for (i = 0; i < VNET_CRYPTO_ASYNC_OP_N_TYPES; i++)
+    {
+      vnet_crypto_async_op_data_t *od;
+      vnet_crypto_async_op_id_t id = ad->op_by_type[i];
+      if (id == 0)
+       continue;
+
+      od = cm->async_opt_data + id;
+      crypto_set_active_async_engine (od, id, p[0]);
+    }
+
+  return 0;
+}
+
+u32
+vnet_crypto_register_post_node (vlib_main_t * vm, char *post_node_name)
+{
+  vnet_crypto_main_t *cm = &crypto_main;
+  vnet_crypto_async_next_node_t *nn = 0;
+  vlib_node_t *cc, *pn;
+  uword index = vec_len (cm->next_nodes);
+
+  pn = vlib_get_node_by_name (vm, (u8 *) post_node_name);
+  if (!pn)
+    return ~0;
+
+  /* *INDENT-OFF* */
+  vec_foreach (cm->next_nodes, nn)
+  {
+    if (nn->node_idx == pn->index)
+      return nn->next_idx;
+  }
+  /* *INDENT-ON* */
+
+  vec_validate (cm->next_nodes, index);
+  nn = vec_elt_at_index (cm->next_nodes, index);
+
+  cc = vlib_get_node_by_name (vm, (u8 *) "crypto-dispatch");
+  nn->next_idx = vlib_node_add_named_next (vm, cc->index, post_node_name);
+  nn->node_idx = pn->index;
+
+  return nn->next_idx;
+}
+
+void
+vnet_crypto_request_async_mode (int is_enable)
+{
+  vlib_main_t *vm = vlib_get_main ();
+  vlib_thread_main_t *tm = vlib_get_thread_main ();
+  vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "crypto-dispatch");
+  vnet_crypto_main_t *cm = &crypto_main;
+  u32 skip_master = vlib_num_workers () > 0, i;
+  u32 state_change = 0;
+  vlib_node_state_t state;
+
+  if (is_enable && cm->async_refcnt == 0)
+    {
+      state_change = 1;
+      state = VLIB_NODE_STATE_POLLING;
+    }
+
+  if (!is_enable && cm->async_refcnt == 1)
+    {
+      state_change = 1;
+      state = VLIB_NODE_STATE_DISABLED;
+    }
+
+  if (state_change)
+    for (i = skip_master; i < tm->n_vlib_mains; i++)
+      vlib_node_set_state (vlib_mains[i], node->index, state);
+
+  if (is_enable)
+    cm->async_refcnt += 1;
+  else if (cm->async_refcnt > 0)
+    cm->async_refcnt -= 1;
+}
+
+int
+vnet_crypto_is_set_async_handler (vnet_crypto_async_op_id_t op)
+{
+  vnet_crypto_main_t *cm = &crypto_main;
+
+  return (op < vec_len (cm->enqueue_handlers) &&
+         NULL != cm->enqueue_handlers[op]);
+}
+
 static void
 vnet_crypto_init_cipher_data (vnet_crypto_alg_t alg, vnet_crypto_op_id_t eid,
                              vnet_crypto_op_id_t did, char *name, u8 is_aead)
@@ -392,16 +636,44 @@ vnet_crypto_init_hmac_data (vnet_crypto_alg_t alg,
   hash_set_mem (cm->alg_index_by_name, name, alg);
 }
 
+static void
+vnet_crypto_init_async_data (vnet_crypto_async_alg_t alg,
+                            vnet_crypto_async_op_id_t eid,
+                            vnet_crypto_async_op_id_t did, char *name)
+{
+  vnet_crypto_main_t *cm = &crypto_main;
+
+  cm->async_algs[alg].name = name;
+  cm->async_algs[alg].op_by_type[VNET_CRYPTO_ASYNC_OP_TYPE_ENCRYPT] = eid;
+  cm->async_algs[alg].op_by_type[VNET_CRYPTO_ASYNC_OP_TYPE_DECRYPT] = did;
+  cm->async_opt_data[eid].type = VNET_CRYPTO_ASYNC_OP_TYPE_ENCRYPT;
+  cm->async_opt_data[eid].alg = alg;
+  cm->async_opt_data[eid].active_engine_index_async = ~0;
+  cm->async_opt_data[eid].active_engine_index_async = ~0;
+  cm->async_opt_data[did].type = VNET_CRYPTO_ASYNC_OP_TYPE_DECRYPT;
+  cm->async_opt_data[did].alg = alg;
+  cm->async_opt_data[did].active_engine_index_async = ~0;
+  cm->async_opt_data[did].active_engine_index_async = ~0;
+  hash_set_mem (cm->async_alg_index_by_name, name, alg);
+}
+
 clib_error_t *
 vnet_crypto_init (vlib_main_t * vm)
 {
   vnet_crypto_main_t *cm = &crypto_main;
   vlib_thread_main_t *tm = vlib_get_thread_main ();
+  vnet_crypto_thread_t *ct = 0;
   cm->engine_index_by_name = hash_create_string ( /* size */ 0,
                                                 sizeof (uword));
   cm->alg_index_by_name = hash_create_string (0, sizeof (uword));
+  cm->async_alg_index_by_name = hash_create_string (0, sizeof (uword));
   vec_validate_aligned (cm->threads, tm->n_vlib_mains, CLIB_CACHE_LINE_BYTES);
+  vec_foreach (ct, cm->threads)
+    pool_alloc_aligned (ct->frame_pool, 256, CLIB_CACHE_LINE_BYTES);
   vec_validate (cm->algs, VNET_CRYPTO_N_ALGS);
+  vec_validate (cm->async_algs, VNET_CRYPTO_N_ASYNC_ALGS);
+  clib_bitmap_validate (cm->async_active_ids, VNET_CRYPTO_ASYNC_OP_N_IDS - 1);
+
 #define _(n, s, l) \
   vnet_crypto_init_cipher_data (VNET_CRYPTO_ALG_##n, \
                                VNET_CRYPTO_OP_##n##_ENC, \
@@ -419,7 +691,21 @@ vnet_crypto_init (vlib_main_t * vm)
                              VNET_CRYPTO_OP_##n##_HMAC, "hmac-" s);
   foreach_crypto_hmac_alg;
 #undef _
-  return 0;
+#define _(n, s, k, t, a) \
+  vnet_crypto_init_async_data (VNET_CRYPTO_ALG_##n##_TAG##t##_AAD##a, \
+                              VNET_CRYPTO_OP_##n##_TAG##t##_AAD##a##_ENC, \
+                              VNET_CRYPTO_OP_##n##_TAG##t##_AAD##a##_DEC, \
+                              s);
+  foreach_crypto_aead_async_alg
+#undef _
+#define _(c, h, s, k ,d) \
+  vnet_crypto_init_async_data (VNET_CRYPTO_ALG_##c##_##h##_TAG##d, \
+                              VNET_CRYPTO_OP_##c##_##h##_TAG##d##_ENC, \
+                              VNET_CRYPTO_OP_##c##_##h##_TAG##d##_DEC, \
+                              s);
+    foreach_crypto_link_async_alg
+#undef _
+    return 0;
 }
 
 VLIB_INIT_FUNCTION (vnet_crypto_init);
index f89ecf9..b0a83e0 100644 (file)
 #ifndef included_vnet_crypto_crypto_h
 #define included_vnet_crypto_crypto_h
 
-#define VNET_CRYPTO_RING_SIZE 512
-
 #include <vlib/vlib.h>
 
+#define VNET_CRYPTO_FRAME_SIZE 32
+
 /* CRYPTO_ID, PRETTY_NAME, KEY_LENGTH_IN_BYTES */
 #define foreach_crypto_cipher_alg \
   _(DES_CBC,     "des-cbc", 7) \
@@ -45,7 +45,6 @@
   _(SHA384, "sha-384")  \
   _(SHA512, "sha-512")
 
-
 #define foreach_crypto_op_type \
   _(ENCRYPT, "encrypt") \
   _(DECRYPT, "decrypt") \
@@ -62,10 +61,46 @@ typedef enum
 } vnet_crypto_op_type_t;
 
 #define foreach_crypto_op_status \
+  _(IDLE, "idle") \
   _(PENDING, "pending") \
+  _(WORK_IN_PROGRESS, "work-in-progress") \
   _(COMPLETED, "completed") \
   _(FAIL_NO_HANDLER, "no-handler") \
-  _(FAIL_BAD_HMAC, "bad-hmac")
+  _(FAIL_BAD_HMAC, "bad-hmac") \
+  _(FAIL_ENGINE_ERR, "engine-error")
+
+/** async crypto **/
+
+/* CRYPTO_ID, PRETTY_NAME, KEY_LENGTH_IN_BYTES, TAG_LEN, AAD_LEN */
+#define foreach_crypto_aead_async_alg \
+  _(AES_128_GCM, "aes-128-gcm-aad8", 16, 16, 8) \
+  _(AES_128_GCM, "aes-128-gcm-aad12", 16, 16, 12) \
+  _(AES_192_GCM, "aes-192-gcm-aad8", 24, 16, 8) \
+  _(AES_192_GCM, "aes-192-gcm-aad12", 24, 16, 12) \
+  _(AES_256_GCM, "aes-256-gcm-aad8", 32, 16, 8) \
+  _(AES_256_GCM, "aes-256-gcm-aad12", 32, 16, 12)
+
+/* CRYPTO_ID, INTEG_ID, PRETTY_NAME, KEY_LENGTH_IN_BYTES, DIGEST_LEN */
+#define foreach_crypto_link_async_alg \
+  _ (AES_128_CBC, SHA1, "aes-128-cbc-hmac-sha-1", 16, 12) \
+  _ (AES_192_CBC, SHA1, "aes-192-cbc-hmac-sha-1", 24, 12) \
+  _ (AES_256_CBC, SHA1, "aes-256-cbc-hmac-sha-1", 32, 12) \
+  _ (AES_128_CBC, SHA224, "aes-128-cbc-hmac-sha-224", 16, 14) \
+  _ (AES_192_CBC, SHA224, "aes-192-cbc-hmac-sha-224", 24, 14) \
+  _ (AES_256_CBC, SHA224, "aes-256-cbc-hmac-sha-224", 32, 14) \
+  _ (AES_128_CBC, SHA256, "aes-128-cbc-hmac-sha-256", 16, 16) \
+  _ (AES_192_CBC, SHA256, "aes-192-cbc-hmac-sha-256", 24, 16) \
+  _ (AES_256_CBC, SHA256, "aes-256-cbc-hmac-sha-256", 32, 16) \
+  _ (AES_128_CBC, SHA384, "aes-128-cbc-hmac-sha-384", 16, 24) \
+  _ (AES_192_CBC, SHA384, "aes-192-cbc-hmac-sha-384", 24, 24) \
+  _ (AES_256_CBC, SHA384, "aes-256-cbc-hmac-sha-384", 32, 24) \
+  _ (AES_128_CBC, SHA512, "aes-128-cbc-hmac-sha-512", 16, 32) \
+  _ (AES_192_CBC, SHA512, "aes-192-cbc-hmac-sha-512", 24, 32) \
+  _ (AES_256_CBC, SHA512, "aes-256-cbc-hmac-sha-512", 32, 32)
+
+#define foreach_crypto_async_op_type \
+  _(ENCRYPT, "async-encrypt") \
+  _(DECRYPT, "async-decrypt")
 
 typedef enum
 {
@@ -96,10 +131,63 @@ typedef enum
   VNET_CRYPTO_N_ALGS,
 } vnet_crypto_alg_t;
 
+typedef enum
+{
+#define _(n, s) VNET_CRYPTO_ASYNC_OP_TYPE_##n,
+  foreach_crypto_async_op_type
+#undef _
+    VNET_CRYPTO_ASYNC_OP_N_TYPES,
+} vnet_crypto_async_op_type_t;
+
+typedef enum
+{
+  VNET_CRYPTO_ASYNC_ALG_NONE = 0,
+#define _(n, s, k, t, a) \
+  VNET_CRYPTO_ALG_##n##_TAG##t##_AAD##a,
+  foreach_crypto_aead_async_alg
+#undef _
+#define _(c, h, s, k ,d) \
+  VNET_CRYPTO_ALG_##c##_##h##_TAG##d,
+  foreach_crypto_link_async_alg
+#undef _
+  VNET_CRYPTO_N_ASYNC_ALGS,
+} vnet_crypto_async_alg_t;
+
+typedef enum
+{
+  VNET_CRYPTO_ASYNC_OP_NONE = 0,
+#define _(n, s, k, t, a) \
+  VNET_CRYPTO_OP_##n##_TAG##t##_AAD##a##_ENC, \
+  VNET_CRYPTO_OP_##n##_TAG##t##_AAD##a##_DEC,
+  foreach_crypto_aead_async_alg
+#undef _
+#define _(c, h, s, k ,d) \
+  VNET_CRYPTO_OP_##c##_##h##_TAG##d##_ENC, \
+  VNET_CRYPTO_OP_##c##_##h##_TAG##d##_DEC,
+  foreach_crypto_link_async_alg
+#undef _
+  VNET_CRYPTO_ASYNC_OP_N_IDS,
+} vnet_crypto_async_op_id_t;
+
 typedef struct
 {
-  u8 *data;
-  vnet_crypto_alg_t alg:8;
+  union
+  {
+    struct
+    {
+      u8 *data;
+      vnet_crypto_alg_t alg:8;
+    };
+    struct
+    {
+      u32 index_crypto;
+      u32 index_integ;
+      vnet_crypto_async_alg_t async_alg:8;
+    };
+  };
+#define VNET_CRYPTO_KEY_TYPE_DATA 0
+#define VNET_CRYPTO_KEY_TYPE_LINK 1
+  u8 type;
 } vnet_crypto_key_t;
 
 typedef enum
@@ -116,6 +204,7 @@ typedef enum
 } vnet_crypto_op_id_t;
 /* *INDENT-ON* */
 
+
 typedef enum
 {
   CRYPTO_OP_SIMPLE,
@@ -194,10 +283,60 @@ typedef struct
   u32 active_engine_index_chained;
 } vnet_crypto_op_data_t;
 
+typedef struct
+{
+  vnet_crypto_async_op_type_t type;
+  vnet_crypto_async_alg_t alg;
+  u32 active_engine_index_async;
+} vnet_crypto_async_op_data_t;
+
+typedef struct
+{
+  char *name;
+  vnet_crypto_async_op_id_t op_by_type[VNET_CRYPTO_ASYNC_OP_N_TYPES];
+} vnet_crypto_async_alg_data_t;
+
+typedef struct
+{
+  vnet_crypto_op_status_t status:8;
+  u32 key_index;
+  i16 crypto_start_offset;     /* first buffer offset */
+  i16 integ_start_offset;
+  u32 crypto_total_length;
+  /* adj total_length for integ, e.g.4 bytes for IPSec ESN */
+  u16 integ_length_adj;
+  u8 *iv;
+  union
+  {
+    u8 *digest;
+    u8 *tag;
+  };
+  u8 *aad;
+  u8 flags; /**< share same VNET_CRYPTO_OP_FLAG_* values */
+} vnet_crypto_async_frame_elt_t;
+
 typedef struct
 {
   CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
-  clib_bitmap_t *act_queues;
+#define VNET_CRYPTO_FRAME_STATE_NOT_PROCESSED 0
+#define VNET_CRYPTO_FRAME_STATE_WORK_IN_PROGRESS 1
+#define VNET_CRYPTO_FRAME_STATE_SUCCESS 2
+#define VNET_CRYPTO_FRAME_STATE_ELT_ERROR 3
+  u8 state;
+  vnet_crypto_async_op_id_t op:8;
+  u16 n_elts;
+  vnet_crypto_async_frame_elt_t elts[VNET_CRYPTO_FRAME_SIZE];
+  u32 buffer_indices[VNET_CRYPTO_FRAME_SIZE];
+  u16 next_node_index[VNET_CRYPTO_FRAME_SIZE];
+} vnet_crypto_async_frame_t;
+
+typedef struct
+{
+  CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
+  vnet_crypto_async_frame_t *frames[VNET_CRYPTO_ASYNC_OP_N_IDS];
+  vnet_crypto_async_frame_t *frame_pool;
+  u32 *buffer_indice;
+  u16 *nexts;
 } vnet_crypto_thread_t;
 
 typedef u32 vnet_crypto_key_index_t;
@@ -214,6 +353,12 @@ typedef void (vnet_crypto_key_handler_t) (vlib_main_t * vm,
                                          vnet_crypto_key_op_t kop,
                                          vnet_crypto_key_index_t idx);
 
+/** async crypto function handlers **/
+typedef int (vnet_crypto_frame_enqueue_t) (vlib_main_t * vm,
+                                          vnet_crypto_async_frame_t * frame);
+typedef vnet_crypto_async_frame_t *
+  (vnet_crypto_frame_dequeue_t) (vlib_main_t * vm);
+
 u32 vnet_crypto_register_engine (vlib_main_t * vm, char *name, int prio,
                                 char *desc);
 
@@ -226,6 +371,7 @@ void vnet_crypto_register_chained_ops_handler (vlib_main_t * vm,
                                               vnet_crypto_op_id_t opt,
                                               vnet_crypto_chained_ops_handler_t
                                               * oph);
+
 void vnet_crypto_register_ops_handlers (vlib_main_t * vm, u32 engine_index,
                                        vnet_crypto_op_id_t opt,
                                        vnet_crypto_ops_handler_t * fn,
@@ -235,6 +381,15 @@ void vnet_crypto_register_ops_handlers (vlib_main_t * vm, u32 engine_index,
 void vnet_crypto_register_key_handler (vlib_main_t * vm, u32 engine_index,
                                       vnet_crypto_key_handler_t * keyh);
 
+/** async crypto register functions */
+u32 vnet_crypto_register_post_node (vlib_main_t * vm, char *post_node_name);
+void vnet_crypto_register_async_handler (vlib_main_t * vm,
+                                        u32 engine_index,
+                                        vnet_crypto_async_op_id_t opt,
+                                        vnet_crypto_frame_enqueue_t * enq_fn,
+                                        vnet_crypto_frame_dequeue_t *
+                                        deq_fn);
+
 typedef struct
 {
   char *name;
@@ -244,32 +399,46 @@ typedef struct
   vnet_crypto_ops_handler_t *ops_handlers[VNET_CRYPTO_N_OP_IDS];
     vnet_crypto_chained_ops_handler_t
     * chained_ops_handlers[VNET_CRYPTO_N_OP_IDS];
+  vnet_crypto_frame_enqueue_t *enqueue_handlers[VNET_CRYPTO_ASYNC_OP_N_IDS];
+  vnet_crypto_frame_dequeue_t *dequeue_handlers[VNET_CRYPTO_ASYNC_OP_N_IDS];
 } vnet_crypto_engine_t;
 
+typedef struct
+{
+  u32 node_idx;
+  u32 next_idx;
+} vnet_crypto_async_next_node_t;
+
 typedef struct
 {
   vnet_crypto_alg_data_t *algs;
   vnet_crypto_thread_t *threads;
   vnet_crypto_ops_handler_t **ops_handlers;
   vnet_crypto_chained_ops_handler_t **chained_ops_handlers;
+  vnet_crypto_frame_enqueue_t **enqueue_handlers;
+  vnet_crypto_frame_dequeue_t **dequeue_handlers;
+  clib_bitmap_t *async_active_ids;
   vnet_crypto_op_data_t opt_data[VNET_CRYPTO_N_OP_IDS];
+  vnet_crypto_async_op_data_t async_opt_data[VNET_CRYPTO_ASYNC_OP_N_IDS];
   vnet_crypto_engine_t *engines;
   vnet_crypto_key_t *keys;
   uword *engine_index_by_name;
   uword *alg_index_by_name;
+  uword *async_alg_index_by_name;
+  vnet_crypto_async_alg_data_t *async_algs;
+  u32 async_refcnt;
+  vnet_crypto_async_next_node_t *next_nodes;
 } vnet_crypto_main_t;
 
 extern vnet_crypto_main_t crypto_main;
 
-u32 vnet_crypto_submit_ops (vlib_main_t * vm, vnet_crypto_op_t ** jobs,
-                           u32 n_jobs);
-
 u32 vnet_crypto_process_chained_ops (vlib_main_t * vm, vnet_crypto_op_t ops[],
                                     vnet_crypto_op_chunk_t * chunks,
                                     u32 n_ops);
 u32 vnet_crypto_process_ops (vlib_main_t * vm, vnet_crypto_op_t ops[],
                             u32 n_ops);
 
+
 int vnet_crypto_set_handler2 (char *ops_handler_name, char *engine,
                              crypto_op_class_type_t oct);
 int vnet_crypto_is_set_handler (vnet_crypto_alg_t alg);
@@ -278,6 +447,27 @@ u32 vnet_crypto_key_add (vlib_main_t * vm, vnet_crypto_alg_t alg,
                         u8 * data, u16 length);
 void vnet_crypto_key_del (vlib_main_t * vm, vnet_crypto_key_index_t index);
 
+/**
+ * Use 2 created keys to generate new key for linked algs (cipher + integ)
+ * The returned key index is to be used for linked alg only.
+ **/
+u32 vnet_crypto_key_add_linked (vlib_main_t * vm,
+                               vnet_crypto_key_index_t index_crypto,
+                               vnet_crypto_key_index_t index_integ);
+
+clib_error_t *crypto_dispatch_enable_disable (int is_enable);
+
+int vnet_crypto_set_async_handler2 (char *alg_name, char *engine);
+
+int vnet_crypto_is_set_async_handler (vnet_crypto_async_op_id_t opt);
+
+void vnet_crypto_request_async_mode (int is_enable);
+
+vnet_crypto_async_alg_t vnet_crypto_link_algs (vnet_crypto_alg_t crypto_alg,
+                                              vnet_crypto_alg_t integ_alg);
+
+clib_error_t *crypto_dispatch_enable_disable (int is_enable);
+
 format_function_t format_vnet_crypto_alg;
 format_function_t format_vnet_crypto_engine;
 format_function_t format_vnet_crypto_op;
@@ -285,6 +475,10 @@ format_function_t format_vnet_crypto_op_type;
 format_function_t format_vnet_crypto_op_status;
 unformat_function_t unformat_vnet_crypto_alg;
 
+format_function_t format_vnet_crypto_async_op;
+format_function_t format_vnet_crypto_async_alg;
+format_function_t format_vnet_crypto_async_op_type;
+
 static_always_inline void
 vnet_crypto_op_init (vnet_crypto_op_t * op, vnet_crypto_op_id_t type)
 {
@@ -318,6 +512,119 @@ vnet_crypto_set_handler (char *alg_name, char *engine)
   return vnet_crypto_set_handler2 (alg_name, engine, CRYPTO_OP_BOTH);
 }
 
+/** async crypto inline functions **/
+
+static_always_inline vnet_crypto_async_frame_t *
+vnet_crypto_async_get_frame (vlib_main_t * vm, vnet_crypto_async_op_id_t opt)
+{
+  vnet_crypto_main_t *cm = &crypto_main;
+  vnet_crypto_thread_t *ct = cm->threads + vm->thread_index;
+  vnet_crypto_async_frame_t *f = ct->frames[opt];
+
+  if (!f)
+    {
+      pool_get_aligned (ct->frame_pool, f, CLIB_CACHE_LINE_BYTES);
+      if (CLIB_DEBUG > 0)
+       clib_memset (f, 0xfe, sizeof (*f));
+      f->state = VNET_CRYPTO_FRAME_STATE_NOT_PROCESSED;
+      f->op = opt;
+      f->n_elts = 0;
+      ct->frames[opt] = f;
+    }
+  return f;
+}
+
+static_always_inline void
+vnet_crypto_async_free_frame (vlib_main_t * vm,
+                             vnet_crypto_async_frame_t * frame)
+{
+  vnet_crypto_main_t *cm = &crypto_main;
+  vnet_crypto_thread_t *ct = cm->threads + vm->thread_index;
+  pool_put (ct->frame_pool, frame);
+}
+
+static_always_inline int
+vnet_crypto_async_submit_open_frame (vlib_main_t * vm,
+                                    vnet_crypto_async_frame_t * frame)
+{
+  vnet_crypto_main_t *cm = &crypto_main;
+  vnet_crypto_thread_t *ct = cm->threads + vm->thread_index;
+  vnet_crypto_async_op_id_t opt = frame->op;
+  int ret = (cm->enqueue_handlers[frame->op]) (vm, frame);
+  clib_bitmap_set_no_check (cm->async_active_ids, opt, 1);
+  if (PREDICT_TRUE (ret == 0))
+    {
+      vnet_crypto_async_frame_t *nf = 0;
+      frame->state = VNET_CRYPTO_FRAME_STATE_WORK_IN_PROGRESS;
+      pool_get_aligned (ct->frame_pool, nf, CLIB_CACHE_LINE_BYTES);
+      if (CLIB_DEBUG > 0)
+       clib_memset (nf, 0xfe, sizeof (*nf));
+      nf->state = VNET_CRYPTO_FRAME_STATE_NOT_PROCESSED;
+      nf->op = opt;
+      nf->n_elts = 0;
+      ct->frames[opt] = nf;
+    }
+  return ret;
+}
+
+static_always_inline int
+vnet_crypto_async_add_to_frame (vlib_main_t * vm,
+                               vnet_crypto_async_frame_t ** frame,
+                               u32 key_index,
+                               u32 crypto_len, i16 integ_len_adj,
+                               i16 crypto_start_offset,
+                               u16 integ_start_offset,
+                               u32 buffer_index,
+                               u16 next_node,
+                               u8 * iv, u8 * tag, u8 * aad, u8 flags)
+{
+  vnet_crypto_async_frame_t *f = *frame;
+  vnet_crypto_async_frame_elt_t *fe;
+  u16 index;
+
+  if (PREDICT_FALSE (f->n_elts == VNET_CRYPTO_FRAME_SIZE))
+    {
+      vnet_crypto_async_op_id_t opt = f->op;
+      int ret;
+      ret = vnet_crypto_async_submit_open_frame (vm, f);
+      if (PREDICT_FALSE (ret < 0))
+       return -1;
+      f = vnet_crypto_async_get_frame (vm, opt);
+      *frame = f;
+    }
+
+  index = f->n_elts;
+  fe = &f->elts[index];
+  f->n_elts++;
+  fe->key_index = key_index;
+  fe->crypto_total_length = crypto_len;
+  fe->crypto_start_offset = crypto_start_offset;
+  fe->integ_start_offset = integ_start_offset;
+  fe->integ_length_adj = integ_len_adj;
+  fe->iv = iv;
+  fe->tag = tag;
+  fe->aad = aad;
+  fe->flags = flags;
+  f->buffer_indices[index] = buffer_index;
+  f->next_node_index[index] = next_node;
+
+  return 0;
+}
+
+static_always_inline void
+vnet_crypto_async_reset_frame (vnet_crypto_async_frame_t * f)
+{
+  vnet_crypto_async_op_id_t opt;
+  ASSERT (f != 0);
+  ASSERT (f->state == VNET_CRYPTO_FRAME_STATE_NOT_PROCESSED);
+  opt = f->op;
+  if (CLIB_DEBUG > 0)
+    clib_memset (f, 0xfe, sizeof (*f));
+  f->state = VNET_CRYPTO_FRAME_STATE_NOT_PROCESSED;
+  f->op = opt;
+  f->n_elts = 0;
+}
+
 #endif /* included_vnet_crypto_crypto_h */
 
 /*
index 715941e..b58ab7f 100644 (file)
@@ -105,6 +105,61 @@ format_vnet_crypto_engine (u8 * s, va_list * args)
   return format (s, "%s", e->name);
 }
 
+u8 *
+format_vnet_crypto_async_op_type (u8 * s, va_list * args)
+{
+  vnet_crypto_async_op_type_t opt =
+    va_arg (*args, vnet_crypto_async_op_type_t);
+  char *strings[] = {
+#define _(n, s) [VNET_CRYPTO_ASYNC_OP_TYPE_##n] = s,
+    foreach_crypto_async_op_type
+#undef _
+  };
+
+  if (opt >= VNET_CRYPTO_ASYNC_OP_N_TYPES)
+    return format (s, "unknown");
+
+  return format (s, "%s", strings[opt]);
+}
+
+u8 *
+format_vnet_crypto_async_alg (u8 * s, va_list * args)
+{
+  vnet_crypto_async_alg_t alg = va_arg (*args, vnet_crypto_async_alg_t);
+  vnet_crypto_main_t *cm = &crypto_main;
+  vnet_crypto_async_alg_data_t *d = vec_elt_at_index (cm->async_algs, alg);
+  return format (s, "%s", d->name);
+}
+
+u8 *
+format_vnet_crypto_async_op (u8 * s, va_list * args)
+{
+  vnet_crypto_main_t *cm = &crypto_main;
+  vnet_crypto_async_op_id_t op = va_arg (*args, int);  // vnet_crypto_op_id_t);
+  vnet_crypto_async_op_data_t *otd = cm->async_opt_data + op;
+
+  return format (s, "%U-%U", format_vnet_crypto_async_op_type, otd->type,
+                format_vnet_crypto_async_alg, otd->alg);
+
+  vnet_crypto_async_op_id_t opt = va_arg (*args, vnet_crypto_async_op_id_t);
+  char *strings[] = {
+#define _(n, s, k, t, a) \
+               [VNET_CRYPTO_OP_##n##_TAG##t##_AAD##a##_ENC] = s "-enc", \
+               [VNET_CRYPTO_OP_##n##_TAG##t##_AAD##a##_DEC] = s "-dec",
+    foreach_crypto_aead_async_alg
+#undef _
+#define _(c, h, s, k ,d) \
+               [VNET_CRYPTO_OP_##c##_##h##_TAG##d##_ENC] = s "-enc", \
+               [VNET_CRYPTO_OP_##c##_##h##_TAG##d##_DEC] = s "-dec",
+      foreach_crypto_link_async_alg
+#undef _
+  };
+
+  if (opt >= VNET_CRYPTO_ASYNC_OP_N_IDS)
+    return format (s, "unknown");
+
+  return format (s, "%s", strings[opt]);
+}
 
 /*
  * fd.io coding-style-patch-verification: ON
diff --git a/src/vnet/crypto/node.c b/src/vnet/crypto/node.c
new file mode 100644 (file)
index 0000000..51ee63d
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2020 Cisco and/or its affiliates.
+ * 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 <stdbool.h>
+#include <vlib/vlib.h>
+#include <vnet/crypto/crypto.h>
+
+typedef enum
+{
+#define _(sym,str) VNET_CRYPTO_ASYNC_ERROR_##sym,
+  foreach_crypto_op_status
+#undef _
+    VNET_CRYPTO_ASYNC_N_ERROR,
+} vnet_crypto_async_error_t;
+
+static char *vnet_crypto_async_error_strings[] = {
+#define _(sym,string) string,
+  foreach_crypto_op_status
+#undef _
+};
+
+#define foreach_crypto_dispatch_next \
+  _(ERR_DROP, "error-drop")
+
+typedef enum
+{
+#define _(n, s) CRYPTO_DISPATCH_NEXT_##n,
+  foreach_crypto_dispatch_next
+#undef _
+    CRYPTO_DISPATCH_N_NEXT,
+} crypto_dispatch_next_t;
+
+typedef struct
+{
+  vnet_crypto_op_status_t op_status;
+  vnet_crypto_async_op_id_t op;
+} crypto_dispatch_trace_t;
+
+static u8 *
+format_crypto_dispatch_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  crypto_dispatch_trace_t *t = va_arg (*args, crypto_dispatch_trace_t *);
+
+  s = format (s, "%U: %U", format_vnet_crypto_async_op, t->op,
+             format_vnet_crypto_op_status, t->op_status);
+  return s;
+}
+
+static void
+vnet_crypto_async_add_trace (vlib_main_t * vm, vlib_node_runtime_t * node,
+                            vlib_buffer_t * b,
+                            vnet_crypto_async_op_id_t op_id,
+                            vnet_crypto_op_status_t status)
+{
+  crypto_dispatch_trace_t *tr = vlib_add_trace (vm, node, b, sizeof (*tr));
+  tr->op_status = status;
+  tr->op = op_id;
+}
+
+static_always_inline u32
+crypto_dequeue_frame (vlib_main_t * vm, vlib_node_runtime_t * node,
+                     vnet_crypto_thread_t * ct,
+                     vnet_crypto_frame_dequeue_t * hdl,
+                     u32 n_cache, u32 * n_total)
+{
+  vnet_crypto_async_frame_t *cf = (hdl) (vm);
+
+  while (cf)
+    {
+      vec_validate (ct->buffer_indice, n_cache + cf->n_elts);
+      vec_validate (ct->nexts, n_cache + cf->n_elts);
+      clib_memcpy_fast (ct->buffer_indice + n_cache, cf->buffer_indices,
+                       sizeof (u32) * cf->n_elts);
+      if (cf->state == VNET_CRYPTO_FRAME_STATE_SUCCESS)
+       {
+         clib_memcpy_fast (ct->nexts + n_cache, cf->next_node_index,
+                           sizeof (u16) * cf->n_elts);
+       }
+      else
+       {
+         u32 i;
+         for (i = 0; i < cf->n_elts; i++)
+           {
+             if (cf->elts[i].status != VNET_CRYPTO_OP_STATUS_COMPLETED)
+               {
+                 ct->nexts[i + n_cache] = CRYPTO_DISPATCH_NEXT_ERR_DROP;
+                 vlib_node_increment_counter (vm, node->node_index,
+                                              cf->elts[i].status, 1);
+               }
+             else
+               ct->nexts[i + n_cache] = cf->next_node_index[i];
+           }
+       }
+      n_cache += cf->n_elts;
+      *n_total += cf->n_elts;
+      if (n_cache >= VLIB_FRAME_SIZE)
+       {
+         vlib_buffer_enqueue_to_next (vm, node, ct->buffer_indice, ct->nexts,
+                                      n_cache);
+         n_cache = 0;
+       }
+
+      if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
+       {
+         u32 i;
+
+         for (i = 0; i < cf->n_elts; i++)
+           {
+             vlib_buffer_t *b = vlib_get_buffer (vm, cf->buffer_indices[i]);
+             if (b->flags & VLIB_BUFFER_IS_TRACED)
+               vnet_crypto_async_add_trace (vm, node, b, cf->op,
+                                            cf->elts[i].status);
+           }
+       }
+      vnet_crypto_async_free_frame (vm, cf);
+      cf = (hdl) (vm);
+    }
+
+  return n_cache;
+}
+
+VLIB_NODE_FN (crypto_dispatch_node) (vlib_main_t * vm,
+                                    vlib_node_runtime_t * node,
+                                    vlib_frame_t * frame)
+{
+  vnet_crypto_main_t *cm = &crypto_main;
+  vnet_crypto_thread_t *ct = cm->threads + vm->thread_index;
+  u32 n_dispatched = 0, n_cache = 0;
+  u32 index;
+
+  /* *INDENT-OFF* */
+  clib_bitmap_foreach (index, cm->async_active_ids, ({
+    n_cache = crypto_dequeue_frame (vm, node, ct, cm->dequeue_handlers[index],
+                                   n_cache, &n_dispatched);
+  }));
+  /* *INDENT-ON* */
+  if (n_cache)
+    vlib_buffer_enqueue_to_next (vm, node, ct->buffer_indice, ct->nexts,
+                                n_cache);
+
+  return n_dispatched;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (crypto_dispatch_node) = {
+  .name = "crypto-dispatch",
+  .type = VLIB_NODE_TYPE_INPUT,
+  .state = VLIB_NODE_STATE_DISABLED,
+  .format_trace = format_crypto_dispatch_trace,
+
+  .n_errors = ARRAY_LEN(vnet_crypto_async_error_strings),
+  .error_strings = vnet_crypto_async_error_strings,
+
+  .n_next_nodes = CRYPTO_DISPATCH_N_NEXT,
+  .next_nodes = {
+#define _(n, s) \
+  [CRYPTO_DISPATCH_NEXT_##n] = s,
+      foreach_crypto_dispatch_next
+#undef _
+  },
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
index b173645..6c47bb0 100644 (file)
@@ -105,29 +105,101 @@ esp_seq_advance (ipsec_sa_t * sa)
   return 0;
 }
 
-always_inline void
-esp_aad_fill (vnet_crypto_op_t * op,
-             const esp_header_t * esp, const ipsec_sa_t * sa)
+always_inline u16
+esp_aad_fill (u8 * data, const esp_header_t * esp, const ipsec_sa_t * sa)
 {
   esp_aead_t *aad;
 
-  aad = (esp_aead_t *) op->aad;
+  aad = (esp_aead_t *) data;
   aad->data[0] = esp->spi;
 
   if (ipsec_sa_is_set_USE_ESN (sa))
     {
       /* SPI, seq-hi, seq-low */
-      aad->data[1] = clib_host_to_net_u32 (sa->seq_hi);
+      aad->data[1] = (u32) clib_host_to_net_u32 (sa->seq_hi);
       aad->data[2] = esp->seq;
-      op->aad_len = 12;
+      return 12;
     }
   else
     {
       /* SPI, seq-low */
       aad->data[1] = esp->seq;
-      op->aad_len = 8;
+      return 8;
     }
 }
+
+/**
+ * The post data structure to for esp_encrypt/decrypt_inline to write to
+ * vib_buffer_t opaque unused field, and for post nodes to pick up after
+ * dequeue.
+ **/
+typedef struct
+{
+  union
+  {
+    struct
+    {
+      u8 icv_sz;
+      u8 iv_sz;
+      ipsec_sa_flags_t flags;
+      u32 sa_index;
+    };
+    u64 sa_data;
+  };
+
+  u32 seq;
+  i16 current_data;
+  i16 current_length;
+  u16 hdr_sz;
+  u16 is_chain;
+  u32 protect_index;
+} esp_decrypt_packet_data_t;
+
+STATIC_ASSERT_SIZEOF (esp_decrypt_packet_data_t, 3 * sizeof (u64));
+
+/* we are forced to store the decrypt post data into 2 separate places -
+   vlib_opaque and opaque2. */
+typedef struct
+{
+  vlib_buffer_t *lb;
+  u32 free_buffer_index;
+  u8 icv_removed;
+} esp_decrypt_packet_data2_t;
+
+typedef union
+{
+  u16 next_index;
+  esp_decrypt_packet_data_t decrypt_data;
+} esp_post_data_t;
+
+STATIC_ASSERT (sizeof (esp_post_data_t) <=
+              STRUCT_SIZE_OF (vnet_buffer_opaque_t, unused),
+              "Custom meta-data too large for vnet_buffer_opaque_t");
+
+#define esp_post_data(b) \
+    ((esp_post_data_t *)((u8 *)((b)->opaque) \
+        + STRUCT_OFFSET_OF (vnet_buffer_opaque_t, unused)))
+
+STATIC_ASSERT (sizeof (esp_decrypt_packet_data2_t) <=
+              STRUCT_SIZE_OF (vnet_buffer_opaque2_t, unused),
+              "Custom meta-data too large for vnet_buffer_opaque2_t");
+
+#define esp_post_data2(b) \
+    ((esp_decrypt_packet_data2_t *)((u8 *)((b)->opaque2) \
+        + STRUCT_OFFSET_OF (vnet_buffer_opaque2_t, unused)))
+
+typedef struct
+{
+  /* esp post node index for async crypto */
+  u32 esp4_post_next;
+  u32 esp6_post_next;
+  u32 esp4_tun_post_next;
+  u32 esp6_tun_post_next;
+} esp_async_post_next_t;
+
+extern esp_async_post_next_t esp_encrypt_async_next;
+extern esp_async_post_next_t esp_decrypt_async_next;
+
 #endif /* __ESP_H__ */
 
 /*
index 0d14fe5..b3c031e 100644 (file)
@@ -32,7 +32,8 @@ _(DROP, "error-drop")                           \
 _(IP4_INPUT, "ip4-input-no-checksum")           \
 _(IP6_INPUT, "ip6-input")                       \
 _(L2_INPUT, "l2-input")                         \
-_(HANDOFF, "handoff")
+_(HANDOFF, "handoff")                          \
+_(PENDING, "pending")
 
 #define _(v, s) ESP_DECRYPT_NEXT_##v,
 typedef enum
@@ -42,9 +43,23 @@ typedef enum
     ESP_DECRYPT_N_NEXT,
 } esp_decrypt_next_t;
 
+#define foreach_esp_decrypt_post_next                  \
+_(DROP, "error-drop")                                  \
+_(IP4_INPUT, "ip4-input-no-checksum")                  \
+_(IP6_INPUT, "ip6-input")                              \
+_(L2_INPUT, "l2-input")
+
+#define _(v, s) ESP_DECRYPT_POST_NEXT_##v,
+typedef enum
+{
+  foreach_esp_decrypt_post_next
+#undef _
+    ESP_DECRYPT_POST_N_NEXT,
+} esp_decrypt_post_next_t;
 
 #define foreach_esp_decrypt_error                               \
  _(RX_PKTS, "ESP pkts received")                                \
+ _(RX_POST_PKTS, "ESP-POST pkts received")                      \
  _(DECRYPTION_FAILED, "ESP decryption failed")                  \
  _(INTEG_ERROR, "Integrity check failed")                       \
  _(CRYPTO_ENGINE_ERROR, "crypto engine error (packet dropped)") \
@@ -96,32 +111,6 @@ format_esp_decrypt_trace (u8 * s, va_list * args)
   return s;
 }
 
-typedef struct
-{
-  vlib_buffer_t *lb;
-  union
-  {
-    struct
-    {
-      u8 icv_sz;
-      u8 iv_sz;
-      ipsec_sa_flags_t flags;
-      u32 sa_index;
-    };
-    u64 sa_data;
-  };
-
-  u32 seq;
-  u32 free_buffer_index;
-  i16 current_data;
-  i16 current_length;
-  u16 hdr_sz;
-  u8 icv_removed;
-  u8 __unused;
-} esp_decrypt_packet_data_t;
-
-STATIC_ASSERT_SIZEOF (esp_decrypt_packet_data_t, 4 * sizeof (u64));
-
 #define ESP_ENCRYPT_PD_F_FD_TRANSPORT (1 << 2)
 
 static_always_inline void
@@ -214,10 +203,10 @@ esp_remove_tail (vlib_main_t * vm, vlib_buffer_t * b, vlib_buffer_t * last,
    return pointer to it */
 static_always_inline u8 *
 esp_move_icv (vlib_main_t * vm, vlib_buffer_t * first,
-             esp_decrypt_packet_data_t * pd, u16 icv_sz, u16 * dif)
+             esp_decrypt_packet_data2_t * pd2, u16 icv_sz, u16 * dif)
 {
   vlib_buffer_t *before_last, *bp;
-  u16 last_sz = pd->lb->current_length;
+  u16 last_sz = pd2->lb->current_length;
   u16 first_sz = icv_sz - last_sz;
 
   bp = before_last = first;
@@ -227,42 +216,43 @@ esp_move_icv (vlib_main_t * vm, vlib_buffer_t * first,
       bp = vlib_get_buffer (vm, bp->next_buffer);
     }
 
-  u8 *lb_curr = vlib_buffer_get_current (pd->lb);
+  u8 *lb_curr = vlib_buffer_get_current (pd2->lb);
   memmove (lb_curr + first_sz, lb_curr, last_sz);
   clib_memcpy_fast (lb_curr, vlib_buffer_get_tail (before_last) - first_sz,
                    first_sz);
   before_last->current_length -= first_sz;
+  clib_memset (vlib_buffer_get_tail (before_last), 0, first_sz);
   if (dif)
     dif[0] = first_sz;
-  pd->lb = before_last;
-  pd->icv_removed = 1;
-  pd->free_buffer_index = before_last->next_buffer;
+  pd2->lb = before_last;
+  pd2->icv_removed = 1;
+  pd2->free_buffer_index = before_last->next_buffer;
   before_last->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
   return lb_curr;
 }
 
-static_always_inline int
+static_always_inline i16
 esp_insert_esn (vlib_main_t * vm, ipsec_sa_t * sa,
-               esp_decrypt_packet_data_t * pd, vnet_crypto_op_t * op,
-               u16 * len, vlib_buffer_t * b, u8 * payload)
+               esp_decrypt_packet_data2_t * pd2, u32 * data_len,
+               u8 ** digest, u16 * len, vlib_buffer_t * b, u8 * payload)
 {
   if (!ipsec_sa_is_set_USE_ESN (sa))
-    return 1;
+    return 0;
 
   /* shift ICV by 4 bytes to insert ESN */
   u32 seq_hi = clib_host_to_net_u32 (sa->seq_hi);
   u8 tmp[ESP_MAX_ICV_SIZE], sz = sizeof (sa->seq_hi);
 
-  if (pd->icv_removed)
+  if (pd2->icv_removed)
     {
-      u16 space_left = vlib_buffer_space_left_at_end (vm, pd->lb);
+      u16 space_left = vlib_buffer_space_left_at_end (vm, pd2->lb);
       if (space_left >= sz)
        {
-         clib_memcpy_fast (vlib_buffer_get_tail (pd->lb), &seq_hi, sz);
-         op->len += sz;
+         clib_memcpy_fast (vlib_buffer_get_tail (pd2->lb), &seq_hi, sz);
+         *data_len += sz;
        }
       else
-       return 0;
+       return sz;
 
       len[0] = b->current_length;
     }
@@ -271,39 +261,39 @@ esp_insert_esn (vlib_main_t * vm, ipsec_sa_t * sa,
       clib_memcpy_fast (tmp, payload + len[0], ESP_MAX_ICV_SIZE);
       clib_memcpy_fast (payload + len[0], &seq_hi, sz);
       clib_memcpy_fast (payload + len[0] + sz, tmp, ESP_MAX_ICV_SIZE);
-      op->len += sz;
-      op->digest += sz;
+      *data_len += sz;
+      *digest += sz;
     }
-  return 1;
+  return sz;
 }
 
 static_always_inline u8 *
 esp_move_icv_esn (vlib_main_t * vm, vlib_buffer_t * first,
-                 esp_decrypt_packet_data_t * pd, u16 icv_sz, ipsec_sa_t * sa,
-                 u8 * extra_esn, vnet_crypto_op_t * op)
+                 esp_decrypt_packet_data2_t * pd2, u16 icv_sz,
+                 ipsec_sa_t * sa, u8 * extra_esn, u32 * len)
 {
   u16 dif = 0;
-  u8 *digest = esp_move_icv (vm, first, pd, icv_sz, &dif);
+  u8 *digest = esp_move_icv (vm, first, pd2, icv_sz, &dif);
   if (dif)
-    op->len -= dif;
+    *len -= dif;
 
   if (ipsec_sa_is_set_USE_ESN (sa))
     {
       u8 sz = sizeof (sa->seq_hi);
       u32 seq_hi = clib_host_to_net_u32 (sa->seq_hi);
-      u16 space_left = vlib_buffer_space_left_at_end (vm, pd->lb);
+      u16 space_left = vlib_buffer_space_left_at_end (vm, pd2->lb);
 
       if (space_left >= sz)
        {
-         clib_memcpy_fast (vlib_buffer_get_tail (pd->lb), &seq_hi, sz);
-         op->len += sz;
+         clib_memcpy_fast (vlib_buffer_get_tail (pd2->lb), &seq_hi, sz);
+         *len += sz;
        }
       else
        {
          /* no space for ESN at the tail, use the next buffer
           * (with ICV data) */
-         ASSERT (pd->icv_removed);
-         vlib_buffer_t *tmp = vlib_get_buffer (vm, pd->free_buffer_index);
+         ASSERT (pd2->icv_removed);
+         vlib_buffer_t *tmp = vlib_get_buffer (vm, pd2->free_buffer_index);
          clib_memcpy_fast (vlib_buffer_get_current (tmp) - sz, &seq_hi, sz);
          extra_esn[0] = 1;
        }
@@ -311,10 +301,735 @@ esp_move_icv_esn (vlib_main_t * vm, vlib_buffer_t * first,
   return digest;
 }
 
+static_always_inline int
+esp_decrypt_chain_integ (vlib_main_t * vm, ipsec_per_thread_data_t * ptd,
+                        esp_decrypt_packet_data2_t * pd2,
+                        ipsec_sa_t * sa0, vlib_buffer_t * b, u8 icv_sz,
+                        u8 * start_src, u32 start_len,
+                        u8 ** digest, u16 * n_ch, u32 * integ_total_len)
+{
+  vnet_crypto_op_chunk_t *ch;
+  vlib_buffer_t *cb = vlib_get_buffer (vm, b->next_buffer);
+  u16 n_chunks = 1;
+  u32 total_len;
+  vec_add2 (ptd->chunks, ch, 1);
+  total_len = ch->len = start_len;
+  ch->src = start_src;
+
+  while (1)
+    {
+      vec_add2 (ptd->chunks, ch, 1);
+      n_chunks += 1;
+      ch->src = vlib_buffer_get_current (cb);
+      if (pd2->lb == cb)
+       {
+         if (pd2->icv_removed)
+           ch->len = cb->current_length;
+         else
+           ch->len = cb->current_length - icv_sz;
+         if (ipsec_sa_is_set_USE_ESN (sa0))
+           {
+             u32 seq_hi = clib_host_to_net_u32 (sa0->seq_hi);
+             u8 tmp[ESP_MAX_ICV_SIZE], sz = sizeof (sa0->seq_hi);
+             u8 *esn;
+             vlib_buffer_t *tmp_b;
+             u16 space_left = vlib_buffer_space_left_at_end (vm, pd2->lb);
+             if (space_left < sz)
+               {
+                 if (pd2->icv_removed)
+                   {
+                     /* use pre-data area from the last bufer
+                        that was removed from the chain */
+                     tmp_b = vlib_get_buffer (vm, pd2->free_buffer_index);
+                     esn = tmp_b->data - sz;
+                   }
+                 else
+                   {
+                     /* no space, need to allocate new buffer */
+                     u32 tmp_bi = 0;
+                     if (vlib_buffer_alloc (vm, &tmp_bi, 1) != 1)
+                       return -1;
+                     tmp_b = vlib_get_buffer (vm, tmp_bi);
+                     esn = tmp_b->data;
+                     pd2->free_buffer_index = tmp_bi;
+                   }
+                 clib_memcpy_fast (esn, &seq_hi, sz);
+
+                 vec_add2 (ptd->chunks, ch, 1);
+                 n_chunks += 1;
+                 ch->src = esn;
+                 ch->len = sz;
+               }
+             else
+               {
+                 if (pd2->icv_removed)
+                   {
+                     clib_memcpy_fast (vlib_buffer_get_tail
+                                       (pd2->lb), &seq_hi, sz);
+                   }
+                 else
+                   {
+                     clib_memcpy_fast (tmp, *digest, ESP_MAX_ICV_SIZE);
+                     clib_memcpy_fast (*digest, &seq_hi, sz);
+                     clib_memcpy_fast (*digest + sz, tmp, ESP_MAX_ICV_SIZE);
+                     *digest += sz;
+                   }
+                 ch->len += sz;
+               }
+           }
+         total_len += ch->len;
+         break;
+       }
+      else
+       total_len += ch->len = cb->current_length;
+
+      if (!(cb->flags & VLIB_BUFFER_NEXT_PRESENT))
+       break;
+
+      cb = vlib_get_buffer (vm, cb->next_buffer);
+    }
+
+  if (n_ch)
+    *n_ch = n_chunks;
+  if (integ_total_len)
+    *integ_total_len = total_len;
+
+  return 0;
+}
+
+static_always_inline u32
+esp_decrypt_chain_crypto (vlib_main_t * vm, ipsec_per_thread_data_t * ptd,
+                         esp_decrypt_packet_data2_t * pd2,
+                         ipsec_sa_t * sa0, vlib_buffer_t * b, u8 icv_sz,
+                         u8 * start, u32 start_len, u8 ** tag, u16 * n_ch)
+{
+  vnet_crypto_op_chunk_t *ch;
+  vlib_buffer_t *cb = b;
+  u16 n_chunks = 1;
+  u32 total_len;
+  vec_add2 (ptd->chunks, ch, 1);
+  total_len = ch->len = start_len;
+  ch->src = ch->dst = start;
+  cb = vlib_get_buffer (vm, cb->next_buffer);
+  n_chunks = 1;
+
+  while (1)
+    {
+      vec_add2 (ptd->chunks, ch, 1);
+      n_chunks += 1;
+      ch->src = ch->dst = vlib_buffer_get_current (cb);
+      if (pd2->lb == cb)
+       {
+         if (ipsec_sa_is_set_IS_AEAD (sa0))
+           {
+             if (pd2->lb->current_length < icv_sz)
+               {
+                 u16 dif = 0;
+                 *tag = esp_move_icv (vm, b, pd2, icv_sz, &dif);
+
+                 /* this chunk does not contain crypto data */
+                 n_chunks -= 1;
+                 /* and fix previous chunk's length as it might have
+                    been changed */
+                 ASSERT (n_chunks > 0);
+                 if (pd2->lb == b)
+                   {
+                     total_len -= dif;
+                     ch[-1].len -= dif;
+                   }
+                 else
+                   {
+                     total_len = total_len + pd2->lb->current_length -
+                       ch[-1].len;
+                     ch[-1].len = pd2->lb->current_length;
+                   }
+                 break;
+               }
+             else
+               *tag = vlib_buffer_get_tail (pd2->lb) - icv_sz;
+           }
+
+         if (pd2->icv_removed)
+           total_len += ch->len = cb->current_length;
+         else
+           total_len += ch->len = cb->current_length - icv_sz;
+       }
+      else
+       total_len += ch->len = cb->current_length;
+
+      if (!(cb->flags & VLIB_BUFFER_NEXT_PRESENT))
+       break;
+
+      cb = vlib_get_buffer (vm, cb->next_buffer);
+    }
+
+  if (n_ch)
+    *n_ch = n_chunks;
+
+  return total_len;
+}
+
+static_always_inline void
+esp_decrypt_prepare_sync_op (vlib_main_t * vm, vlib_node_runtime_t * node,
+                            ipsec_per_thread_data_t * ptd,
+                            vnet_crypto_op_t *** crypto_ops,
+                            vnet_crypto_op_t *** integ_ops,
+                            vnet_crypto_op_t * op,
+                            ipsec_sa_t * sa0, u8 * payload,
+                            u16 len, u8 icv_sz, u8 iv_sz,
+                            esp_decrypt_packet_data_t * pd,
+                            esp_decrypt_packet_data2_t * pd2,
+                            vlib_buffer_t * b, u16 * next, u32 index)
+{
+  const u8 esp_sz = sizeof (esp_header_t);
+
+  if (PREDICT_TRUE (sa0->integ_op_id != VNET_CRYPTO_OP_NONE))
+    {
+      vnet_crypto_op_init (op, sa0->integ_op_id);
+      op->key_index = sa0->integ_key_index;
+      op->src = payload;
+      op->flags = VNET_CRYPTO_OP_FLAG_HMAC_CHECK;
+      op->user_data = index;
+      op->digest = payload + len;
+      op->digest_len = icv_sz;
+      op->len = len;
+
+      if (pd->is_chain)
+       {
+         /* buffer is chained */
+         op->len = pd->current_length;
+
+         /* special case when ICV is splitted and needs to be reassembled
+          * first -> move it to the last buffer. Also take into account
+          * that ESN needs to be added after encrypted data and may or
+          * may not fit in the tail.*/
+         if (pd2->lb->current_length < icv_sz)
+           {
+             u8 extra_esn = 0;
+             op->digest =
+               esp_move_icv_esn (vm, b, pd2, icv_sz, sa0,
+                                 &extra_esn, &op->len);
+
+             if (extra_esn)
+               {
+                 /* esn is in the last buffer, that was unlinked from
+                  * the chain */
+                 op->len = b->current_length;
+               }
+             else
+               {
+                 if (pd2->lb == b)
+                   {
+                     /* we now have a single buffer of crypto data, adjust
+                      * the length (second buffer contains only ICV) */
+                     *integ_ops = &ptd->integ_ops;
+                     *crypto_ops = &ptd->crypto_ops;
+                     len = b->current_length;
+                     goto out;
+                   }
+               }
+           }
+         else
+           op->digest = vlib_buffer_get_tail (pd2->lb) - icv_sz;
+
+         op->flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
+         op->chunk_index = vec_len (ptd->chunks);
+         if (esp_decrypt_chain_integ (vm, ptd, pd2, sa0, b, icv_sz,
+                                      payload, pd->current_length,
+                                      &op->digest, &op->n_chunks, 0) < 0)
+           {
+             b->error = node->errors[ESP_DECRYPT_ERROR_NO_BUFFERS];
+             next[0] = ESP_DECRYPT_NEXT_DROP;
+             return;
+           }
+       }
+      else
+       esp_insert_esn (vm, sa0, pd2, &op->len, &op->digest, &len, b,
+                       payload);
+    out:
+      vec_add_aligned (*(integ_ops[0]), op, 1, CLIB_CACHE_LINE_BYTES);
+    }
+
+  payload += esp_sz;
+  len -= esp_sz;
+
+  if (sa0->crypto_dec_op_id != VNET_CRYPTO_OP_NONE)
+    {
+      vnet_crypto_op_init (op, sa0->crypto_dec_op_id);
+      op->key_index = sa0->crypto_key_index;
+      op->iv = payload;
+
+      if (ipsec_sa_is_set_IS_AEAD (sa0))
+       {
+         esp_header_t *esp0;
+         esp_aead_t *aad;
+         u8 *scratch;
+
+         /*
+          * construct the AAD and the nonce (Salt || IV) in a scratch
+          * space in front of the IP header.
+          */
+         scratch = payload - esp_sz;
+         esp0 = (esp_header_t *) (scratch);
+
+         scratch -= (sizeof (*aad) + pd->hdr_sz);
+         op->aad = scratch;
+
+         op->aad_len = esp_aad_fill (op->aad, esp0, sa0);
+
+         /*
+          * we don't need to refer to the ESP header anymore so we
+          * can overwrite it with the salt and use the IV where it is
+          * to form the nonce = (Salt + IV)
+          */
+         op->iv -= sizeof (sa0->salt);
+         clib_memcpy_fast (op->iv, &sa0->salt, sizeof (sa0->salt));
+
+         op->tag = payload + len;
+         op->tag_len = 16;
+       }
+      op->src = op->dst = payload += iv_sz;
+      op->len = len - iv_sz;
+      op->user_data = index;
+
+      if (pd->is_chain && (pd2->lb != b))
+       {
+         /* buffer is chained */
+         op->flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
+         op->chunk_index = vec_len (ptd->chunks);
+         esp_decrypt_chain_crypto (vm, ptd, pd2, sa0, b, icv_sz,
+                                   payload, len - pd->iv_sz + pd->icv_sz,
+                                   &op->tag, &op->n_chunks);
+       }
+
+      vec_add_aligned (*(crypto_ops[0]), op, 1, CLIB_CACHE_LINE_BYTES);
+    }
+}
+
+static_always_inline int
+esp_decrypt_prepare_async_frame (vlib_main_t * vm,
+                                vlib_node_runtime_t * node,
+                                ipsec_per_thread_data_t * ptd,
+                                vnet_crypto_async_frame_t ** f,
+                                ipsec_sa_t * sa0, u8 * payload, u16 len,
+                                u8 icv_sz, u8 iv_sz,
+                                esp_decrypt_packet_data_t * pd,
+                                esp_decrypt_packet_data2_t * pd2, u32 bi,
+                                vlib_buffer_t * b, u16 * next,
+                                u16 async_next)
+{
+  const u8 esp_sz = sizeof (esp_header_t);
+  u32 current_protect_index = vnet_buffer (b)->ipsec.protect_index;
+  esp_decrypt_packet_data_t *async_pd = &(esp_post_data (b))->decrypt_data;
+  esp_decrypt_packet_data2_t *async_pd2 = esp_post_data2 (b);
+  u8 *tag = payload + len, *iv = payload + esp_sz, *aad = 0;
+  u32 key_index;
+  u32 crypto_len, integ_len = 0;
+  i16 crypto_start_offset, integ_start_offset = 0;
+  u8 flags = 0;
+
+  if (!ipsec_sa_is_set_IS_AEAD (sa0))
+    {
+      /* linked algs */
+      key_index = sa0->linked_key_index;
+      integ_start_offset = payload - b->data;
+      integ_len = len;
+
+      if (pd->is_chain)
+       {
+         /* buffer is chained */
+         integ_len = pd->current_length;
+
+         /* special case when ICV is splitted and needs to be reassembled
+          * first -> move it to the last buffer. Also take into account
+          * that ESN needs to be added after encrypted data and may or
+          * may not fit in the tail.*/
+         if (pd2->lb->current_length < icv_sz)
+           {
+             u8 extra_esn = 0;
+             tag = esp_move_icv_esn (vm, b, pd2, icv_sz, sa0,
+                                     &extra_esn, &integ_len);
+
+             if (extra_esn)
+               {
+                 /* esn is in the last buffer, that was unlinked from
+                  * the chain */
+                 integ_len = b->current_length;
+               }
+             else
+               {
+                 if (pd2->lb == b)
+                   {
+                     /* we now have a single buffer of crypto data, adjust
+                      * the length (second buffer contains only ICV) */
+                     len = b->current_length;
+                     goto out;
+                   }
+               }
+           }
+         else
+           tag = vlib_buffer_get_tail (pd2->lb) - icv_sz;
+
+         flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
+         if (esp_decrypt_chain_integ (vm, ptd, pd2, sa0, b, icv_sz, payload,
+                                      pd->current_length, &tag,
+                                      0, &integ_len) < 0)
+           {
+             /* allocate buffer failed, will not add to frame and drop */
+             b->error = node->errors[ESP_DECRYPT_ERROR_NO_BUFFERS];
+             next[0] = ESP_DECRYPT_NEXT_DROP;
+             return 0;
+           }
+       }
+      else
+       esp_insert_esn (vm, sa0, pd2, &integ_len, &tag, &len, b, payload);
+    }
+  else
+    key_index = sa0->crypto_key_index;
+
+out:
+  /* crypto */
+  payload += esp_sz;
+  len -= esp_sz;
+  iv = payload;
+
+  if (ipsec_sa_is_set_IS_AEAD (sa0))
+    {
+      esp_header_t *esp0;
+      u8 *scratch;
+
+      /*
+       * construct the AAD and the nonce (Salt || IV) in a scratch
+       * space in front of the IP header.
+       */
+      scratch = payload - esp_sz;
+      esp0 = (esp_header_t *) (scratch);
+
+      scratch -= (sizeof (esp_aead_t) + pd->hdr_sz);
+      aad = scratch;
+
+      esp_aad_fill (aad, esp0, sa0);
+
+      /*
+       * we don't need to refer to the ESP header anymore so we
+       * can overwrite it with the salt and use the IV where it is
+       * to form the nonce = (Salt + IV)
+       */
+      iv -= sizeof (sa0->salt);
+      clib_memcpy_fast (iv, &sa0->salt, sizeof (sa0->salt));
+
+      tag = payload + len;
+    }
+
+  crypto_start_offset = (payload += iv_sz) - b->data;
+  crypto_len = len - iv_sz;
+
+  if (pd->is_chain && (pd2->lb != b))
+    {
+      /* buffer is chained */
+      flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
+
+      crypto_len = esp_decrypt_chain_crypto (vm, ptd, pd2, sa0, b, icv_sz,
+                                            payload,
+                                            len - pd->iv_sz + pd->icv_sz,
+                                            &tag, 0);
+    }
+
+  *async_pd = *pd;
+  *async_pd2 = *pd2;
+  pd->protect_index = current_protect_index;
+  next[0] = ESP_DECRYPT_NEXT_PENDING;
+
+  /* for AEAD integ_len - crypto_len will be negative, it is ok since it
+   * is ignored by the engine. */
+  return vnet_crypto_async_add_to_frame (vm, f, key_index, crypto_len,
+                                        integ_len - crypto_len,
+                                        crypto_start_offset,
+                                        integ_start_offset,
+                                        bi, async_next, iv, tag, aad, flags);
+}
+
+static_always_inline void
+esp_decrypt_post_crypto (vlib_main_t * vm, vlib_node_runtime_t * node,
+                        esp_decrypt_packet_data_t * pd,
+                        esp_decrypt_packet_data2_t * pd2, vlib_buffer_t * b,
+                        u16 * next, int is_ip6, int is_tun, int is_async)
+{
+  ipsec_main_t *im = &ipsec_main;
+  ipsec_sa_t *sa0 = vec_elt_at_index (im->sad, pd->sa_index);
+  vlib_buffer_t *lb = b;
+  const u8 esp_sz = sizeof (esp_header_t);
+  const u8 tun_flags = IPSEC_SA_FLAG_IS_TUNNEL | IPSEC_SA_FLAG_IS_TUNNEL_V6;
+  u8 pad_length = 0, next_header = 0;
+  u16 icv_sz;
+
+  /*
+   * redo the anti-reply check
+   * in this frame say we have sequence numbers, s, s+1, s+1, s+1
+   * and s and s+1 are in the window. When we did the anti-replay
+   * check above we did so against the state of the window (W),
+   * after packet s-1. So each of the packets in the sequence will be
+   * accepted.
+   * This time s will be cheked against Ws-1, s+1 chceked against Ws
+   * (i.e. the window state is updated/advnaced)
+   * so this time the successive s+! packet will be dropped.
+   * This is a consequence of batching the decrypts. If the
+   * check-dcrypt-advance process was done for each packet it would
+   * be fine. But we batch the decrypts because it's much more efficient
+   * to do so in SW and if we offload to HW and the process is async.
+   *
+   * You're probably thinking, but this means an attacker can send the
+   * above sequence and cause VPP to perform decrpyts that will fail,
+   * and that's true. But if the attacker can determine s (a valid
+   * sequence number in the window) which is non-trivial, it can generate
+   * a sequence s, s+1, s+2, s+3, ... s+n and nothing will prevent any
+   * implementation, sequential or batching, from decrypting these.
+   */
+  if (ipsec_sa_anti_replay_check (sa0, pd->seq))
+    {
+      b->error = node->errors[ESP_DECRYPT_ERROR_REPLAY];
+      next[0] = ESP_DECRYPT_NEXT_DROP;
+      return;
+    }
+
+  ipsec_sa_anti_replay_advance (sa0, pd->seq);
+
+  if (pd->is_chain)
+    {
+      lb = pd2->lb;
+      icv_sz = pd2->icv_removed ? 0 : pd->icv_sz;
+      if (pd2->free_buffer_index)
+       {
+         vlib_buffer_free_one (vm, pd2->free_buffer_index);
+         lb->next_buffer = 0;
+       }
+      if (lb->current_length < sizeof (esp_footer_t) + icv_sz)
+       {
+         /* esp footer is either splitted in two buffers or in the before
+          * last buffer */
+
+         vlib_buffer_t *before_last = b, *bp = b;
+         while (bp->flags & VLIB_BUFFER_NEXT_PRESENT)
+           {
+             before_last = bp;
+             bp = vlib_get_buffer (vm, bp->next_buffer);
+           }
+         u8 *bt = vlib_buffer_get_tail (before_last);
+
+         if (lb->current_length == icv_sz)
+           {
+             esp_footer_t *f = (esp_footer_t *) (bt - sizeof (*f));
+             pad_length = f->pad_length;
+             next_header = f->next_header;
+           }
+         else
+           {
+             pad_length = (bt - 1)[0];
+             next_header = ((u8 *) vlib_buffer_get_current (lb))[0];
+           }
+       }
+      else
+       {
+         esp_footer_t *f =
+           (esp_footer_t *) (lb->data + lb->current_data +
+                             lb->current_length - sizeof (esp_footer_t) -
+                             icv_sz);
+         pad_length = f->pad_length;
+         next_header = f->next_header;
+       }
+    }
+  else
+    {
+      icv_sz = pd->icv_sz;
+      esp_footer_t *f =
+       (esp_footer_t *) (lb->data + lb->current_data + lb->current_length -
+                         sizeof (esp_footer_t) - icv_sz);
+      pad_length = f->pad_length;
+      next_header = f->next_header;
+    }
+
+  u16 adv = pd->iv_sz + esp_sz;
+  u16 tail = sizeof (esp_footer_t) + pad_length + icv_sz;
+  u16 tail_orig = sizeof (esp_footer_t) + pad_length + pd->icv_sz;
+  b->flags &= ~VLIB_BUFFER_TOTAL_LENGTH_VALID;
+
+  if ((pd->flags & tun_flags) == 0 && !is_tun) /* transport mode */
+    {
+      u8 udp_sz = (is_ip6 == 0 && pd->flags & IPSEC_SA_FLAG_UDP_ENCAP) ?
+       sizeof (udp_header_t) : 0;
+      u16 ip_hdr_sz = pd->hdr_sz - udp_sz;
+      u8 *old_ip = b->data + pd->current_data - ip_hdr_sz - udp_sz;
+      u8 *ip = old_ip + adv + udp_sz;
+
+      if (is_ip6 && ip_hdr_sz > 64)
+       memmove (ip, old_ip, ip_hdr_sz);
+      else
+       clib_memcpy_le64 (ip, old_ip, ip_hdr_sz);
+
+      b->current_data = pd->current_data + adv - ip_hdr_sz;
+      b->current_length += ip_hdr_sz - adv;
+      esp_remove_tail (vm, b, lb, tail);
+
+      if (is_ip6)
+       {
+         ip6_header_t *ip6 = (ip6_header_t *) ip;
+         u16 len = clib_net_to_host_u16 (ip6->payload_length);
+         len -= adv + tail_orig;
+         ip6->payload_length = clib_host_to_net_u16 (len);
+         ip6->protocol = next_header;
+         next[0] = ESP_DECRYPT_NEXT_IP6_INPUT;
+       }
+      else
+       {
+         ip4_header_t *ip4 = (ip4_header_t *) ip;
+         ip_csum_t sum = ip4->checksum;
+         u16 len = clib_net_to_host_u16 (ip4->length);
+         len = clib_host_to_net_u16 (len - adv - tail_orig - udp_sz);
+         sum = ip_csum_update (sum, ip4->protocol, next_header,
+                               ip4_header_t, protocol);
+         sum = ip_csum_update (sum, ip4->length, len, ip4_header_t, length);
+         ip4->checksum = ip_csum_fold (sum);
+         ip4->protocol = next_header;
+         ip4->length = len;
+         next[0] = ESP_DECRYPT_NEXT_IP4_INPUT;
+       }
+    }
+  else
+    {
+      if (PREDICT_TRUE (next_header == IP_PROTOCOL_IP_IN_IP))
+       {
+         next[0] = ESP_DECRYPT_NEXT_IP4_INPUT;
+         b->current_data = pd->current_data + adv;
+         b->current_length = pd->current_length - adv;
+         esp_remove_tail (vm, b, lb, tail);
+       }
+      else if (next_header == IP_PROTOCOL_IPV6)
+       {
+         next[0] = ESP_DECRYPT_NEXT_IP6_INPUT;
+         b->current_data = pd->current_data + adv;
+         b->current_length = pd->current_length - adv;
+         esp_remove_tail (vm, b, lb, tail);
+       }
+      else
+       {
+         if (is_tun && next_header == IP_PROTOCOL_GRE)
+           {
+             gre_header_t *gre;
+
+             b->current_data = pd->current_data + adv;
+             b->current_length = pd->current_length - adv - tail;
+
+             gre = vlib_buffer_get_current (b);
+
+             vlib_buffer_advance (b, sizeof (*gre));
+
+             switch (clib_net_to_host_u16 (gre->protocol))
+               {
+               case GRE_PROTOCOL_teb:
+                 vnet_update_l2_len (b);
+                 next[0] = ESP_DECRYPT_NEXT_L2_INPUT;
+                 break;
+               case GRE_PROTOCOL_ip4:
+                 next[0] = ESP_DECRYPT_NEXT_IP4_INPUT;
+                 break;
+               case GRE_PROTOCOL_ip6:
+                 next[0] = ESP_DECRYPT_NEXT_IP6_INPUT;
+                 break;
+               default:
+                 b->error = node->errors[ESP_DECRYPT_ERROR_UNSUP_PAYLOAD];
+                 next[0] = ESP_DECRYPT_NEXT_DROP;
+                 break;
+               }
+           }
+         else
+           {
+             next[0] = ESP_DECRYPT_NEXT_DROP;
+             b->error = node->errors[ESP_DECRYPT_ERROR_UNSUP_PAYLOAD];
+             return;
+           }
+       }
+      if (is_tun)
+       {
+         if (ipsec_sa_is_set_IS_PROTECT (sa0))
+           {
+             /*
+              * There are two encap possibilities
+              * 1) the tunnel and ths SA are prodiving encap, i.e. it's
+              *   MAC | SA-IP | TUN-IP | ESP | PAYLOAD
+              * implying the SA is in tunnel mode (on a tunnel interface)
+              * 2) only the tunnel provides encap
+              *   MAC | TUN-IP | ESP | PAYLOAD
+              * implying the SA is in transport mode.
+              *
+              * For 2) we need only strip the tunnel encap and we're good.
+              *  since the tunnel and crypto ecnap (int the tun=protect
+              * object) are the same and we verified above that these match
+              * for 1) we need to strip the SA-IP outer headers, to
+              * reveal the tunnel IP and then check that this matches
+              * the configured tunnel.
+              */
+             const ipsec_tun_protect_t *itp;
+
+             if (is_async)
+               itp = ipsec_tun_protect_get (pd->protect_index);
+             else
+               itp =
+                 ipsec_tun_protect_get (vnet_buffer (b)->
+                                        ipsec.protect_index);
+
+             if (PREDICT_TRUE (next_header == IP_PROTOCOL_IP_IN_IP))
+               {
+                 const ip4_header_t *ip4;
+
+                 ip4 = vlib_buffer_get_current (b);
+
+                 if (!ip46_address_is_equal_v4 (&itp->itp_tun.src,
+                                                &ip4->dst_address) ||
+                     !ip46_address_is_equal_v4 (&itp->itp_tun.dst,
+                                                &ip4->src_address))
+                   {
+                     next[0] = ESP_DECRYPT_NEXT_DROP;
+                     b->error = node->errors[ESP_DECRYPT_ERROR_TUN_NO_PROTO];
+                   }
+               }
+             else if (next_header == IP_PROTOCOL_IPV6)
+               {
+                 const ip6_header_t *ip6;
+
+                 ip6 = vlib_buffer_get_current (b);
+
+                 if (!ip46_address_is_equal_v6 (&itp->itp_tun.src,
+                                                &ip6->dst_address) ||
+                     !ip46_address_is_equal_v6 (&itp->itp_tun.dst,
+                                                &ip6->src_address))
+                   {
+                     next[0] = ESP_DECRYPT_NEXT_DROP;
+                     b->error = node->errors[ESP_DECRYPT_ERROR_TUN_NO_PROTO];
+                   }
+               }
+           }
+       }
+    }
+}
+
+/* when submitting a frame is failed, drop all buffers in the frame */
+static_always_inline void
+esp_async_recycle_failed_submit (vnet_crypto_async_frame_t * f,
+                                vlib_buffer_t ** b, u16 * next)
+{
+  u32 n_drop = f->n_elts;
+  while (--n_drop)
+    {
+      (b - n_drop)[0]->error = ESP_DECRYPT_ERROR_CRYPTO_ENGINE_ERROR;
+      (next - n_drop)[0] = ESP_DECRYPT_NEXT_DROP;
+    }
+  vnet_crypto_async_reset_frame (f);
+}
+
 always_inline uword
 esp_decrypt_inline (vlib_main_t * vm,
                    vlib_node_runtime_t * node, vlib_frame_t * from_frame,
-                   int is_ip6, int is_tun)
+                   int is_ip6, int is_tun, u16 async_next)
 {
   ipsec_main_t *im = &ipsec_main;
   u32 thread_index = vm->thread_index;
@@ -325,20 +1040,26 @@ esp_decrypt_inline (vlib_main_t * vm,
   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
   u16 nexts[VLIB_FRAME_SIZE], *next = nexts;
   esp_decrypt_packet_data_t pkt_data[VLIB_FRAME_SIZE], *pd = pkt_data;
+  esp_decrypt_packet_data2_t pkt_data2[VLIB_FRAME_SIZE], *pd2 = pkt_data2;
   esp_decrypt_packet_data_t cpd = { };
   u32 current_sa_index = ~0, current_sa_bytes = 0, current_sa_pkts = 0;
   const u8 esp_sz = sizeof (esp_header_t);
   ipsec_sa_t *sa0 = 0;
   vnet_crypto_op_t _op, *op = &_op;
-  vnet_crypto_op_chunk_t *ch;
   vnet_crypto_op_t **crypto_ops = &ptd->crypto_ops;
   vnet_crypto_op_t **integ_ops = &ptd->integ_ops;
+  vnet_crypto_async_frame_t *async_frame = 0;
+  int is_async = im->async_mode;
+  vnet_crypto_async_op_id_t last_async_op = ~0;
 
   vlib_get_buffers (vm, from, b, n_left);
-  vec_reset_length (ptd->crypto_ops);
-  vec_reset_length (ptd->integ_ops);
-  vec_reset_length (ptd->chained_crypto_ops);
-  vec_reset_length (ptd->chained_integ_ops);
+  if (!is_async)
+    {
+      vec_reset_length (ptd->crypto_ops);
+      vec_reset_length (ptd->integ_ops);
+      vec_reset_length (ptd->chained_crypto_ops);
+      vec_reset_length (ptd->chained_integ_ops);
+    }
   vec_reset_length (ptd->chunks);
   clib_memset_u16 (nexts, -1, n_left);
 
@@ -379,6 +1100,19 @@ esp_decrypt_inline (vlib_main_t * vm,
          cpd.iv_sz = sa0->crypto_iv_size;
          cpd.flags = sa0->flags;
          cpd.sa_index = current_sa_index;
+
+         /* submit frame when op_id is different then the old one */
+         if (is_async && last_async_op != sa0->crypto_async_dec_op_id)
+           {
+             if (async_frame && async_frame->n_elts)
+               {
+                 if (vnet_crypto_async_submit_open_frame (vm, async_frame))
+                   esp_async_recycle_failed_submit (async_frame, b, next);
+               }
+             async_frame =
+               vnet_crypto_async_get_frame (vm, sa0->crypto_async_dec_op_id);
+             last_async_op = sa0->crypto_async_dec_op_id;
+           }
        }
 
       if (PREDICT_FALSE (~0 == sa0->decrypt_thread_index))
@@ -402,19 +1136,22 @@ esp_decrypt_inline (vlib_main_t * vm,
       pd->hdr_sz = pd->current_data - vnet_buffer (b[0])->l3_hdr_offset;
       payload = b[0]->data + pd->current_data;
       pd->seq = clib_host_to_net_u32 (((esp_header_t *) payload)->seq);
-      pd->free_buffer_index = 0;
-      pd->icv_removed = 0;
+      pd->is_chain = 0;
+      pd2->lb = b[0];
+      pd2->free_buffer_index = 0;
+      pd2->icv_removed = 0;
 
-      pd->lb = b[0];
       if (n_bufs > 1)
        {
+         pd->is_chain = 1;
          /* find last buffer in the chain */
-         while (pd->lb->flags & VLIB_BUFFER_NEXT_PRESENT)
-           pd->lb = vlib_get_buffer (vm, pd->lb->next_buffer);
+         while (pd2->lb->flags & VLIB_BUFFER_NEXT_PRESENT)
+           pd2->lb = vlib_get_buffer (vm, pd2->lb->next_buffer);
 
          crypto_ops = &ptd->chained_crypto_ops;
          integ_ops = &ptd->chained_integ_ops;
        }
+
       pd->current_length = b[0]->current_length;
 
       /* anti-reply check */
@@ -436,260 +1173,33 @@ esp_decrypt_inline (vlib_main_t * vm,
       current_sa_pkts += 1;
       current_sa_bytes += vlib_buffer_length_in_chain (vm, b[0]);
 
-      if (PREDICT_TRUE (sa0->integ_op_id != VNET_CRYPTO_OP_NONE))
+      if (is_async)
        {
-         vnet_crypto_op_init (op, sa0->integ_op_id);
-         op->key_index = sa0->integ_key_index;
-         op->src = payload;
-         op->flags = VNET_CRYPTO_OP_FLAG_HMAC_CHECK;
-         op->user_data = b - bufs;
-         op->digest = payload + len;
-         op->digest_len = cpd.icv_sz;
-         op->len = len;
-
-         if (pd->lb != b[0])
+         int ret = esp_decrypt_prepare_async_frame (vm, node, ptd,
+                                                    &async_frame,
+                                                    sa0, payload, len,
+                                                    cpd.icv_sz,
+                                                    cpd.iv_sz,
+                                                    pd, pd2,
+                                                    from[b - bufs],
+                                                    b[0], next, async_next);
+         if (PREDICT_FALSE (ret < 0))
            {
-             /* buffer is chained */
-             vlib_buffer_t *cb = b[0];
-             op->len = pd->current_length;
-
-             /* special case when ICV is splitted and needs to be reassembled
-              * first -> move it to the last buffer. Also take into account
-              * that ESN needs to be added after encrypted data and may or
-              * may not fit in the tail.*/
-             if (pd->lb->current_length < cpd.icv_sz)
-               {
-                 u8 extra_esn = 0;
-                 op->digest =
-                   esp_move_icv_esn (vm, b[0], pd, cpd.icv_sz, sa0,
-                                     &extra_esn, op);
-
-                 if (extra_esn)
-                   {
-                     /* esn is in the last buffer, that was unlinked from
-                      * the chain */
-                     op->len = b[0]->current_length;
-                   }
-                 else
-                   {
-                     if (pd->lb == b[0])
-                       {
-                         /* we now have a single buffer of crypto data, adjust
-                          * the length (second buffer contains only ICV) */
-                         integ_ops = &ptd->integ_ops;
-                         crypto_ops = &ptd->crypto_ops;
-                         len = b[0]->current_length;
-                         goto out;
-                       }
-                   }
-               }
-             else
-               op->digest = vlib_buffer_get_tail (pd->lb) - cpd.icv_sz;
-
-             op->flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
-             op->chunk_index = vec_len (ptd->chunks);
-             vec_add2 (ptd->chunks, ch, 1);
-             ch->len = pd->current_length;
-             ch->src = payload;
-             cb = vlib_get_buffer (vm, cb->next_buffer);
-             op->n_chunks = 1;
-             while (1)
-               {
-                 vec_add2 (ptd->chunks, ch, 1);
-                 op->n_chunks += 1;
-                 ch->src = vlib_buffer_get_current (cb);
-                 if (pd->lb == cb)
-                   {
-                     if (pd->icv_removed)
-                       ch->len = cb->current_length;
-                     else
-                       ch->len = cb->current_length - cpd.icv_sz;
-                     if (ipsec_sa_is_set_USE_ESN (sa0))
-                       {
-                         u32 seq_hi = clib_host_to_net_u32 (sa0->seq_hi);
-                         u8 tmp[ESP_MAX_ICV_SIZE], sz = sizeof (sa0->seq_hi);
-                         u8 *esn;
-                         vlib_buffer_t *tmp_b;
-                         u16 space_left = vlib_buffer_space_left_at_end
-                           (vm, pd->lb);
-                         if (space_left < sz)
-                           {
-                             if (pd->icv_removed)
-                               {
-                                 /* use pre-data area from the last bufer
-                                    that was removed from the chain */
-                                 tmp_b =
-                                   vlib_get_buffer (vm,
-                                                    pd->free_buffer_index);
-                                 esn = tmp_b->data - sz;
-                               }
-                             else
-                               {
-                                 /* no space, need to allocate new buffer */
-                                 u32 tmp_bi = 0;
-                                 if (1 != vlib_buffer_alloc (vm, &tmp_bi, 1))
-                                   {
-                                     b[0]->error = node->errors
-                                       [ESP_DECRYPT_ERROR_NO_BUFFERS];
-                                     next[0] = ESP_DECRYPT_NEXT_DROP;
-                                     goto next;
-                                   }
-                                 tmp_b = vlib_get_buffer (vm, tmp_bi);
-                                 esn = tmp_b->data;
-                                 pd->free_buffer_index = tmp_bi;
-                               }
-                             clib_memcpy_fast (esn, &seq_hi, sz);
-
-                             vec_add2 (ptd->chunks, ch, 1);
-                             op->n_chunks += 1;
-                             ch->src = esn;
-                             ch->len = sz;
-                           }
-                         else
-                           {
-                             if (pd->icv_removed)
-                               {
-                                 clib_memcpy_fast (vlib_buffer_get_tail
-                                                   (pd->lb), &seq_hi, sz);
-                               }
-                             else
-                               {
-                                 clib_memcpy_fast (tmp, op->digest,
-                                                   ESP_MAX_ICV_SIZE);
-                                 clib_memcpy_fast (op->digest, &seq_hi, sz);
-                                 clib_memcpy_fast (op->digest + sz, tmp,
-                                                   ESP_MAX_ICV_SIZE);
-                                 op->digest += sz;
-                               }
-                             ch->len += sz;
-                           }
-                       }
-                     break;
-                   }
-                 else
-                   ch->len = cb->current_length;
-
-                 if (!(cb->flags & VLIB_BUFFER_NEXT_PRESENT))
-                   break;
-
-                 cb = vlib_get_buffer (vm, cb->next_buffer);
-               }
+             esp_async_recycle_failed_submit (async_frame, b, next);
+             goto next;
            }
-         else
-           esp_insert_esn (vm, sa0, pd, op, &len, b[0], payload);
-       out:
-         vec_add_aligned (integ_ops[0], op, 1, CLIB_CACHE_LINE_BYTES);
        }
-
-      payload += esp_sz;
-      len -= esp_sz;
-
-      if (sa0->crypto_dec_op_id != VNET_CRYPTO_OP_NONE)
-       {
-         vnet_crypto_op_init (op, sa0->crypto_dec_op_id);
-         op->key_index = sa0->crypto_key_index;
-         op->iv = payload;
-
-         if (ipsec_sa_is_set_IS_AEAD (sa0))
-           {
-             esp_header_t *esp0;
-             esp_aead_t *aad;
-             u8 *scratch;
-
-             /*
-              * construct the AAD and the nonce (Salt || IV) in a scratch
-              * space in front of the IP header.
-              */
-             scratch = payload - esp_sz;
-             esp0 = (esp_header_t *) (scratch);
-
-             scratch -= (sizeof (*aad) + pd->hdr_sz);
-             op->aad = scratch;
-
-             esp_aad_fill (op, esp0, sa0);
-
-             /*
-              * we don't need to refer to the ESP header anymore so we
-              * can overwrite it with the salt and use the IV where it is
-              * to form the nonce = (Salt + IV)
-              */
-             op->iv -= sizeof (sa0->salt);
-             clib_memcpy_fast (op->iv, &sa0->salt, sizeof (sa0->salt));
-
-             op->tag = payload + len;
-             op->tag_len = 16;
-           }
-         op->src = op->dst = payload += cpd.iv_sz;
-         op->len = len - cpd.iv_sz;
-         op->user_data = b - bufs;
-
-         if (pd->lb != b[0])
-           {
-             /* buffer is chained */
-             vlib_buffer_t *cb = b[0];
-             op->flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
-             op->chunk_index = vec_len (ptd->chunks);
-             vec_add2 (ptd->chunks, ch, 1);
-             ch->len = len - cpd.iv_sz + cpd.icv_sz;
-             ch->src = ch->dst = payload;
-             cb = vlib_get_buffer (vm, cb->next_buffer);
-             op->n_chunks = 1;
-
-             while (1)
-               {
-                 vec_add2 (ptd->chunks, ch, 1);
-                 op->n_chunks += 1;
-                 ch->src = ch->dst = vlib_buffer_get_current (cb);
-                 if (pd->lb == cb)
-                   {
-                     if (ipsec_sa_is_set_IS_AEAD (sa0))
-                       {
-                         if (pd->lb->current_length < cpd.icv_sz)
-                           {
-                             u16 dif = 0;
-                             op->tag =
-                               esp_move_icv (vm, b[0], pd, cpd.icv_sz, &dif);
-
-                             /* this chunk does not contain crypto data */
-                             op->n_chunks -= 1;
-
-                             /* and fix previous chunk's length as it might have
-                                been changed */
-                             ASSERT (op->n_chunks > 0);
-                             if (pd->lb == b[0])
-                               ch[-1].len -= dif;
-                             else
-                               ch[-1].len = pd->lb->current_length;
-                             break;
-                           }
-                         else
-                           op->tag =
-                             vlib_buffer_get_tail (pd->lb) - cpd.icv_sz;
-                       }
-
-                     if (pd->icv_removed)
-                       ch->len = cb->current_length;
-                     else
-                       ch->len = cb->current_length - cpd.icv_sz;
-                   }
-                 else
-                   ch->len = cb->current_length;
-
-                 if (!(cb->flags & VLIB_BUFFER_NEXT_PRESENT))
-                   break;
-
-                 cb = vlib_get_buffer (vm, cb->next_buffer);
-               }
-           }
-
-         vec_add_aligned (crypto_ops[0], op, 1, CLIB_CACHE_LINE_BYTES);
-       }
-
+      else
+       esp_decrypt_prepare_sync_op (vm, node, ptd, &crypto_ops, &integ_ops,
+                                    op, sa0, payload, len, cpd.icv_sz,
+                                    cpd.iv_sz, pd, pd2, b[0], next,
+                                    b - bufs);
       /* next */
     next:
       n_left -= 1;
       next += 1;
       pd += 1;
+      pd2 += 1;
       b += 1;
     }
 
@@ -698,15 +1208,35 @@ esp_decrypt_inline (vlib_main_t * vm,
                                     current_sa_index, current_sa_pkts,
                                     current_sa_bytes);
 
-  esp_process_ops (vm, node, ptd->integ_ops, bufs, nexts,
-                  ESP_DECRYPT_ERROR_INTEG_ERROR);
-  esp_process_chained_ops (vm, node, ptd->chained_integ_ops, bufs, nexts,
-                          ptd->chunks, ESP_DECRYPT_ERROR_INTEG_ERROR);
+  if (is_async)
+    {
+      if (async_frame && async_frame->n_elts)
+       {
+         if (vnet_crypto_async_submit_open_frame (vm, async_frame) < 0)
+           esp_async_recycle_failed_submit (async_frame, b, next);
+       }
+
+      /* no post process in async */
+      n_left = from_frame->n_vectors;
+      vlib_node_increment_counter (vm, node->node_index,
+                                  ESP_DECRYPT_ERROR_RX_PKTS, n_left);
+      vlib_buffer_enqueue_to_next (vm, node, from, nexts, n_left);
 
-  esp_process_ops (vm, node, ptd->crypto_ops, bufs, nexts,
-                  ESP_DECRYPT_ERROR_DECRYPTION_FAILED);
-  esp_process_chained_ops (vm, node, ptd->chained_crypto_ops, bufs, nexts,
-                          ptd->chunks, ESP_DECRYPT_ERROR_DECRYPTION_FAILED);
+      return n_left;
+    }
+  else
+    {
+      esp_process_ops (vm, node, ptd->integ_ops, bufs, nexts,
+                      ESP_DECRYPT_ERROR_INTEG_ERROR);
+      esp_process_chained_ops (vm, node, ptd->chained_integ_ops, bufs, nexts,
+                              ptd->chunks, ESP_DECRYPT_ERROR_INTEG_ERROR);
+
+      esp_process_ops (vm, node, ptd->crypto_ops, bufs, nexts,
+                      ESP_DECRYPT_ERROR_DECRYPTION_FAILED);
+      esp_process_chained_ops (vm, node, ptd->chained_crypto_ops, bufs, nexts,
+                              ptd->chunks,
+                              ESP_DECRYPT_ERROR_DECRYPTION_FAILED);
+    }
 
   /* Post decryption ronud - adjust packet data start and length and next
      node */
@@ -714,13 +1244,11 @@ esp_decrypt_inline (vlib_main_t * vm,
   n_left = from_frame->n_vectors;
   next = nexts;
   pd = pkt_data;
+  pd2 = pkt_data2;
   b = bufs;
 
   while (n_left)
     {
-      const u8 tun_flags = IPSEC_SA_FLAG_IS_TUNNEL |
-       IPSEC_SA_FLAG_IS_TUNNEL_V6;
-
       if (n_left >= 2)
        {
          void *data = b[1]->data + pd[1].current_data;
@@ -737,253 +1265,83 @@ esp_decrypt_inline (vlib_main_t * vm,
                         CLIB_CACHE_LINE_BYTES * 2, LOAD);
        }
 
-      if (next[0] < ESP_DECRYPT_N_NEXT)
-       goto trace;
-
-      sa0 = vec_elt_at_index (im->sad, pd->sa_index);
+      if (next[0] >= ESP_DECRYPT_N_NEXT)
+       esp_decrypt_post_crypto (vm, node, pd, pd2, b[0], next, is_ip6,
+                                is_tun, 0);
 
-      /*
-       * redo the anti-reply check
-       * in this frame say we have sequence numbers, s, s+1, s+1, s+1
-       * and s and s+1 are in the window. When we did the anti-replay
-       * check above we did so against the state of the window (W),
-       * after packet s-1. So each of the packets in the sequence will be
-       * accepted.
-       * This time s will be cheked against Ws-1, s+1 chceked against Ws
-       * (i.e. the window state is updated/advnaced)
-       * so this time the successive s+! packet will be dropped.
-       * This is a consequence of batching the decrypts. If the
-       * check-dcrypt-advance process was done for each packet it would
-       * be fine. But we batch the decrypts because it's much more efficient
-       * to do so in SW and if we offload to HW and the process is async.
-       *
-       * You're probably thinking, but this means an attacker can send the
-       * above sequence and cause VPP to perform decrpyts that will fail,
-       * and that's true. But if the attacker can determine s (a valid
-       * sequence number in the window) which is non-trivial, it can generate
-       * a sequence s, s+1, s+2, s+3, ... s+n and nothing will prevent any
-       * implementation, sequential or batching, from decrypting these.
-       */
-      if (ipsec_sa_anti_replay_check (sa0, pd->seq))
+      /* trace: */
+      if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
        {
-         b[0]->error = node->errors[ESP_DECRYPT_ERROR_REPLAY];
-         next[0] = ESP_DECRYPT_NEXT_DROP;
-         goto trace;
+         esp_decrypt_trace_t *tr;
+         tr = vlib_add_trace (vm, node, b[0], sizeof (*tr));
+         sa0 = pool_elt_at_index (im->sad,
+                                  vnet_buffer (b[0])->ipsec.sad_index);
+         tr->crypto_alg = sa0->crypto_alg;
+         tr->integ_alg = sa0->integ_alg;
+         tr->seq = pd->seq;
+         tr->sa_seq = sa0->last_seq;
+         tr->sa_seq_hi = sa0->seq_hi;
        }
 
-      ipsec_sa_anti_replay_advance (sa0, pd->seq);
-
-      u8 pad_length = 0, next_header = 0;
-      u16 icv_sz = pd->icv_removed ? 0 : pd->icv_sz;
+      /* next */
+      n_left -= 1;
+      next += 1;
+      pd += 1;
+      pd2 += 1;
+      b += 1;
+    }
 
-      if (pd->free_buffer_index)
-       vlib_buffer_free_one (vm, pd->free_buffer_index);
+  n_left = from_frame->n_vectors;
+  vlib_node_increment_counter (vm, node->node_index,
+                              ESP_DECRYPT_ERROR_RX_PKTS, n_left);
 
-      if (pd->lb->current_length < sizeof (esp_footer_t) + icv_sz)
-       {
-         /* esp footer is either splitted in two buffers or in the before
-          * last buffer */
+  vlib_buffer_enqueue_to_next (vm, node, from, nexts, n_left);
 
-         vlib_buffer_t *before_last = b[0], *bp = b[0];
-         while (bp->flags & VLIB_BUFFER_NEXT_PRESENT)
-           {
-             before_last = bp;
-             bp = vlib_get_buffer (vm, bp->next_buffer);
-           }
-         u8 *bt = vlib_buffer_get_tail (before_last);
+  return n_left;
+}
 
-         if (pd->lb->current_length == icv_sz)
-           {
-             esp_footer_t *f = (esp_footer_t *) (bt - sizeof (*f));
-             pad_length = f->pad_length;
-             next_header = f->next_header;
-           }
-         else
-           {
-             pad_length = (bt - 1)[0];
-             next_header = ((u8 *) vlib_buffer_get_current (pd->lb))[0];
-           }
-       }
-      else
-       {
-         esp_footer_t *f =
-           (esp_footer_t *) (pd->lb->data + pd->lb->current_data +
-                             pd->lb->current_length - sizeof (esp_footer_t) -
-                             icv_sz);
-         pad_length = f->pad_length;
-         next_header = f->next_header;
-       }
+always_inline uword
+esp_decrypt_post_inline (vlib_main_t * vm,
+                        vlib_node_runtime_t * node,
+                        vlib_frame_t * from_frame, int is_ip6, int is_tun)
+{
+  ipsec_main_t *im = &ipsec_main;
+  u32 *from = vlib_frame_vector_args (from_frame);
+  u32 n_left = from_frame->n_vectors;
+  vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
+  u16 nexts[VLIB_FRAME_SIZE], *next = nexts;
+  vlib_get_buffers (vm, from, b, n_left);
 
-      u16 adv = pd->iv_sz + esp_sz;
-      u16 tail = sizeof (esp_footer_t) + pad_length + icv_sz;
-      u16 tail_orig = sizeof (esp_footer_t) + pad_length + pd->icv_sz;
-      b[0]->flags &= ~VLIB_BUFFER_TOTAL_LENGTH_VALID;
+  while (n_left > 0)
+    {
+      esp_decrypt_packet_data_t *pd = &(esp_post_data (b[0]))->decrypt_data;
 
-      if ((pd->flags & tun_flags) == 0 && !is_tun)     /* transport mode */
+      if (n_left > 2)
        {
-         u8 udp_sz = (is_ip6 == 0 && pd->flags & IPSEC_SA_FLAG_UDP_ENCAP) ?
-           sizeof (udp_header_t) : 0;
-         u16 ip_hdr_sz = pd->hdr_sz - udp_sz;
-         u8 *old_ip = b[0]->data + pd->current_data - ip_hdr_sz - udp_sz;
-         u8 *ip = old_ip + adv + udp_sz;
-
-         if (is_ip6 && ip_hdr_sz > 64)
-           memmove (ip, old_ip, ip_hdr_sz);
-         else
-           clib_memcpy_le64 (ip, old_ip, ip_hdr_sz);
-
-         b[0]->current_data = pd->current_data + adv - ip_hdr_sz;
-         b[0]->current_length += ip_hdr_sz - adv;
-         esp_remove_tail (vm, b[0], pd->lb, tail);
-
-         if (is_ip6)
-           {
-             ip6_header_t *ip6 = (ip6_header_t *) ip;
-             u16 len = clib_net_to_host_u16 (ip6->payload_length);
-             len -= adv + tail_orig;
-             ip6->payload_length = clib_host_to_net_u16 (len);
-             ip6->protocol = next_header;
-             next[0] = ESP_DECRYPT_NEXT_IP6_INPUT;
-           }
-         else
-           {
-             ip4_header_t *ip4 = (ip4_header_t *) ip;
-             ip_csum_t sum = ip4->checksum;
-             u16 len = clib_net_to_host_u16 (ip4->length);
-             len = clib_host_to_net_u16 (len - adv - tail_orig - udp_sz);
-             sum = ip_csum_update (sum, ip4->protocol, next_header,
-                                   ip4_header_t, protocol);
-             sum = ip_csum_update (sum, ip4->length, len,
-                                   ip4_header_t, length);
-             ip4->checksum = ip_csum_fold (sum);
-             ip4->protocol = next_header;
-             ip4->length = len;
-             next[0] = ESP_DECRYPT_NEXT_IP4_INPUT;
-           }
+         vlib_prefetch_buffer_header (b[2], LOAD);
+         vlib_prefetch_buffer_header (b[1], LOAD);
        }
+
+      if (!pd->is_chain)
+       esp_decrypt_post_crypto (vm, node, pd, 0, b[0], next, is_ip6, is_tun,
+                                1);
       else
        {
-         if (PREDICT_TRUE (next_header == IP_PROTOCOL_IP_IN_IP))
-           {
-             next[0] = ESP_DECRYPT_NEXT_IP4_INPUT;
-             b[0]->current_data = pd->current_data + adv;
-             b[0]->current_length = pd->current_length - adv;
-             esp_remove_tail (vm, b[0], pd->lb, tail);
-           }
-         else if (next_header == IP_PROTOCOL_IPV6)
-           {
-             next[0] = ESP_DECRYPT_NEXT_IP6_INPUT;
-             b[0]->current_data = pd->current_data + adv;
-             b[0]->current_length = pd->current_length - adv;
-             esp_remove_tail (vm, b[0], pd->lb, tail);
-           }
-         else
-           {
-             if (is_tun && next_header == IP_PROTOCOL_GRE)
-               {
-                 gre_header_t *gre;
-
-                 b[0]->current_data = pd->current_data + adv;
-                 b[0]->current_length = pd->current_length - adv - tail;
-
-                 gre = vlib_buffer_get_current (b[0]);
-
-                 vlib_buffer_advance (b[0], sizeof (*gre));
-
-                 switch (clib_net_to_host_u16 (gre->protocol))
-                   {
-                   case GRE_PROTOCOL_teb:
-                     vnet_update_l2_len (b[0]);
-                     next[0] = ESP_DECRYPT_NEXT_L2_INPUT;
-                     break;
-                   case GRE_PROTOCOL_ip4:
-                     next[0] = ESP_DECRYPT_NEXT_IP4_INPUT;
-                     break;
-                   case GRE_PROTOCOL_ip6:
-                     next[0] = ESP_DECRYPT_NEXT_IP6_INPUT;
-                     break;
-                   default:
-                     b[0]->error =
-                       node->errors[ESP_DECRYPT_ERROR_UNSUP_PAYLOAD];
-                     next[0] = ESP_DECRYPT_NEXT_DROP;
-                     break;
-                   }
-               }
-             else
-               {
-                 next[0] = ESP_DECRYPT_NEXT_DROP;
-                 b[0]->error = node->errors[ESP_DECRYPT_ERROR_UNSUP_PAYLOAD];
-                 goto trace;
-               }
-           }
-         if (is_tun)
-           {
-             if (ipsec_sa_is_set_IS_PROTECT (sa0))
-               {
-                 /*
-                  * There are two encap possibilities
-                  * 1) the tunnel and ths SA are prodiving encap, i.e. it's
-                  *   MAC | SA-IP | TUN-IP | ESP | PAYLOAD
-                  * implying the SA is in tunnel mode (on a tunnel interface)
-                  * 2) only the tunnel provides encap
-                  *   MAC | TUN-IP | ESP | PAYLOAD
-                  * implying the SA is in transport mode.
-                  *
-                  * For 2) we need only strip the tunnel encap and we're good.
-                  *  since the tunnel and crypto ecnap (int the tun=protect
-                  * object) are the same and we verified above that these match
-                  * for 1) we need to strip the SA-IP outer headers, to
-                  * reveal the tunnel IP and then check that this matches
-                  * the configured tunnel.
-                  */
-                 const ipsec_tun_protect_t *itp;
-
-                 itp = ipsec_tun_protect_get
-                   (vnet_buffer (b[0])->ipsec.protect_index);
-
-                 if (PREDICT_TRUE (next_header == IP_PROTOCOL_IP_IN_IP))
-                   {
-                     const ip4_header_t *ip4;
-
-                     ip4 = vlib_buffer_get_current (b[0]);
-
-                     if (!ip46_address_is_equal_v4 (&itp->itp_tun.src,
-                                                    &ip4->dst_address) ||
-                         !ip46_address_is_equal_v4 (&itp->itp_tun.dst,
-                                                    &ip4->src_address))
-                       {
-                         next[0] = ESP_DECRYPT_NEXT_DROP;
-                         b[0]->error =
-                           node->errors[ESP_DECRYPT_ERROR_TUN_NO_PROTO];
-                       }
-                   }
-                 else if (next_header == IP_PROTOCOL_IPV6)
-                   {
-                     const ip6_header_t *ip6;
-
-                     ip6 = vlib_buffer_get_current (b[0]);
-
-                     if (!ip46_address_is_equal_v6 (&itp->itp_tun.src,
-                                                    &ip6->dst_address) ||
-                         !ip46_address_is_equal_v6 (&itp->itp_tun.dst,
-                                                    &ip6->src_address))
-                       {
-                         next[0] = ESP_DECRYPT_NEXT_DROP;
-                         b[0]->error =
-                           node->errors[ESP_DECRYPT_ERROR_TUN_NO_PROTO];
-                       }
-                   }
-               }
-           }
+         esp_decrypt_packet_data2_t *pd2 = esp_post_data2 (b[0]);
+         esp_decrypt_post_crypto (vm, node, pd, pd2, b[0], next, is_ip6,
+                                  is_tun, 1);
        }
 
-    trace:
+      /*trace: */
       if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
        {
+         ipsec_sa_t *sa0 = pool_elt_at_index (im->sad, pd->sa_index);
          esp_decrypt_trace_t *tr;
+         esp_decrypt_packet_data_t *async_pd =
+           &(esp_post_data (b[0]))->decrypt_data;
          tr = vlib_add_trace (vm, node, b[0], sizeof (*tr));
-         sa0 = pool_elt_at_index (im->sad,
-                                  vnet_buffer (b[0])->ipsec.sad_index);
+         sa0 = pool_elt_at_index (im->sad, async_pd->sa_index);
+
          tr->crypto_alg = sa0->crypto_alg;
          tr->integ_alg = sa0->integ_alg;
          tr->seq = pd->seq;
@@ -991,16 +1349,14 @@ esp_decrypt_inline (vlib_main_t * vm,
          tr->sa_seq_hi = sa0->seq_hi;
        }
 
-      /* next */
-      n_left -= 1;
-      next += 1;
-      pd += 1;
-      b += 1;
+      n_left--;
+      next++;
+      b++;
     }
 
   n_left = from_frame->n_vectors;
   vlib_node_increment_counter (vm, node->node_index,
-                              ESP_DECRYPT_ERROR_RX_PKTS, n_left);
+                              ESP_DECRYPT_ERROR_RX_POST_PKTS, n_left);
 
   vlib_buffer_enqueue_to_next (vm, node, from, nexts, n_left);
 
@@ -1011,30 +1367,79 @@ VLIB_NODE_FN (esp4_decrypt_node) (vlib_main_t * vm,
                                  vlib_node_runtime_t * node,
                                  vlib_frame_t * from_frame)
 {
-  return esp_decrypt_inline (vm, node, from_frame, 0, 0);
+  return esp_decrypt_inline (vm, node, from_frame, 0, 0,
+                            esp_decrypt_async_next.esp4_post_next);
+}
+
+VLIB_NODE_FN (esp4_decrypt_post_node) (vlib_main_t * vm,
+                                      vlib_node_runtime_t * node,
+                                      vlib_frame_t * from_frame)
+{
+  return esp_decrypt_post_inline (vm, node, from_frame, 0, 0);
 }
 
 VLIB_NODE_FN (esp4_decrypt_tun_node) (vlib_main_t * vm,
                                      vlib_node_runtime_t * node,
                                      vlib_frame_t * from_frame)
 {
-  return esp_decrypt_inline (vm, node, from_frame, 0, 1);
+  return esp_decrypt_inline (vm, node, from_frame, 0, 1,
+                            esp_decrypt_async_next.esp4_tun_post_next);
+}
+
+VLIB_NODE_FN (esp4_decrypt_tun_post_node) (vlib_main_t * vm,
+                                          vlib_node_runtime_t * node,
+                                          vlib_frame_t * from_frame)
+{
+  return esp_decrypt_post_inline (vm, node, from_frame, 0, 1);
 }
 
 VLIB_NODE_FN (esp6_decrypt_node) (vlib_main_t * vm,
                                  vlib_node_runtime_t * node,
                                  vlib_frame_t * from_frame)
 {
-  return esp_decrypt_inline (vm, node, from_frame, 1, 0);
+  return esp_decrypt_inline (vm, node, from_frame, 1, 0,
+                            esp_decrypt_async_next.esp6_post_next);
+}
+
+VLIB_NODE_FN (esp6_decrypt_post_node) (vlib_main_t * vm,
+                                      vlib_node_runtime_t * node,
+                                      vlib_frame_t * from_frame)
+{
+  return esp_decrypt_post_inline (vm, node, from_frame, 1, 0);
 }
 
 VLIB_NODE_FN (esp6_decrypt_tun_node) (vlib_main_t * vm,
                                      vlib_node_runtime_t * node,
                                      vlib_frame_t * from_frame)
 {
-  return esp_decrypt_inline (vm, node, from_frame, 1, 1);
+  return esp_decrypt_inline (vm, node, from_frame, 1, 1,
+                            esp_decrypt_async_next.esp6_tun_post_next);
+}
+
+VLIB_NODE_FN (esp6_decrypt_tun_post_node) (vlib_main_t * vm,
+                                          vlib_node_runtime_t * node,
+                                          vlib_frame_t * from_frame)
+{
+  return esp_decrypt_post_inline (vm, node, from_frame, 1, 1);
+}
+
+VLIB_NODE_FN (esp_decrypt_pending_node) (vlib_main_t * vm,
+                                        vlib_node_runtime_t * node,
+                                        vlib_frame_t * from_frame)
+{
+  return from_frame->n_vectors;
 }
 
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (esp_decrypt_pending_node) = {
+  .name = "esp-decrypt-pending",
+  .vector_size = sizeof (u32),
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_next_nodes = 0
+};
+/* *INDENT-ON* */
+
 /* *INDENT-OFF* */
 VLIB_REGISTER_NODE (esp4_decrypt_node) = {
   .name = "esp4-decrypt",
@@ -1052,9 +1457,22 @@ VLIB_REGISTER_NODE (esp4_decrypt_node) = {
     [ESP_DECRYPT_NEXT_IP6_INPUT] = "ip6-input",
     [ESP_DECRYPT_NEXT_L2_INPUT] = "l2-input",
     [ESP_DECRYPT_NEXT_HANDOFF] = "esp4-decrypt-handoff",
+    [ESP_DECRYPT_NEXT_PENDING] = "esp-decrypt-pending"
   },
 };
 
+VLIB_REGISTER_NODE (esp4_decrypt_post_node) = {
+  .name = "esp4-decrypt-post",
+  .vector_size = sizeof (u32),
+  .format_trace = format_esp_decrypt_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = ARRAY_LEN(esp_decrypt_error_strings),
+  .error_strings = esp_decrypt_error_strings,
+
+  .sibling_of = "esp4-decrypt",
+};
+
 VLIB_REGISTER_NODE (esp6_decrypt_node) = {
   .name = "esp6-decrypt",
   .vector_size = sizeof (u32),
@@ -1071,9 +1489,22 @@ VLIB_REGISTER_NODE (esp6_decrypt_node) = {
     [ESP_DECRYPT_NEXT_IP6_INPUT] = "ip6-input",
     [ESP_DECRYPT_NEXT_L2_INPUT] = "l2-input",
     [ESP_DECRYPT_NEXT_HANDOFF]=  "esp6-decrypt-handoff",
+    [ESP_DECRYPT_NEXT_PENDING] = "esp-decrypt-pending"
   },
 };
 
+VLIB_REGISTER_NODE (esp6_decrypt_post_node) = {
+  .name = "esp6-decrypt-post",
+  .vector_size = sizeof (u32),
+  .format_trace = format_esp_decrypt_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = ARRAY_LEN(esp_decrypt_error_strings),
+  .error_strings = esp_decrypt_error_strings,
+
+  .sibling_of = "esp6-decrypt",
+};
+
 VLIB_REGISTER_NODE (esp4_decrypt_tun_node) = {
   .name = "esp4-decrypt-tun",
   .vector_size = sizeof (u32),
@@ -1088,9 +1519,22 @@ VLIB_REGISTER_NODE (esp4_decrypt_tun_node) = {
     [ESP_DECRYPT_NEXT_IP6_INPUT] = "ip6-input",
     [ESP_DECRYPT_NEXT_L2_INPUT] = "l2-input",
     [ESP_DECRYPT_NEXT_HANDOFF] = "esp4-decrypt-tun-handoff",
+    [ESP_DECRYPT_NEXT_PENDING] = "esp-decrypt-pending"
   },
 };
 
+VLIB_REGISTER_NODE (esp4_decrypt_tun_post_node) = {
+  .name = "esp4-decrypt-tun-post",
+  .vector_size = sizeof (u32),
+  .format_trace = format_esp_decrypt_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = ARRAY_LEN(esp_decrypt_error_strings),
+  .error_strings = esp_decrypt_error_strings,
+
+  .sibling_of = "esp4-decrypt-tun",
+};
+
 VLIB_REGISTER_NODE (esp6_decrypt_tun_node) = {
   .name = "esp6-decrypt-tun",
   .vector_size = sizeof (u32),
@@ -1105,8 +1549,21 @@ VLIB_REGISTER_NODE (esp6_decrypt_tun_node) = {
     [ESP_DECRYPT_NEXT_IP6_INPUT] = "ip6-input",
     [ESP_DECRYPT_NEXT_L2_INPUT] = "l2-input",
     [ESP_DECRYPT_NEXT_HANDOFF]=  "esp6-decrypt-tun-handoff",
+    [ESP_DECRYPT_NEXT_PENDING] = "esp-decrypt-pending"
   },
 };
+
+VLIB_REGISTER_NODE (esp6_decrypt_tun_post_node) = {
+  .name = "esp6-decrypt-tun-post",
+  .vector_size = sizeof (u32),
+  .format_trace = format_esp_decrypt_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = ARRAY_LEN(esp_decrypt_error_strings),
+  .error_strings = esp_decrypt_error_strings,
+
+  .sibling_of = "esp6-decrypt-tun",
+};
 /* *INDENT-ON* */
 
 /*
index e40d30d..dce887f 100644 (file)
@@ -28,6 +28,7 @@
 
 #define foreach_esp_encrypt_next                   \
 _(DROP, "error-drop")                              \
+_(PENDING, "pending")                              \
 _(HANDOFF, "handoff")                              \
 _(INTERFACE_OUTPUT, "interface-output")
 
@@ -41,8 +42,10 @@ typedef enum
 
 #define foreach_esp_encrypt_error                               \
  _(RX_PKTS, "ESP pkts received")                                \
+ _(POST_RX_PKTS, "ESP-post pkts received")                      \
  _(SEQ_CYCLED, "sequence number cycled (packet dropped)")       \
  _(CRYPTO_ENGINE_ERROR, "crypto engine error (packet dropped)") \
+ _(CRYPTO_QUEUE_FULL, "crypto queue full (packet dropped)")     \
  _(NO_BUFFERS, "no buffers (packet dropped)")                   \
 
 typedef enum
@@ -70,6 +73,11 @@ typedef struct
   ipsec_integ_alg_t integ_alg;
 } esp_encrypt_trace_t;
 
+typedef struct
+{
+  u32 next_index;
+} esp_encrypt_post_trace_t;
+
 /* packet trace format function */
 static u8 *
 format_esp_encrypt_trace (u8 * s, va_list * args)
@@ -88,6 +96,17 @@ format_esp_encrypt_trace (u8 * s, va_list * args)
   return s;
 }
 
+static u8 *
+format_esp_post_encrypt_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  esp_encrypt_post_trace_t *t = va_arg (*args, esp_encrypt_post_trace_t *);
+
+  s = format (s, "esp-post: next node index %u", t->next_index);
+  return s;
+}
+
 /* pad packet in input buffer */
 static_always_inline u8 *
 esp_add_footer_and_icv (vlib_main_t * vm, vlib_buffer_t ** last,
@@ -276,9 +295,281 @@ typedef struct
 
 STATIC_ASSERT_SIZEOF (esp_gcm_nonce_t, 12);
 
+static_always_inline u32
+esp_encrypt_chain_crypto (vlib_main_t * vm, ipsec_per_thread_data_t * ptd,
+                         ipsec_sa_t * sa0, vlib_buffer_t * b,
+                         vlib_buffer_t * lb, u8 icv_sz, u8 * start,
+                         u32 start_len, u16 * n_ch)
+{
+  vnet_crypto_op_chunk_t *ch;
+  vlib_buffer_t *cb = b;
+  u32 n_chunks = 1;
+  u32 total_len;
+  vec_add2 (ptd->chunks, ch, 1);
+  total_len = ch->len = start_len;
+  ch->src = ch->dst = start;
+  cb = vlib_get_buffer (vm, cb->next_buffer);
+
+  while (1)
+    {
+      vec_add2 (ptd->chunks, ch, 1);
+      n_chunks += 1;
+      if (lb == cb)
+       total_len += ch->len = cb->current_length - icv_sz;
+      else
+       total_len += ch->len = cb->current_length;
+      ch->src = ch->dst = vlib_buffer_get_current (cb);
+
+      if (!(cb->flags & VLIB_BUFFER_NEXT_PRESENT))
+       break;
+
+      cb = vlib_get_buffer (vm, cb->next_buffer);
+    }
+
+  if (n_ch)
+    *n_ch = n_chunks;
+
+  return total_len;
+}
+
+static_always_inline u32
+esp_encrypt_chain_integ (vlib_main_t * vm, ipsec_per_thread_data_t * ptd,
+                        ipsec_sa_t * sa0, vlib_buffer_t * b,
+                        vlib_buffer_t * lb, u8 icv_sz, u8 * start,
+                        u32 start_len, u8 * digest, u16 * n_ch)
+{
+  vnet_crypto_op_chunk_t *ch;
+  vlib_buffer_t *cb = b;
+  u32 n_chunks = 1;
+  u32 total_len;
+  vec_add2 (ptd->chunks, ch, 1);
+  total_len = ch->len = start_len;
+  ch->src = start;
+  cb = vlib_get_buffer (vm, cb->next_buffer);
+
+  while (1)
+    {
+      vec_add2 (ptd->chunks, ch, 1);
+      n_chunks += 1;
+      if (lb == cb)
+       {
+         total_len += ch->len = cb->current_length - icv_sz;
+         if (ipsec_sa_is_set_USE_ESN (sa0))
+           {
+             u32 seq_hi = clib_net_to_host_u32 (sa0->seq_hi);
+             clib_memcpy_fast (digest, &seq_hi, sizeof (seq_hi));
+             ch->len += sizeof (seq_hi);
+             total_len += sizeof (seq_hi);
+           }
+       }
+      else
+       total_len += ch->len = cb->current_length;
+      ch->src = vlib_buffer_get_current (cb);
+
+      if (!(cb->flags & VLIB_BUFFER_NEXT_PRESENT))
+       break;
+
+      cb = vlib_get_buffer (vm, cb->next_buffer);
+    }
+
+  if (n_ch)
+    *n_ch = n_chunks;
+
+  return total_len;
+}
+
+always_inline void
+esp_prepare_sync_op (vlib_main_t * vm, ipsec_per_thread_data_t * ptd,
+                    vnet_crypto_op_t ** crypto_ops,
+                    vnet_crypto_op_t ** integ_ops, ipsec_sa_t * sa0,
+                    u8 * payload, u16 payload_len, u8 iv_sz, u8 icv_sz,
+                    vlib_buffer_t ** bufs, vlib_buffer_t ** b,
+                    vlib_buffer_t * lb, u32 hdr_len, esp_header_t * esp,
+                    esp_gcm_nonce_t * nonce)
+{
+  if (sa0->crypto_enc_op_id)
+    {
+      vnet_crypto_op_t *op;
+      vec_add2_aligned (crypto_ops[0], op, 1, CLIB_CACHE_LINE_BYTES);
+      vnet_crypto_op_init (op, sa0->crypto_enc_op_id);
+
+      op->src = op->dst = payload;
+      op->key_index = sa0->crypto_key_index;
+      op->len = payload_len - icv_sz;
+      op->user_data = b - bufs;
+
+      if (ipsec_sa_is_set_IS_AEAD (sa0))
+       {
+         /*
+          * construct the AAD in a scratch space in front
+          * of the IP header.
+          */
+         op->aad = payload - hdr_len - sizeof (esp_aead_t);
+         op->aad_len = esp_aad_fill (op->aad, esp, sa0);
+
+         op->tag = payload + op->len;
+         op->tag_len = 16;
+
+         u64 *iv = (u64 *) (payload - iv_sz);
+         nonce->salt = sa0->salt;
+         nonce->iv = *iv = clib_host_to_net_u64 (sa0->gcm_iv_counter++);
+         op->iv = (u8 *) nonce;
+       }
+      else
+       {
+         op->iv = payload - iv_sz;
+         op->flags = VNET_CRYPTO_OP_FLAG_INIT_IV;
+       }
+
+      if (lb != b[0])
+       {
+         /* is chained */
+         op->flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
+         op->chunk_index = vec_len (ptd->chunks);
+         op->tag = vlib_buffer_get_tail (lb) - icv_sz;
+         esp_encrypt_chain_crypto (vm, ptd, sa0, b[0], lb, icv_sz, payload,
+                                   payload_len, &op->n_chunks);
+       }
+    }
+
+  if (sa0->integ_op_id)
+    {
+      vnet_crypto_op_t *op;
+      vec_add2_aligned (integ_ops[0], op, 1, CLIB_CACHE_LINE_BYTES);
+      vnet_crypto_op_init (op, sa0->integ_op_id);
+      op->src = payload - iv_sz - sizeof (esp_header_t);
+      op->digest = payload + payload_len - icv_sz;
+      op->key_index = sa0->integ_key_index;
+      op->digest_len = icv_sz;
+      op->len = payload_len - icv_sz + iv_sz + sizeof (esp_header_t);
+      op->user_data = b - bufs;
+
+      if (lb != b[0])
+       {
+         /* is chained */
+         op->flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
+         op->chunk_index = vec_len (ptd->chunks);
+         op->digest = vlib_buffer_get_tail (lb) - icv_sz;
+
+         esp_encrypt_chain_integ (vm, ptd, sa0, b[0], lb, icv_sz,
+                                  payload - iv_sz - sizeof (esp_header_t),
+                                  payload_len + iv_sz +
+                                  sizeof (esp_header_t), op->digest,
+                                  &op->n_chunks);
+       }
+      else if (ipsec_sa_is_set_USE_ESN (sa0))
+       {
+         u32 seq_hi = clib_net_to_host_u32 (sa0->seq_hi);
+         clib_memcpy_fast (op->digest, &seq_hi, sizeof (seq_hi));
+         op->len += sizeof (seq_hi);
+       }
+    }
+}
+
+static_always_inline int
+esp_prepare_async_frame (vlib_main_t * vm, ipsec_per_thread_data_t * ptd,
+                        vnet_crypto_async_frame_t ** async_frame,
+                        ipsec_sa_t * sa, vlib_buffer_t * b,
+                        esp_header_t * esp, u8 * payload, u32 payload_len,
+                        u8 iv_sz, u8 icv_sz, u32 bi, u16 * next, u32 hdr_len,
+                        u16 async_next, vlib_buffer_t * lb)
+{
+  esp_post_data_t *post = esp_post_data (b);
+  u8 *tag, *iv, *aad = 0;
+  u8 flag = 0;
+  u32 key_index;
+  i16 crypto_start_offset, integ_start_offset = 0;
+  u16 crypto_total_len, integ_total_len;
+
+  post->next_index = next[0];
+  next[0] = ESP_ENCRYPT_NEXT_PENDING;
+
+  /* crypto */
+  crypto_start_offset = payload - b->data;
+  crypto_total_len = integ_total_len = payload_len - icv_sz;
+  tag = payload + crypto_total_len;
+
+  /* aead */
+  if (ipsec_sa_is_set_IS_AEAD (sa))
+    {
+      esp_gcm_nonce_t *nonce;
+      u64 *pkt_iv = (u64 *) (payload - iv_sz);
+
+      aad = payload - hdr_len - sizeof (esp_aead_t);
+      esp_aad_fill (aad, esp, sa);
+      nonce = (esp_gcm_nonce_t *) (aad - sizeof (*nonce));
+      nonce->salt = sa->salt;
+      nonce->iv = *pkt_iv = clib_host_to_net_u64 (sa->gcm_iv_counter++);
+      iv = (u8 *) nonce;
+      key_index = sa->crypto_key_index;
+
+      if (lb != b)
+       {
+         /* chain */
+         flag |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
+         tag = vlib_buffer_get_tail (lb) - icv_sz;
+         crypto_total_len = esp_encrypt_chain_crypto (vm, ptd, sa, b, lb,
+                                                      icv_sz, payload,
+                                                      payload_len, 0);
+       }
+      goto out;
+    }
+
+  /* cipher then hash */
+  iv = payload - iv_sz;
+  integ_start_offset = crypto_start_offset - iv_sz - sizeof (esp_header_t);
+  integ_total_len += iv_sz + sizeof (esp_header_t);
+  flag |= VNET_CRYPTO_OP_FLAG_INIT_IV;
+  key_index = sa->linked_key_index;
+
+  if (b != lb)
+    {
+      flag |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
+      crypto_total_len = esp_encrypt_chain_crypto (vm, ptd, sa, b, lb,
+                                                  icv_sz, payload,
+                                                  payload_len, 0);
+      tag = vlib_buffer_get_tail (lb) - icv_sz;
+      integ_total_len = esp_encrypt_chain_integ (vm, ptd, sa, b, lb, icv_sz,
+                                                payload - iv_sz -
+                                                sizeof (esp_header_t),
+                                                payload_len + iv_sz +
+                                                sizeof (esp_header_t),
+                                                tag, 0);
+    }
+  else if (ipsec_sa_is_set_USE_ESN (sa) && !ipsec_sa_is_set_IS_AEAD (sa))
+    {
+      u32 seq_hi = clib_net_to_host_u32 (sa->seq_hi);
+      clib_memcpy_fast (tag, &seq_hi, sizeof (seq_hi));
+      integ_total_len += sizeof (seq_hi);
+    }
+
+out:
+  return vnet_crypto_async_add_to_frame (vm, async_frame, key_index,
+                                        crypto_total_len,
+                                        integ_total_len - crypto_total_len,
+                                        crypto_start_offset,
+                                        integ_start_offset, bi, async_next,
+                                        iv, tag, aad, flag);
+}
+
+/* when submitting a frame is failed, drop all buffers in the frame */
+static_always_inline void
+esp_async_recycle_failed_submit (vnet_crypto_async_frame_t * f,
+                                vlib_buffer_t ** b, u16 * next)
+{
+  u32 n_drop = f->n_elts;
+  while (--n_drop)
+    {
+      (b - n_drop)[0]->error = ESP_ENCRYPT_ERROR_CRYPTO_ENGINE_ERROR;
+      (next - n_drop)[0] = ESP_ENCRYPT_NEXT_DROP;
+    }
+  vnet_crypto_async_reset_frame (f);
+}
+
 always_inline uword
 esp_encrypt_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
-                   vlib_frame_t * frame, int is_ip6, int is_tun)
+                   vlib_frame_t * frame, int is_ip6, int is_tun,
+                   u16 async_next)
 {
   ipsec_main_t *im = &ipsec_main;
   ipsec_per_thread_data_t *ptd = vec_elt_at_index (im->ptd, vm->thread_index);
@@ -293,16 +584,21 @@ esp_encrypt_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
   u32 current_sa_bytes = 0, spi = 0;
   u8 block_sz = 0, iv_sz = 0, icv_sz = 0;
   ipsec_sa_t *sa0 = 0;
-  vnet_crypto_op_chunk_t *ch;
   vlib_buffer_t *lb;
   vnet_crypto_op_t **crypto_ops = &ptd->crypto_ops;
   vnet_crypto_op_t **integ_ops = &ptd->integ_ops;
+  vnet_crypto_async_frame_t *async_frame = 0;
+  int is_async = im->async_mode;
+  vnet_crypto_async_op_id_t last_async_op = ~0;
 
   vlib_get_buffers (vm, from, b, n_left);
-  vec_reset_length (ptd->crypto_ops);
-  vec_reset_length (ptd->integ_ops);
-  vec_reset_length (ptd->chained_crypto_ops);
-  vec_reset_length (ptd->chained_integ_ops);
+  if (!is_async)
+    {
+      vec_reset_length (ptd->crypto_ops);
+      vec_reset_length (ptd->integ_ops);
+      vec_reset_length (ptd->chained_crypto_ops);
+      vec_reset_length (ptd->chained_integ_ops);
+    }
   vec_reset_length (ptd->chunks);
 
   while (n_left > 0)
@@ -351,6 +647,20 @@ esp_encrypt_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
          block_sz = sa0->crypto_block_size;
          icv_sz = sa0->integ_icv_size;
          iv_sz = sa0->crypto_iv_size;
+
+         /* submit frame when op_id is different then the old one */
+         if (is_async && sa0->crypto_async_enc_op_id != last_async_op)
+           {
+             if (async_frame && async_frame->n_elts)
+               {
+                 if (vnet_crypto_async_submit_open_frame (vm, async_frame)
+                     < 0)
+                   esp_async_recycle_failed_submit (async_frame, b, next);
+               }
+             async_frame =
+               vnet_crypto_async_get_frame (vm, sa0->crypto_async_enc_op_id);
+             last_async_op = sa0->crypto_async_enc_op_id;
+           }
        }
 
       if (PREDICT_FALSE (~0 == sa0->encrypt_thread_index))
@@ -569,129 +879,25 @@ esp_encrypt_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
       esp->spi = spi;
       esp->seq = clib_net_to_host_u32 (sa0->seq);
 
-      if (sa0->crypto_enc_op_id)
+      if (is_async)
        {
-         vnet_crypto_op_t *op;
-         vec_add2_aligned (crypto_ops[0], op, 1, CLIB_CACHE_LINE_BYTES);
-         vnet_crypto_op_init (op, sa0->crypto_enc_op_id);
-
-         op->src = op->dst = payload;
-         op->key_index = sa0->crypto_key_index;
-         op->len = payload_len - icv_sz;
-         op->user_data = b - bufs;
-
-         if (ipsec_sa_is_set_IS_AEAD (sa0))
-           {
-             /*
-              * construct the AAD in a scratch space in front
-              * of the IP header.
-              */
-             op->aad = payload - hdr_len - sizeof (esp_aead_t);
-
-             esp_aad_fill (op, esp, sa0);
-
-             op->tag = payload + op->len;
-             op->tag_len = 16;
-
-             u64 *iv = (u64 *) (payload - iv_sz);
-             nonce->salt = sa0->salt;
-             nonce->iv = *iv = clib_host_to_net_u64 (sa0->gcm_iv_counter++);
-             op->iv = (u8 *) nonce;
-             nonce++;
-           }
-         else
-           {
-             op->iv = payload - iv_sz;
-             op->flags = VNET_CRYPTO_OP_FLAG_INIT_IV;
-           }
+         if (PREDICT_FALSE (sa0->crypto_async_enc_op_id == 0))
+           goto trace;
 
-         if (lb != b[0])
+         if (esp_prepare_async_frame (vm, ptd, &async_frame, sa0, b[0], esp,
+                                      payload, payload_len, iv_sz,
+                                      icv_sz, from[b - bufs], next, hdr_len,
+                                      async_next, lb))
            {
-             /* is chained */
-             vlib_buffer_t *cb = b[0];
-             op->flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
-             op->chunk_index = vec_len (ptd->chunks);
-             op->tag = vlib_buffer_get_tail (lb) - icv_sz;
-             vec_add2 (ptd->chunks, ch, 1);
-             ch->len = payload_len;
-             ch->src = ch->dst = payload;
-             cb = vlib_get_buffer (vm, cb->next_buffer);
-             op->n_chunks = 1;
-
-             while (1)
-               {
-                 vec_add2 (ptd->chunks, ch, 1);
-                 op->n_chunks += 1;
-                 if (lb == cb)
-                   ch->len = cb->current_length - icv_sz;
-                 else
-                   ch->len = cb->current_length;
-                 ch->src = ch->dst = vlib_buffer_get_current (cb);
-
-                 if (!(cb->flags & VLIB_BUFFER_NEXT_PRESENT))
-                   break;
-
-                 cb = vlib_get_buffer (vm, cb->next_buffer);
-               }
+             esp_async_recycle_failed_submit (async_frame, b, next);
+             goto trace;
            }
        }
-
-      if (sa0->integ_op_id)
+      else
        {
-         vnet_crypto_op_t *op;
-         vec_add2_aligned (integ_ops[0], op, 1, CLIB_CACHE_LINE_BYTES);
-         vnet_crypto_op_init (op, sa0->integ_op_id);
-         op->src = payload - iv_sz - sizeof (esp_header_t);
-         op->digest = payload + payload_len - icv_sz;
-         op->key_index = sa0->integ_key_index;
-         op->digest_len = icv_sz;
-         op->len = payload_len - icv_sz + iv_sz + sizeof (esp_header_t);
-         op->user_data = b - bufs;
-
-         if (lb != b[0])
-           {
-             /* is chained */
-             op->flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
-             vlib_buffer_t *cb = b[0];
-             op->chunk_index = vec_len (ptd->chunks);
-             op->digest = vlib_buffer_get_tail (lb) - icv_sz;
-             vec_add2 (ptd->chunks, ch, 1);
-             ch->len = payload_len + iv_sz + sizeof (esp_header_t);
-             ch->src = payload - iv_sz - sizeof (esp_header_t);
-             cb = vlib_get_buffer (vm, cb->next_buffer);
-             op->n_chunks = 1;
-
-             while (1)
-               {
-                 vec_add2 (ptd->chunks, ch, 1);
-                 op->n_chunks += 1;
-                 if (lb == cb)
-                   {
-                     ch->len = cb->current_length - icv_sz;
-                     if (ipsec_sa_is_set_USE_ESN (sa0))
-                       {
-                         u32 seq_hi = clib_net_to_host_u32 (sa0->seq_hi);
-                         clib_memcpy_fast (op->digest, &seq_hi,
-                                           sizeof (seq_hi));
-                         ch->len += sizeof (seq_hi);
-                       }
-                   }
-                 else
-                   ch->len = cb->current_length;
-                 ch->src = vlib_buffer_get_current (cb);
-
-                 if (!(cb->flags & VLIB_BUFFER_NEXT_PRESENT))
-                   break;
-
-                 cb = vlib_get_buffer (vm, cb->next_buffer);
-               }
-           }
-         else if (ipsec_sa_is_set_USE_ESN (sa0))
-           {
-             u32 seq_hi = clib_net_to_host_u32 (sa0->seq_hi);
-             clib_memcpy_fast (op->digest, &seq_hi, sizeof (seq_hi));
-             op->len += sizeof (seq_hi);
-           }
+         esp_prepare_sync_op (vm, ptd, crypto_ops, integ_ops, sa0, payload,
+                              payload_len, iv_sz, icv_sz, bufs, b, lb,
+                              hdr_len, esp, nonce++);
        }
 
       vlib_buffer_advance (b[0], 0LL - hdr_len);
@@ -721,14 +927,21 @@ esp_encrypt_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
   vlib_increment_combined_counter (&ipsec_sa_counters, thread_index,
                                   current_sa_index, current_sa_packets,
                                   current_sa_bytes);
+  if (!is_async)
+    {
+      esp_process_ops (vm, node, ptd->crypto_ops, bufs, nexts);
+      esp_process_chained_ops (vm, node, ptd->chained_crypto_ops, bufs, nexts,
+                              ptd->chunks);
 
-  esp_process_ops (vm, node, ptd->crypto_ops, bufs, nexts);
-  esp_process_chained_ops (vm, node, ptd->chained_crypto_ops, bufs, nexts,
-                          ptd->chunks);
-
-  esp_process_ops (vm, node, ptd->integ_ops, bufs, nexts);
-  esp_process_chained_ops (vm, node, ptd->chained_integ_ops, bufs, nexts,
-                          ptd->chunks);
+      esp_process_ops (vm, node, ptd->integ_ops, bufs, nexts);
+      esp_process_chained_ops (vm, node, ptd->chained_integ_ops, bufs, nexts,
+                              ptd->chunks);
+    }
+  else if (async_frame && async_frame->n_elts)
+    {
+      if (vnet_crypto_async_submit_open_frame (vm, async_frame) < 0)
+       esp_async_recycle_failed_submit (async_frame, b, next);
+    }
 
   vlib_node_increment_counter (vm, node->node_index,
                               ESP_ENCRYPT_ERROR_RX_PKTS, frame->n_vectors);
@@ -737,11 +950,98 @@ esp_encrypt_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
   return frame->n_vectors;
 }
 
+always_inline uword
+esp_encrypt_post_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
+                        vlib_frame_t * frame)
+{
+  vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
+  u16 nexts[VLIB_FRAME_SIZE], *next = nexts;
+  u32 *from = vlib_frame_vector_args (frame);
+  u32 n_left = frame->n_vectors;
+
+  vlib_get_buffers (vm, from, b, n_left);
+
+  if (n_left >= 4)
+    {
+      vlib_prefetch_buffer_header (b[0], LOAD);
+      vlib_prefetch_buffer_header (b[1], LOAD);
+      vlib_prefetch_buffer_header (b[2], LOAD);
+      vlib_prefetch_buffer_header (b[3], LOAD);
+    }
+
+  while (n_left > 8)
+    {
+      vlib_prefetch_buffer_header (b[4], LOAD);
+      vlib_prefetch_buffer_header (b[5], LOAD);
+      vlib_prefetch_buffer_header (b[6], LOAD);
+      vlib_prefetch_buffer_header (b[7], LOAD);
+
+      next[0] = (esp_post_data (b[0]))->next_index;
+      next[1] = (esp_post_data (b[1]))->next_index;
+      next[2] = (esp_post_data (b[2]))->next_index;
+      next[3] = (esp_post_data (b[3]))->next_index;
+
+      if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
+       {
+         if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
+           {
+             esp_encrypt_post_trace_t *tr = vlib_add_trace (vm, node, b[0],
+                                                            sizeof (*tr));
+             tr->next_index = next[0];
+           }
+         if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
+           {
+             esp_encrypt_post_trace_t *tr = vlib_add_trace (vm, node, b[1],
+                                                            sizeof (*tr));
+             tr->next_index = next[1];
+           }
+         if (b[2]->flags & VLIB_BUFFER_IS_TRACED)
+           {
+             esp_encrypt_post_trace_t *tr = vlib_add_trace (vm, node, b[2],
+                                                            sizeof (*tr));
+             tr->next_index = next[2];
+           }
+         if (b[3]->flags & VLIB_BUFFER_IS_TRACED)
+           {
+             esp_encrypt_post_trace_t *tr = vlib_add_trace (vm, node, b[3],
+                                                            sizeof (*tr));
+             tr->next_index = next[3];
+           }
+       }
+
+      b += 4;
+      next += 4;
+      n_left -= 4;
+    }
+
+  while (n_left > 0)
+    {
+      next[0] = (esp_post_data (b[0]))->next_index;
+      if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
+       {
+         esp_encrypt_post_trace_t *tr = vlib_add_trace (vm, node, b[0],
+                                                        sizeof (*tr));
+         tr->next_index = next[0];
+       }
+
+      b += 1;
+      next += 1;
+      n_left -= 1;
+    }
+
+  vlib_node_increment_counter (vm, node->node_index,
+                              ESP_ENCRYPT_ERROR_POST_RX_PKTS,
+                              frame->n_vectors);
+  vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
+  return frame->n_vectors;
+}
+
 VLIB_NODE_FN (esp4_encrypt_node) (vlib_main_t * vm,
                                  vlib_node_runtime_t * node,
                                  vlib_frame_t * from_frame)
 {
-  return esp_encrypt_inline (vm, node, from_frame, 0 /* is_ip6 */ , 0);
+  return esp_encrypt_inline (vm, node, from_frame, 0 /* is_ip6 */ , 0,
+                            esp_encrypt_async_next.esp4_post_next);
 }
 
 /* *INDENT-OFF* */
@@ -759,15 +1059,37 @@ VLIB_REGISTER_NODE (esp4_encrypt_node) = {
     [ESP_ENCRYPT_NEXT_DROP] = "ip4-drop",
     [ESP_ENCRYPT_NEXT_HANDOFF] = "esp4-encrypt-handoff",
     [ESP_ENCRYPT_NEXT_INTERFACE_OUTPUT] = "interface-output",
+    [ESP_ENCRYPT_NEXT_PENDING] = "esp-encrypt-pending",
   },
 };
 /* *INDENT-ON* */
 
+VLIB_NODE_FN (esp4_encrypt_post_node) (vlib_main_t * vm,
+                                      vlib_node_runtime_t * node,
+                                      vlib_frame_t * from_frame)
+{
+  return esp_encrypt_post_inline (vm, node, from_frame);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (esp4_encrypt_post_node) = {
+  .name = "esp4-encrypt-post",
+  .vector_size = sizeof (u32),
+  .format_trace = format_esp_post_encrypt_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .sibling_of = "esp4-encrypt",
+
+  .n_errors = ARRAY_LEN(esp_encrypt_error_strings),
+  .error_strings = esp_encrypt_error_strings,
+};
+/* *INDENT-ON* */
+
 VLIB_NODE_FN (esp6_encrypt_node) (vlib_main_t * vm,
                                  vlib_node_runtime_t * node,
                                  vlib_frame_t * from_frame)
 {
-  return esp_encrypt_inline (vm, node, from_frame, 1 /* is_ip6 */ , 0);
+  return esp_encrypt_inline (vm, node, from_frame, 1 /* is_ip6 */ , 0,
+                            esp_encrypt_async_next.esp6_post_next);
 }
 
 /* *INDENT-OFF* */
@@ -785,15 +1107,37 @@ VLIB_REGISTER_NODE (esp6_encrypt_node) = {
     [ESP_ENCRYPT_NEXT_DROP] = "ip6-drop",
     [ESP_ENCRYPT_NEXT_HANDOFF] = "esp6-encrypt-handoff",
     [ESP_ENCRYPT_NEXT_INTERFACE_OUTPUT] = "interface-output",
+    [ESP_ENCRYPT_NEXT_PENDING] = "esp-encrypt-pending",
   },
 };
 /* *INDENT-ON* */
 
+VLIB_NODE_FN (esp6_encrypt_post_node) (vlib_main_t * vm,
+                                      vlib_node_runtime_t * node,
+                                      vlib_frame_t * from_frame)
+{
+  return esp_encrypt_post_inline (vm, node, from_frame);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (esp6_encrypt_post_node) = {
+  .name = "esp6-encrypt-post",
+  .vector_size = sizeof (u32),
+  .format_trace = format_esp_post_encrypt_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .sibling_of = "esp6-encrypt",
+
+  .n_errors = ARRAY_LEN(esp_encrypt_error_strings),
+  .error_strings = esp_encrypt_error_strings,
+};
+/* *INDENT-ON* */
+
 VLIB_NODE_FN (esp4_encrypt_tun_node) (vlib_main_t * vm,
                                      vlib_node_runtime_t * node,
                                      vlib_frame_t * from_frame)
 {
-  return esp_encrypt_inline (vm, node, from_frame, 0 /* is_ip6 */ , 1);
+  return esp_encrypt_inline (vm, node, from_frame, 0 /* is_ip6 */ , 1,
+                            esp_encrypt_async_next.esp4_tun_post_next);
 }
 
 /* *INDENT-OFF* */
@@ -811,9 +1155,29 @@ VLIB_REGISTER_NODE (esp4_encrypt_tun_node) = {
     [ESP_ENCRYPT_NEXT_DROP] = "ip4-drop",
     [ESP_ENCRYPT_NEXT_HANDOFF] = "esp4-encrypt-tun-handoff",
     [ESP_ENCRYPT_NEXT_INTERFACE_OUTPUT] = "error-drop",
+    [ESP_ENCRYPT_NEXT_PENDING] = "esp-encrypt-pending",
   },
 };
 
+VLIB_NODE_FN (esp4_encrypt_tun_post_node) (vlib_main_t * vm,
+                                 vlib_node_runtime_t * node,
+                                 vlib_frame_t * from_frame)
+{
+  return esp_encrypt_post_inline (vm, node, from_frame);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (esp4_encrypt_tun_post_node) = {
+  .name = "esp4-encrypt-tun-post",
+  .vector_size = sizeof (u32),
+  .format_trace = format_esp_post_encrypt_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .sibling_of = "esp4-encrypt-tun",
+
+  .n_errors = ARRAY_LEN(esp_encrypt_error_strings),
+  .error_strings = esp_encrypt_error_strings,
+};
+
 VNET_FEATURE_INIT (esp4_encrypt_tun_feat_node, static) =
 {
   .arc_name = "ip4-output",
@@ -840,7 +1204,8 @@ VLIB_NODE_FN (esp6_encrypt_tun_node) (vlib_main_t * vm,
                                      vlib_node_runtime_t * node,
                                      vlib_frame_t * from_frame)
 {
-  return esp_encrypt_inline (vm, node, from_frame, 1 /* is_ip6 */ , 1);
+  return esp_encrypt_inline (vm, node, from_frame, 1 /* is_ip6 */ , 1,
+                            esp_encrypt_async_next.esp6_tun_post_next);
 }
 
 /* *INDENT-OFF* */
@@ -858,6 +1223,7 @@ VLIB_REGISTER_NODE (esp6_encrypt_tun_node) = {
     [ESP_ENCRYPT_NEXT_DROP] = "ip6-drop",
     [ESP_ENCRYPT_NEXT_HANDOFF] = "esp6-encrypt-tun-handoff",
     [ESP_ENCRYPT_NEXT_INTERFACE_OUTPUT] = "error-drop",
+    [ESP_ENCRYPT_NEXT_PENDING] = "esp-encrypt-pending",
   },
 };
 
@@ -877,6 +1243,26 @@ VNET_FEATURE_INIT (esp4o6_encrypt_tun_feat_node, static) =
 
 /* *INDENT-ON* */
 
+VLIB_NODE_FN (esp6_encrypt_tun_post_node) (vlib_main_t * vm,
+                                          vlib_node_runtime_t * node,
+                                          vlib_frame_t * from_frame)
+{
+  return esp_encrypt_post_inline (vm, node, from_frame);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (esp6_encrypt_tun_post_node) = {
+  .name = "esp6-encrypt-tun-post",
+  .vector_size = sizeof (u32),
+  .format_trace = format_esp_post_encrypt_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .sibling_of = "esp6-encrypt-tun",
+
+  .n_errors = ARRAY_LEN(esp_encrypt_error_strings),
+  .error_strings = esp_encrypt_error_strings,
+};
+/* *INDENT-ON* */
+
 typedef struct
 {
   u32 sa_index;
@@ -1008,6 +1394,23 @@ VNET_FEATURE_INIT (esp6_no_crypto_tun_feat_node, static) =
 };
 /* *INDENT-ON* */
 
+VLIB_NODE_FN (esp_encrypt_pending_node) (vlib_main_t * vm,
+                                        vlib_node_runtime_t * node,
+                                        vlib_frame_t * from_frame)
+{
+  return from_frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (esp_encrypt_pending_node) = {
+  .name = "esp-encrypt-pending",
+  .vector_size = sizeof (u32),
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_next_nodes = 0
+};
+/* *INDENT-ON* */
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index e7cb615..6b31926 100644 (file)
@@ -26,6 +26,8 @@
 #include <vnet/ipsec/ah.h>
 
 ipsec_main_t ipsec_main;
+esp_async_post_next_t esp_encrypt_async_next;
+esp_async_post_next_t esp_decrypt_async_next;
 
 static clib_error_t *
 ipsec_check_ah_support (ipsec_sa_t * sa)
@@ -173,7 +175,8 @@ ipsec_register_esp_backend (vlib_main_t * vm, ipsec_main_t * im,
                            const char *esp6_decrypt_node_name,
                            const char *esp6_decrypt_tun_node_name,
                            check_support_cb_t esp_check_support_cb,
-                           add_del_sa_sess_cb_t esp_add_del_sa_sess_cb)
+                           add_del_sa_sess_cb_t esp_add_del_sa_sess_cb,
+                           enable_disable_cb_t enable_disable_cb)
 {
   ipsec_esp_backend_t *b;
 
@@ -206,6 +209,8 @@ ipsec_register_esp_backend (vlib_main_t * vm, ipsec_main_t * im,
 
   b->check_support_cb = esp_check_support_cb;
   b->add_del_sa_sess_cb = esp_add_del_sa_sess_cb;
+  b->enable_disable_cb = enable_disable_cb;
+
   return b - im->esp_backends;
 }
 
@@ -253,6 +258,18 @@ ipsec_select_esp_backend (ipsec_main_t * im, u32 backend_idx)
   if (pool_is_free_index (im->esp_backends, backend_idx))
     return VNET_API_ERROR_INVALID_VALUE;
 
+  /* disable current backend */
+  if (im->esp_current_backend != ~0)
+    {
+      ipsec_esp_backend_t *cb = pool_elt_at_index (im->esp_backends,
+                                                  im->esp_current_backend);
+      if (cb->enable_disable_cb)
+       {
+         if ((cb->enable_disable_cb) (0) != 0)
+           return -1;
+       }
+    }
+
   ipsec_esp_backend_t *b = pool_elt_at_index (im->esp_backends, backend_idx);
   im->esp_current_backend = backend_idx;
   im->esp4_encrypt_node_index = b->esp4_encrypt_node_index;
@@ -273,9 +290,64 @@ ipsec_select_esp_backend (ipsec_main_t * im, u32 backend_idx)
   im->esp46_encrypt_tun_feature_index = b->esp46_encrypt_tun_feature_index;
   im->esp66_encrypt_tun_feature_index = b->esp66_encrypt_tun_feature_index;
 
+  if (b->enable_disable_cb)
+    {
+      if ((b->enable_disable_cb) (1) != 0)
+       return -1;
+    }
   return 0;
 }
 
+void
+ipsec_set_async_mode (u32 is_enabled)
+{
+  ipsec_main_t *im = &ipsec_main;
+  ipsec_sa_t *sa;
+
+  /* lock all SAs before change im->async_mode */
+  pool_foreach (sa, im->sad, (
+                              {
+                              fib_node_lock (&sa->node);
+                              }));
+
+  im->async_mode = is_enabled;
+
+  /* change SA crypto op data before unlock them */
+  pool_foreach (sa, im->sad, (
+                              {
+                              sa->crypto_op_data = is_enabled ?
+                              sa->async_op_data.data : sa->sync_op_data.data;
+                              fib_node_unlock (&sa->node);
+                              }));
+}
+
+static void
+crypto_engine_backend_register_post_node (vlib_main_t * vm)
+{
+  esp_async_post_next_t *eit;
+  esp_async_post_next_t *dit;
+
+  eit = &esp_encrypt_async_next;
+  eit->esp4_post_next =
+    vnet_crypto_register_post_node (vm, "esp4-encrypt-post");
+  eit->esp6_post_next =
+    vnet_crypto_register_post_node (vm, "esp6-encrypt-post");
+  eit->esp4_tun_post_next =
+    vnet_crypto_register_post_node (vm, "esp4-encrypt-tun-post");
+  eit->esp6_tun_post_next =
+    vnet_crypto_register_post_node (vm, "esp6-encrypt-tun-post");
+
+  dit = &esp_decrypt_async_next;
+  dit->esp4_post_next =
+    vnet_crypto_register_post_node (vm, "esp4-decrypt-post");
+  dit->esp6_post_next =
+    vnet_crypto_register_post_node (vm, "esp6-decrypt-post");
+  dit->esp4_tun_post_next =
+    vnet_crypto_register_post_node (vm, "esp4-decrypt-tun-post");
+  dit->esp6_tun_post_next =
+    vnet_crypto_register_post_node (vm, "esp6-decrypt-tun-post");
+}
+
 static clib_error_t *
 ipsec_init (vlib_main_t * vm)
 {
@@ -298,6 +370,9 @@ ipsec_init (vlib_main_t * vm)
   ASSERT (node);
   im->error_drop_node_index = node->index;
 
+  im->ah_current_backend = ~0;
+  im->esp_current_backend = ~0;
+
   u32 idx = ipsec_register_ah_backend (vm, im, "crypto engine backend",
                                       "ah4-encrypt",
                                       "ah4-decrypt",
@@ -320,7 +395,8 @@ ipsec_init (vlib_main_t * vm)
                                    "esp6-encrypt-tun",
                                    "esp6-decrypt",
                                    "esp6-decrypt-tun",
-                                   ipsec_check_esp_support, NULL);
+                                   ipsec_check_esp_support,
+                                   NULL, crypto_dispatch_enable_disable);
   im->esp_default_backend = idx;
 
   rv = ipsec_select_esp_backend (im, idx);
@@ -454,6 +530,9 @@ ipsec_init (vlib_main_t * vm)
   im->esp6_dec_tun_fq_index =
     vlib_frame_queue_main_init (esp6_decrypt_tun_node.index, 0);
 
+  im->async_mode = 0;
+  crypto_engine_backend_register_post_node (vm);
+
   return 0;
 }
 
index f1b7daf..712e16d 100644 (file)
@@ -28,6 +28,7 @@
 
 typedef clib_error_t *(*add_del_sa_sess_cb_t) (u32 sa_index, u8 is_add);
 typedef clib_error_t *(*check_support_cb_t) (ipsec_sa_t * sa);
+typedef clib_error_t *(*enable_disable_cb_t) (int is_enable);
 
 typedef struct
 {
@@ -53,6 +54,8 @@ typedef struct
   add_del_sa_sess_cb_t add_del_sa_sess_cb;
   /* check support function */
   check_support_cb_t check_support_cb;
+  /* enable or disable function */
+  enable_disable_cb_t enable_disable_cb;
   u32 esp4_encrypt_node_index;
   u32 esp4_decrypt_node_index;
   u32 esp4_encrypt_next_index;
@@ -194,6 +197,8 @@ typedef struct
   u32 esp6_enc_tun_fq_index;
   u32 esp4_dec_tun_fq_index;
   u32 esp6_dec_tun_fq_index;
+
+  u8 async_mode;
 } ipsec_main_t;
 
 typedef enum ipsec_format_flags_t_
@@ -266,7 +271,8 @@ u32 ipsec_register_esp_backend (vlib_main_t * vm, ipsec_main_t * im,
                                const char *esp6_decrypt_node_name,
                                const char *esp6_decrypt_tun_node_name,
                                check_support_cb_t esp_check_support_cb,
-                               add_del_sa_sess_cb_t esp_add_del_sa_sess_cb);
+                               add_del_sa_sess_cb_t esp_add_del_sa_sess_cb,
+                               enable_disable_cb_t enable_disable_cb);
 
 int ipsec_select_ah_backend (ipsec_main_t * im, u32 ah_backend_idx);
 int ipsec_select_esp_backend (ipsec_main_t * im, u32 esp_backend_idx);
@@ -282,6 +288,7 @@ ipsec_sa_get (u32 sa_index)
 void ipsec_add_feature (const char *arc_name, const char *node_name,
                        u32 * out_feature_index);
 
+void ipsec_set_async_mode (u32 is_enabled);
 
 #endif /* __IPSEC_H__ */
 
index 7779e79..13f9efd 100644 (file)
@@ -414,6 +414,9 @@ show_ipsec_command_fn (vlib_main_t * vm,
   ipsec_spd_bindings_show_all (vm, im);
   ipsec_tun_protect_walk (ipsec_tun_protect_show_one, vm);
 
+  vlib_cli_output (vm, "IPSec async mode: %s",
+                  (im->async_mode ? "on" : "off"));
+
   return 0;
 }
 
@@ -1038,6 +1041,41 @@ ipsec_cli_init (vlib_main_t * vm)
 
 VLIB_INIT_FUNCTION (ipsec_cli_init);
 
+static clib_error_t *
+set_async_mode_command_fn (vlib_main_t * vm, unformat_input_t * input,
+                          vlib_cli_command_t * cmd)
+{
+  unformat_input_t _line_input, *line_input = &_line_input;
+  int async_enable = 0;
+
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (line_input, "on"))
+       async_enable = 1;
+      else if (unformat (line_input, "off"))
+       async_enable = 0;
+      else
+       return (clib_error_return (0, "unknown input '%U'",
+                                  format_unformat_error, line_input));
+    }
+
+  vnet_crypto_request_async_mode (async_enable);
+  ipsec_set_async_mode (async_enable);
+
+  unformat_free (line_input);
+  return (NULL);
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (set_async_mode_command, static) = {
+    .path = "set ipsec async mode",
+    .short_help = "set ipsec async mode on|off",
+    .function = set_async_mode_command_fn,
+};
+/* *INDENT-ON* */
 
 /*
  * fd.io coding-style-patch-verification: ON
index 0e1e63d..f60566c 100644 (file)
@@ -100,8 +100,8 @@ ipsec_sa_set_crypto_alg (ipsec_sa_t * sa, ipsec_crypto_alg_t crypto_alg)
   sa->crypto_alg = crypto_alg;
   sa->crypto_iv_size = im->crypto_algs[crypto_alg].iv_size;
   sa->crypto_block_size = im->crypto_algs[crypto_alg].block_size;
-  sa->crypto_enc_op_id = im->crypto_algs[crypto_alg].enc_op_id;
-  sa->crypto_dec_op_id = im->crypto_algs[crypto_alg].dec_op_id;
+  sa->sync_op_data.crypto_enc_op_id = im->crypto_algs[crypto_alg].enc_op_id;
+  sa->sync_op_data.crypto_dec_op_id = im->crypto_algs[crypto_alg].dec_op_id;
   sa->crypto_calg = im->crypto_algs[crypto_alg].alg;
   ASSERT (sa->crypto_iv_size <= ESP_MAX_IV_SIZE);
   ASSERT (sa->crypto_block_size <= ESP_MAX_BLOCK_SIZE);
@@ -118,11 +118,54 @@ ipsec_sa_set_integ_alg (ipsec_sa_t * sa, ipsec_integ_alg_t integ_alg)
   ipsec_main_t *im = &ipsec_main;
   sa->integ_alg = integ_alg;
   sa->integ_icv_size = im->integ_algs[integ_alg].icv_size;
-  sa->integ_op_id = im->integ_algs[integ_alg].op_id;
+  sa->sync_op_data.integ_op_id = im->integ_algs[integ_alg].op_id;
   sa->integ_calg = im->integ_algs[integ_alg].alg;
   ASSERT (sa->integ_icv_size <= ESP_MAX_ICV_SIZE);
 }
 
+void
+ipsec_sa_set_async_op_ids (ipsec_sa_t * sa)
+{
+  /* *INDENT-OFF* */
+  if (ipsec_sa_is_set_USE_ESN (sa))
+    {
+#define _(n, s, k) \
+  if( sa->sync_op_data.crypto_enc_op_id == VNET_CRYPTO_OP_##n##_ENC ) \
+    sa->async_op_data.crypto_async_enc_op_id = \
+      VNET_CRYPTO_OP_##n##_TAG16_AAD12_ENC; \
+  if( sa->sync_op_data.crypto_dec_op_id == VNET_CRYPTO_OP_##n##_DEC ) \
+    sa->async_op_data.crypto_async_dec_op_id = \
+      VNET_CRYPTO_OP_##n##_TAG16_AAD12_DEC;
+    foreach_crypto_aead_alg
+#undef _
+    }
+  else
+    {
+#define _(n, s, k) \
+  if( sa->sync_op_data.crypto_enc_op_id == VNET_CRYPTO_OP_##n##_ENC ) \
+    sa->async_op_data.crypto_async_enc_op_id = \
+      VNET_CRYPTO_OP_##n##_TAG16_AAD8_ENC; \
+  if( sa->sync_op_data.crypto_dec_op_id == VNET_CRYPTO_OP_##n##_DEC ) \
+    sa->async_op_data.crypto_async_dec_op_id = \
+      VNET_CRYPTO_OP_##n##_TAG16_AAD8_DEC;
+    foreach_crypto_aead_alg
+#undef _
+    }
+
+#define _(c, h, s, k ,d) \
+  if( sa->sync_op_data.crypto_enc_op_id == VNET_CRYPTO_OP_##c##_ENC && \
+      sa->sync_op_data.integ_op_id == VNET_CRYPTO_OP_##h##_HMAC) \
+    sa->async_op_data.crypto_async_enc_op_id = \
+      VNET_CRYPTO_OP_##c##_##h##_TAG##d##_ENC; \
+  if( sa->sync_op_data.crypto_dec_op_id == VNET_CRYPTO_OP_##c##_DEC && \
+      sa->sync_op_data.integ_op_id == VNET_CRYPTO_OP_##h##_HMAC) \
+    sa->async_op_data.crypto_async_dec_op_id = \
+      VNET_CRYPTO_OP_##c##_##h##_TAG##d##_DEC;
+  foreach_crypto_link_async_alg
+#undef _
+  /* *INDENT-ON* */
+}
+
 int
 ipsec_sa_add_and_lock (u32 id,
                       u32 spi,
@@ -172,6 +215,8 @@ ipsec_sa_add_and_lock (u32 id,
       clib_memcpy (&sa->integ_key, ik, sizeof (sa->integ_key));
     }
   ipsec_sa_set_crypto_alg (sa, crypto_alg);
+  ipsec_sa_set_async_op_ids (sa);
+
   clib_memcpy (&sa->crypto_key, ck, sizeof (sa->crypto_key));
   ip46_address_copy (&sa->tunnel_src_addr, tun_src);
   ip46_address_copy (&sa->tunnel_dst_addr, tun_dst);
@@ -198,6 +243,19 @@ ipsec_sa_add_and_lock (u32 id,
        }
     }
 
+  if (sa->async_op_data.crypto_async_enc_op_id &&
+      !ipsec_sa_is_set_IS_AEAD (sa))
+    {                          //AES-CBC & HMAC
+      sa->async_op_data.linked_key_index =
+       vnet_crypto_key_add_linked (vm, sa->crypto_key_index,
+                                   sa->integ_key_index);
+    }
+
+  if (im->async_mode)
+    sa->crypto_op_data = sa->async_op_data.data;
+  else
+    sa->crypto_op_data = sa->sync_op_data.data;
+
   err = ipsec_check_support_cb (im, sa);
   if (err)
     {
index 0997eb7..de9b51e 100644 (file)
@@ -127,9 +127,27 @@ typedef struct
 
   vnet_crypto_key_index_t crypto_key_index;
   vnet_crypto_key_index_t integ_key_index;
-  vnet_crypto_op_id_t crypto_enc_op_id:16;
-  vnet_crypto_op_id_t crypto_dec_op_id:16;
-  vnet_crypto_op_id_t integ_op_id:16;
+
+  /* Union data shared by sync and async ops, updated when mode is
+   * changed. */
+  union
+  {
+    struct
+    {
+      vnet_crypto_op_id_t crypto_enc_op_id:16;
+      vnet_crypto_op_id_t crypto_dec_op_id:16;
+      vnet_crypto_op_id_t integ_op_id:16;
+    };
+
+    struct
+    {
+      vnet_crypto_async_op_id_t crypto_async_enc_op_id:16;
+      vnet_crypto_async_op_id_t crypto_async_dec_op_id:16;
+      vnet_crypto_key_index_t linked_key_index;
+    };
+
+    u64 crypto_op_data;
+  };
 
   /* data accessed by dataplane code should be above this comment */
     CLIB_CACHE_LINE_ALIGN_MARK (cacheline1);
@@ -165,6 +183,28 @@ typedef struct
   /* Salt used in GCM modes - stored in network byte order */
   u32 salt;
   u64 gcm_iv_counter;
+
+  union
+  {
+    struct
+    {
+      vnet_crypto_op_id_t crypto_enc_op_id:16;
+      vnet_crypto_op_id_t crypto_dec_op_id:16;
+      vnet_crypto_op_id_t integ_op_id:16;
+    };
+    u64 data;
+  } sync_op_data;
+
+  union
+  {
+    struct
+    {
+      vnet_crypto_async_op_id_t crypto_async_enc_op_id:16;
+      vnet_crypto_async_op_id_t crypto_async_dec_op_id:16;
+      vnet_crypto_key_index_t linked_key_index;
+    };
+    u64 data;
+  } async_op_data;
 } ipsec_sa_t;
 
 STATIC_ASSERT_OFFSET_OF (ipsec_sa_t, cacheline1, CLIB_CACHE_LINE_BYTES);