dpdk: add ipsec cryptodev support

DPDK Cryptodev support and related IPsec ESP nodes using DPDK Cryptodev APIs.

When DPDK Cryptodev support is enabled, the node graph is modified by adding
and replacing some of the nodes.

The following nodes are replaced:
* esp-encrypt -> dpdk-esp-encrypt
* esp-decrypt -> dpdk-esp-decrypt

The following nodes are added:
* dpdk-crypto-input : polling input node
* dpdk-esp-encrypt-post : internal node
* dpdk-esp-decrypt-post : internal node

Change-Id: I6dca9a890abaf4fb2a4fffce3fd08ac013e4d701
Signed-off-by: Zhang, Roy Fan <roy.fan.zhang@intel.com>
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
diff --git a/build-data/packages/dpdk.mk b/build-data/packages/dpdk.mk
index f9163c8..a529e36 100644
--- a/build-data/packages/dpdk.mk
+++ b/build-data/packages/dpdk.mk
@@ -22,6 +22,10 @@
 	DPDK_TUNE=$(DPDK_TUNE) \
 	DPDK_DEBUG=$(DPDK_DEBUG)
 
+DPDK_CRYPTO_PMD=$(strip $($(PLATFORM)_uses_dpdk_cryptodev))
+ifneq ($(DPDK_CRYPTO_PMD),)
+DPDK_MAKE_ARGS += DPDK_CRYPTO_PMD=y
+endif
 
 DPDK_PLATFORM_TARGET=$(strip $($(PLATFORM)_dpdk_target))
 ifneq ($(DPDK_PLATFORM_TARGET),)
diff --git a/build-data/packages/vnet.mk b/build-data/packages/vnet.mk
index cafb939..399ca1b 100644
--- a/build-data/packages/vnet.mk
+++ b/build-data/packages/vnet.mk
@@ -38,4 +38,7 @@
 vnet_CPPFLAGS += $(call installed_includes_fn, dpdk)
 vnet_LDFLAGS += $(call installed_libs_fn, dpdk)
 endif
+ifeq ($($(PLATFORM)_uses_dpdk_cryptodev),yes)
+vnet_configure_args += --with-dpdk-crypto
+endif
 endif
diff --git a/build-data/packages/vpp.mk b/build-data/packages/vpp.mk
index be10e17..6831c6b 100644
--- a/build-data/packages/vpp.mk
+++ b/build-data/packages/vpp.mk
@@ -45,4 +45,7 @@
 vpp_CPPFLAGS += $(call installed_includes_fn, dpdk)
 vpp_LDFLAGS += $(call installed_libs_fn, dpdk)
 endif
+ifeq ($($(PLATFORM)_uses_dpdk_cryptodev),yes)
+vpp_configure_args += --with-dpdk-crypto
+endif
 endif
diff --git a/build-data/platforms/vpp.mk b/build-data/platforms/vpp.mk
index 4fe7cc8..15d4dc3 100644
--- a/build-data/platforms/vpp.mk
+++ b/build-data/platforms/vpp.mk
@@ -41,6 +41,7 @@
 plugins_configure_args_vpp = --with-dpdk
 
 # DPDK configuration parameters
+# vpp_uses_dpdk_cryptodev = yes
 # vpp_uses_external_dpdk = yes
 # vpp_dpdk_inc_dir = /usr/include/dpdk
 # vpp_dpdk_lib_dir = /usr/lib
diff --git a/doxygen/user_doc.md b/doxygen/user_doc.md
index 5906376..2e87c87 100644
--- a/doxygen/user_doc.md
+++ b/doxygen/user_doc.md
@@ -6,6 +6,7 @@
 - [GUI guided user demo](https://wiki.fd.io/view/VPP_Sandbox/vpp-userdemo)
 - @subpage qos_doc
 - @subpage ipsec_gre_doc
+- @subpage dpdk_crypto_ipsec_doc
 - @subpage map_doc
 - @subpage lldp_doc
 - @subpage ioam_plugin_doc
diff --git a/dpdk/Makefile b/dpdk/Makefile
index 2f5037d..46cc3db 100644
--- a/dpdk/Makefile
+++ b/dpdk/Makefile
@@ -21,6 +21,7 @@
 DPDK_MARCH            ?= native
 DPDK_TUNE             ?= generic
 DPDK_DEBUG            ?= n
+DPDK_CRYPTO_PMD        ?= n
 
 B := $(DPDK_BUILD_DIR)
 I := $(DPDK_INSTALL_DIR)
@@ -118,6 +119,9 @@
 	$(call set,RTE_LIBRTE_VMXNET3_DEBUG_INIT,$(DPDK_DEBUG))
 	$(call set,RTE_LIBRTE_PMD_BOND,y)
 	$(call set,RTE_LIBRTE_IP_FRAG,y)
+	$(call set,RTE_LIBRTE_PMD_AESNI_MB,$(DPDK_CRYPTO_PMD))
+	$(call set,RTE_LIBRTE_PMD_AESNI_GCM,$(DPDK_CRYPTO_PMD))
+	$(call set,RTE_LIBRTE_PMD_QAT,$(DPDK_CRYPTO_PMD))
 	@# not needed
 	$(call set,RTE_LIBRTE_TIMER,n)
 	$(call set,RTE_LIBRTE_CFGFILE,n)
diff --git a/vnet/Makefile.am b/vnet/Makefile.am
index 86b7390..d806785 100644
--- a/vnet/Makefile.am
+++ b/vnet/Makefile.am
@@ -13,7 +13,7 @@
 
 AUTOMAKE_OPTIONS = foreign subdir-objects
 
-AM_CFLAGS = -Wall -Werror @DPDK@ @IPSEC@ @IPV6SR@
+AM_CFLAGS = -Wall -Werror @DPDK@ @DPDK_CRYPTO@ @IPSEC@ @IPV6SR@
 
 libvnet_la_SOURCES =
 libvnetplugin_la_SOURCES =
@@ -345,7 +345,15 @@
  vnet/ipsec/ikev2_cli.c				\
  vnet/ipsec/ikev2_payload.c			\
  vnet/ipsec/ikev2_format.c
-endif 
+if WITH_DPDK_CRYPTO
+libvnet_la_SOURCES +=       			\
+ vnet/devices/dpdk/ipsec/esp_encrypt.c		\
+ vnet/devices/dpdk/ipsec/esp_decrypt.c		\
+ vnet/devices/dpdk/ipsec/crypto_node.c		\
+ vnet/devices/dpdk/ipsec/cli.c			\
+ vnet/devices/dpdk/ipsec/ipsec.c
+endif
+endif
 
 libvnet_la_SOURCES +=       			\
  vnet/ipsec/ipsec_output.c
@@ -355,6 +363,11 @@
  vnet/ipsec/esp.h				\
  vnet/ipsec/ikev2.h                             \
  vnet/ipsec/ikev2_priv.h
+if WITH_DPDK_CRYPTO
+nobase_include_HEADERS +=     		        \
+ vnet/devices/dpdk/ipsec/ipsec.h		\
+ vnet/devices/dpdk/ipsec/esp.h
+endif
 
 ########################################
 # Layer 3 protocol: osi
diff --git a/vnet/configure.ac b/vnet/configure.ac
index 80de43a..6a5281b 100644
--- a/vnet/configure.ac
+++ b/vnet/configure.ac
@@ -12,6 +12,11 @@
             [with_dpdk=1],
             [with_dpdk=0])
 
+AC_ARG_WITH(dpdk_crypto,
+            AC_HELP_STRING([--with-dpdk-crypto],[Use DPDK cryptodev]),
+            [with_dpdk_crypto=1],
+            [with_dpdk_crypto=0])
+
 AC_ARG_WITH(ipsec,
             AC_HELP_STRING([--without-ipsec],[Disable ipsec]),
             [with_ipsec=0],
@@ -27,6 +32,9 @@
               [enable_tests=1],
               [enable_tests=0])
 
+AM_CONDITIONAL(WITH_DPDK_CRYPTO, test "$with_dpdk_crypto" = "1")
+AC_SUBST(DPDK_CRYPTO,[-DDPDK_CRYPTO=${with_dpdk_crypto}])
+
 AM_CONDITIONAL(WITH_DPDK, test "$with_dpdk" = "1")
 AC_SUBST(DPDK,[-DDPDK=${with_dpdk}])
 
diff --git a/vnet/vnet/devices/dpdk/ipsec/cli.c b/vnet/vnet/devices/dpdk/ipsec/cli.c
new file mode 100644
index 0000000..3b634e0
--- /dev/null
+++ b/vnet/vnet/devices/dpdk/ipsec/cli.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2016 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 <vnet/vnet.h>
+#include <vnet/devices/dpdk/ipsec/ipsec.h>
+
+static void
+dpdk_ipsec_show_mapping (vlib_main_t * vm, u16 detail_display)
+{
+  dpdk_crypto_main_t *dcm = &dpdk_crypto_main;
+  vlib_thread_main_t *tm = vlib_get_thread_main ();
+  u32 i, skip_master;
+
+  if (detail_display)
+    vlib_cli_output (vm, "worker\t%10s\t%15s\tdir\tdev\tqp\n",
+		     "cipher", "auth");
+  else
+    vlib_cli_output (vm, "worker\tcrypto device id(type)\n");
+
+  skip_master = vlib_num_workers () > 0;
+
+  for (i = 0; i < tm->n_vlib_mains; i++)
+    {
+      uword key, data;
+      u32 cpu_index = vlib_mains[i]->cpu_index;
+      crypto_worker_main_t *cwm = &dcm->workers_main[cpu_index];
+      u8 *s = 0;
+
+      if (skip_master)
+	{
+	  skip_master = 0;
+	  continue;
+	}
+
+      if (!detail_display)
+	{
+	  i32 last_cdev = -1;
+	  crypto_qp_data_t *qpd;
+
+	  s = format (s, "%u\t", cpu_index);
+
+	  /* *INDENT-OFF* */
+	  vec_foreach (qpd, cwm->qp_data)
+	    {
+	      u32 dev_id = qpd->dev_id;
+
+	      if ((u16) last_cdev != dev_id)
+		{
+		  struct rte_cryptodev_info cdev_info;
+
+		  rte_cryptodev_info_get (dev_id, &cdev_info);
+
+		  s = format(s, "%u(%s)\t", dev_id, cdev_info.feature_flags &
+			     RTE_CRYPTODEV_FF_HW_ACCELERATED ? "HW" : "SW");
+		}
+	      last_cdev = dev_id;
+	    }
+	  /* *INDENT-ON* */
+	  vlib_cli_output (vm, "%s", s);
+	}
+      else
+	{
+	  char cipher_str[15], auth_str[15];
+	  struct rte_cryptodev_capabilities cap;
+	  crypto_worker_qp_key_t *p_key = (crypto_worker_qp_key_t *) & key;
+	  /* *INDENT-OFF* */
+	  hash_foreach (key, data, cwm->algo_qp_map,
+	  ({
+	    cap.op = RTE_CRYPTO_OP_TYPE_SYMMETRIC;
+	    cap.sym.xform_type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+	    cap.sym.cipher.algo = p_key->cipher_algo;
+	    check_algo_is_supported (&cap, cipher_str);
+	    cap.op = RTE_CRYPTO_OP_TYPE_SYMMETRIC;
+	    cap.sym.xform_type = RTE_CRYPTO_SYM_XFORM_AUTH;
+	    cap.sym.auth.algo = p_key->auth_algo;
+	    check_algo_is_supported (&cap, auth_str);
+	    vlib_cli_output (vm, "%u\t%10s\t%15s\t%3s\t%u\t%u\n",
+			     vlib_mains[i]->cpu_index, cipher_str, auth_str,
+			     p_key->is_outbound ? "out" : "in",
+			     cwm->qp_data[data].dev_id,
+			     cwm->qp_data[data].qp_id);
+	  }));
+	  /* *INDENT-ON* */
+	}
+    }
+}
+
+static clib_error_t *
+lcore_cryptodev_map_fn (vlib_main_t * vm, unformat_input_t * input,
+			vlib_cli_command_t * cmd)
+{
+  unformat_input_t _line_input, *line_input = &_line_input;
+  u16 detail = 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, "verbose"))
+	detail = 1;
+      else
+	return clib_error_return (0, "parse error: '%U'",
+				  format_unformat_error, line_input);
+    }
+
+  unformat_free (line_input);
+
+  dpdk_ipsec_show_mapping (vm, detail);
+
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (lcore_cryptodev_map, static) = {
+    .path = "show crypto device mapping",
+    .short_help =
+    "show cryptodev device mapping <verbose>",
+    .function = lcore_cryptodev_map_fn,
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/vnet/vnet/devices/dpdk/ipsec/crypto_node.c b/vnet/vnet/devices/dpdk/ipsec/crypto_node.c
new file mode 100644
index 0000000..37d5e5f
--- /dev/null
+++ b/vnet/vnet/devices/dpdk/ipsec/crypto_node.c
@@ -0,0 +1,209 @@
+/*
+ *------------------------------------------------------------------
+ * crypto_node.c - DPDK Cryptodev input node
+ *
+ * Copyright (c) 2016 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/ip/ip.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/ipsec/ipsec.h>
+
+#include <vnet/devices/dpdk/ipsec/ipsec.h>
+
+#define foreach_dpdk_crypto_input_next		\
+  _(DROP, "error-drop")				\
+  _(ENCRYPT_POST, "dpdk-esp-encrypt-post")	\
+  _(DECRYPT_POST, "dpdk-esp-decrypt-post")
+
+typedef enum
+{
+#define _(f,s) DPDK_CRYPTO_INPUT_NEXT_##f,
+  foreach_dpdk_crypto_input_next
+#undef _
+    DPDK_CRYPTO_INPUT_N_NEXT,
+} dpdk_crypto_input_next_t;
+
+#define foreach_dpdk_crypto_input_error		\
+  _(DQ_COPS, "Crypto ops dequeued")		\
+  _(COP_FAILED, "Crypto op failed")
+
+typedef enum
+{
+#define _(f,s) DPDK_CRYPTO_INPUT_ERROR_##f,
+  foreach_dpdk_crypto_input_error
+#undef _
+    DPDK_CRYPTO_INPUT_N_ERROR,
+} dpdk_crypto_input_error_t;
+
+static char *dpdk_crypto_input_error_strings[] = {
+#define _(n, s) s,
+  foreach_dpdk_crypto_input_error
+#undef _
+};
+
+vlib_node_registration_t dpdk_crypto_input_node;
+
+typedef struct
+{
+  u32 cdev;
+  u32 qp;
+  u32 status;
+  u32 sa_idx;
+  u32 next_index;
+} dpdk_crypto_input_trace_t;
+
+static u8 *
+format_dpdk_crypto_input_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 *);
+  dpdk_crypto_input_trace_t *t = va_arg (*args, dpdk_crypto_input_trace_t *);
+
+  s = format (s, "dpdk_crypto: cryptodev-id %u queue-pair %u next-index %d",
+	      t->cdev, t->qp, t->next_index);
+
+  s = format (s, "status %u sa-idx %u\n", t->status, t->sa_idx);
+
+  return s;
+}
+
+static_always_inline u32
+dpdk_crypto_dequeue (vlib_main_t * vm, vlib_node_runtime_t * node,
+		     crypto_qp_data_t * qpd)
+{
+  u32 n_deq, *to_next = 0, next_index, n_cops, def_next_index;
+  struct rte_crypto_op **cops = qpd->cops;
+
+  if (qpd->inflights == 0)
+    return 0;
+
+  if (qpd->is_outbound)
+    def_next_index = DPDK_CRYPTO_INPUT_NEXT_ENCRYPT_POST;
+  else
+    def_next_index = DPDK_CRYPTO_INPUT_NEXT_DECRYPT_POST;
+
+  n_cops = rte_cryptodev_dequeue_burst (qpd->dev_id, qpd->qp_id,
+					cops, VLIB_FRAME_SIZE);
+  n_deq = n_cops;
+  next_index = def_next_index;
+
+  qpd->inflights -= n_cops;
+  ASSERT (qpd->inflights >= 0);
+
+  while (n_cops > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_cops > 0 && n_left_to_next > 0)
+	{
+	  u32 bi0, next0;
+	  vlib_buffer_t *b0 = 0;
+	  struct rte_crypto_op *cop;
+	  struct rte_crypto_sym_op *sym_cop;
+
+	  cop = cops[0];
+	  cops += 1;
+	  n_cops -= 1;
+	  n_left_to_next -= 1;
+
+	  next0 = def_next_index;
+
+	  if (PREDICT_FALSE (cop->status != RTE_CRYPTO_OP_STATUS_SUCCESS))
+	    {
+	      next0 = DPDK_CRYPTO_INPUT_NEXT_DROP;
+	      vlib_node_increment_counter (vm, dpdk_crypto_input_node.index,
+					   DPDK_CRYPTO_INPUT_ERROR_COP_FAILED,
+					   1);
+	    }
+	  cop->status = RTE_CRYPTO_OP_STATUS_NOT_PROCESSED;
+
+	  sym_cop = (struct rte_crypto_sym_op *) (cop + 1);
+	  b0 = vlib_buffer_from_rte_mbuf (sym_cop->m_src);
+	  bi0 = vlib_get_buffer_index (vm, b0);
+
+	  to_next[0] = bi0;
+	  to_next += 1;
+
+	  if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+	    {
+	      dpdk_crypto_input_trace_t *tr;
+	      tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
+	      tr->cdev = qpd->dev_id;
+	      tr->qp = qpd->qp_id;
+	      tr->status = cop->status;
+	      tr->next_index = next0;
+	      tr->sa_idx = vnet_buffer (b0)->ipsec.sad_index;
+	    }
+
+	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+					   n_left_to_next, bi0, next0);
+	}
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  crypto_free_cop (qpd, qpd->cops, n_deq);
+
+  vlib_node_increment_counter (vm, dpdk_crypto_input_node.index,
+			       DPDK_CRYPTO_INPUT_ERROR_DQ_COPS, n_deq);
+  return n_deq;
+}
+
+static uword
+dpdk_crypto_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
+		      vlib_frame_t * frame)
+{
+  u32 cpu_index = os_get_cpu_number ();
+  dpdk_crypto_main_t *dcm = &dpdk_crypto_main;
+  crypto_worker_main_t *cwm = &dcm->workers_main[cpu_index];
+  crypto_qp_data_t *qpd;
+  u32 n_deq = 0;
+
+  /* *INDENT-OFF* */
+  vec_foreach (qpd, cwm->qp_data)
+      n_deq += dpdk_crypto_dequeue(vm, node, qpd);
+  /* *INDENT-ON* */
+
+  return n_deq;
+}
+
+VLIB_REGISTER_NODE (dpdk_crypto_input_node) =
+{
+  .function = dpdk_crypto_input_fn,.name = "dpdk-crypto-input",.format_trace =
+    format_dpdk_crypto_input_trace,.type = VLIB_NODE_TYPE_INPUT,.state =
+    VLIB_NODE_STATE_DISABLED,.n_errors =
+    DPDK_CRYPTO_INPUT_N_ERROR,.error_strings =
+    dpdk_crypto_input_error_strings,.n_next_nodes =
+    DPDK_CRYPTO_INPUT_N_NEXT,.next_nodes =
+  {
+#define _(s,n) [DPDK_CRYPTO_INPUT_NEXT_##s] = n,
+    foreach_dpdk_crypto_input_next
+#undef _
+  }
+,};
+
+#if DPDK_CRYPTO==1
+VLIB_NODE_FUNCTION_MULTIARCH (dpdk_crypto_input_node, dpdk_crypto_input_fn)
+#endif
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/vnet/vnet/devices/dpdk/ipsec/dir.dox b/vnet/vnet/devices/dpdk/ipsec/dir.dox
new file mode 100644
index 0000000..ffebfc4
--- /dev/null
+++ b/vnet/vnet/devices/dpdk/ipsec/dir.dox
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2016 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.
+ */
+/**
+ @dir vnet/vnet/devices/dpdk/ipsec
+ @brief IPSec ESP encrypt/decrypt using DPDK Cryptodev API
+*/
diff --git a/vnet/vnet/devices/dpdk/ipsec/dpdk_crypto_ipsec_doc.md b/vnet/vnet/devices/dpdk/ipsec/dpdk_crypto_ipsec_doc.md
new file mode 100644
index 0000000..8089696
--- /dev/null
+++ b/vnet/vnet/devices/dpdk/ipsec/dpdk_crypto_ipsec_doc.md
@@ -0,0 +1,73 @@
+# VPP IPSec implementation using DPDK Cryptodev API    {#dpdk_crypto_ipsec_doc}
+
+This document is meant to contain all related information about implementation and usability.
+
+
+## VPP IPsec with DPDK Cryptodev
+
+DPDK Cryptodev is an asynchronous crypto API that supports both Hardware and Software implementations (for more details refer to [DPDK Cryptography Device Library documentation](http://dpdk.org/doc/guides/prog_guide/cryptodev_lib.html)).
+
+When DPDK Cryptodev support is enabled, the node graph is modified by adding and replacing some of the nodes.
+
+The following nodes are replaced:
+* esp-encrypt -> dpdk-esp-encrypt
+* esp-decrypt -> dpdk-esp-decrypt
+
+The following nodes are added:
+* dpdk-crypto-input : polling input node, basically dequeuing from crypto devices.
+* dpdk-esp-encrypt-post : internal node.
+* dpdk-esp-decrypt-post : internal node.
+
+
+### How to enable VPP IPSec with DPDK Cryptodev support
+
+To enable DPDK Cryptodev support (disabled by default), we need the following env option:
+
+    vpp_uses_dpdk_cryptodev=yes
+
+A couple of ways to achive this:
+* uncomment/add it in the platforms config (ie. build-data/platforms/vpp.mk)
+* set the option when building vpp (ie. make vpp_uses_dpdk_cryptodev=yes build-release)
+
+
+### Crypto Resources allocation
+
+VPP allocates crypto resources based on a best effort approach:
+* first allocate Hardware crypto resources, then Software.
+* if there are not enough crypto resources for all workers, all packets will be dropped if they reach ESP encrypt/decrypt nodes, displaying the warning:
+
+      0: dpdk_ipsec_init: not enough cryptodevs for ipsec
+
+
+### Configuration example
+
+No especial IPsec configuration is required.
+
+Once DPDK Cryptodev is enabled, the user just needs to provide cryptodevs in the startup.conf.
+
+Example startup.conf:
+
+```
+dpdk {
+    socket-mem 1024,1024
+    num-mbufs 131072
+    dev 0000:81:00.0
+    dev 0000:81:00.1
+    dev 0000:85:01.0
+    dev 0000:85:01.1
+    vdev cryptodev_aesni_mb_pmd,socket_id=1
+    vdev cryptodev_aesni_mb_pmd,socket_id=1
+}
+```
+
+In the above configuration:
+* 0000:85:01.0 and 0000:85:01.1 are crypto BDFs and they require the same driver binding as DPDK Ethernet devices but they do not support any extra configuration options.
+* Two AESNI-MB Software Cryptodev PMDs are created in NUMA node 1.
+
+For further details refer to [DPDK Crypto Device Driver documentation](http://dpdk.org/doc/guides/cryptodevs/index.html)
+
+### Operational data
+
+The following CLI command displays the Cryptodev/Worker mapping:
+
+    show crypto device mapping [verbose]
diff --git a/vnet/vnet/devices/dpdk/ipsec/esp.h b/vnet/vnet/devices/dpdk/ipsec/esp.h
new file mode 100644
index 0000000..71282ac
--- /dev/null
+++ b/vnet/vnet/devices/dpdk/ipsec/esp.h
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2016 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 __DPDK_ESP_H__
+#define __DPDK_ESP_H__
+
+#include <vnet/devices/dpdk/ipsec/ipsec.h>
+#include <vnet/ipsec/ipsec.h>
+#include <vnet/ipsec/esp.h>
+
+typedef struct
+{
+  enum rte_crypto_cipher_algorithm algo;
+  u8 key_len;
+  u8 iv_len;
+} dpdk_esp_crypto_alg_t;
+
+typedef struct
+{
+  enum rte_crypto_auth_algorithm algo;
+  u8 trunc_size;
+} dpdk_esp_integ_alg_t;
+
+typedef struct
+{
+  dpdk_esp_crypto_alg_t *esp_crypto_algs;
+  dpdk_esp_integ_alg_t *esp_integ_algs;
+} dpdk_esp_main_t;
+
+dpdk_esp_main_t dpdk_esp_main;
+
+static_always_inline void
+dpdk_esp_init ()
+{
+  dpdk_esp_main_t *em = &dpdk_esp_main;
+  dpdk_esp_integ_alg_t *i;
+  dpdk_esp_crypto_alg_t *c;
+
+  vec_validate (em->esp_crypto_algs, IPSEC_CRYPTO_N_ALG - 1);
+
+  c = &em->esp_crypto_algs[IPSEC_CRYPTO_ALG_AES_CBC_128];
+  c->algo = RTE_CRYPTO_CIPHER_AES_CBC;
+  c->key_len = 16;
+  c->iv_len = 16;
+
+  c = &em->esp_crypto_algs[IPSEC_CRYPTO_ALG_AES_CBC_192];
+  c->algo = RTE_CRYPTO_CIPHER_AES_CBC;
+  c->key_len = 24;
+  c->iv_len = 16;
+
+  c = &em->esp_crypto_algs[IPSEC_CRYPTO_ALG_AES_CBC_256];
+  c->algo = RTE_CRYPTO_CIPHER_AES_CBC;
+  c->key_len = 32;
+  c->iv_len = 16;
+
+  vec_validate (em->esp_integ_algs, IPSEC_INTEG_N_ALG - 1);
+
+  i = &em->esp_integ_algs[IPSEC_INTEG_ALG_SHA1_96];
+  i->algo = RTE_CRYPTO_AUTH_SHA1_HMAC;
+  i->trunc_size = 12;
+
+  i = &em->esp_integ_algs[IPSEC_INTEG_ALG_SHA_256_96];
+  i->algo = RTE_CRYPTO_AUTH_SHA256_HMAC;
+  i->trunc_size = 12;
+
+  i = &em->esp_integ_algs[IPSEC_INTEG_ALG_SHA_256_128];
+  i->algo = RTE_CRYPTO_AUTH_SHA256_HMAC;
+  i->trunc_size = 16;
+
+  i = &em->esp_integ_algs[IPSEC_INTEG_ALG_SHA_384_192];
+  i->algo = RTE_CRYPTO_AUTH_SHA384_HMAC;
+  i->trunc_size = 24;
+
+  i = &em->esp_integ_algs[IPSEC_INTEG_ALG_SHA_512_256];
+  i->algo = RTE_CRYPTO_AUTH_SHA512_HMAC;
+  i->trunc_size = 32;
+}
+
+static_always_inline int
+add_del_sa_sess (u32 sa_index, u8 is_add)
+{
+  dpdk_crypto_main_t *dcm = &dpdk_crypto_main;
+  crypto_worker_main_t *cwm;
+  u8 skip_master = vlib_num_workers () > 0;
+
+  /* *INDENT-OFF* */
+  vec_foreach (cwm, dcm->workers_main)
+    {
+      crypto_sa_session_t *sa_sess;
+      u8 is_outbound;
+
+      if (skip_master)
+	{
+	  skip_master = 0;
+	  continue;
+	}
+
+      for (is_outbound = 0; is_outbound < 2; is_outbound++)
+	{
+	  if (is_add)
+	    {
+	      pool_get (cwm->sa_sess_d[is_outbound], sa_sess);
+	    }
+	  else
+	    {
+	      u8 dev_id;
+
+	      sa_sess = pool_elt_at_index (cwm->sa_sess_d[is_outbound], sa_index);
+	      dev_id = cwm->qp_data[sa_sess->qp_index].dev_id;
+
+	      if (!sa_sess->sess)
+		continue;
+
+	      if (rte_cryptodev_sym_session_free(dev_id, sa_sess->sess))
+		{
+		  clib_warning("failed to free session");
+		  return -1;
+		}
+	      memset(sa_sess, 0, sizeof(sa_sess[0]));
+	    }
+	}
+    }
+  /* *INDENT-OFF* */
+
+  return 0;
+}
+
+static_always_inline int
+translate_crypto_algo(ipsec_crypto_alg_t crypto_algo,
+		      struct rte_crypto_sym_xform *cipher_xform)
+{
+  switch (crypto_algo)
+  {
+    case IPSEC_CRYPTO_ALG_NONE:
+      cipher_xform->cipher.algo = RTE_CRYPTO_CIPHER_NULL;
+      break;
+    case IPSEC_CRYPTO_ALG_AES_CBC_128:
+    case IPSEC_CRYPTO_ALG_AES_CBC_192:
+    case IPSEC_CRYPTO_ALG_AES_CBC_256:
+      cipher_xform->cipher.algo = RTE_CRYPTO_CIPHER_AES_CBC;
+      break;
+    default:
+      return -1;
+  }
+
+  cipher_xform->type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+
+  return 0;
+}
+
+static_always_inline int
+translate_integ_algo(ipsec_integ_alg_t integ_alg,
+		     struct rte_crypto_sym_xform *auth_xform)
+{
+  switch (integ_alg) {
+    case IPSEC_INTEG_ALG_NONE:
+      auth_xform->auth.algo = RTE_CRYPTO_AUTH_NULL;
+      auth_xform->auth.digest_length = 0;
+      break;
+    case IPSEC_INTEG_ALG_SHA1_96:
+      auth_xform->auth.algo = RTE_CRYPTO_AUTH_SHA1_HMAC;
+      auth_xform->auth.digest_length = 12;
+      break;
+    case IPSEC_INTEG_ALG_SHA_256_96:
+      auth_xform->auth.algo = RTE_CRYPTO_AUTH_SHA256_HMAC;
+      auth_xform->auth.digest_length = 12;
+      break;
+    case IPSEC_INTEG_ALG_SHA_256_128:
+      auth_xform->auth.algo = RTE_CRYPTO_AUTH_SHA256_HMAC;
+      auth_xform->auth.digest_length = 16;
+      break;
+    case IPSEC_INTEG_ALG_SHA_384_192:
+      auth_xform->auth.algo = RTE_CRYPTO_AUTH_SHA384_HMAC;
+      auth_xform->auth.digest_length = 24;
+      break;
+    case IPSEC_INTEG_ALG_SHA_512_256:
+      auth_xform->auth.algo = RTE_CRYPTO_AUTH_SHA512_HMAC;
+      auth_xform->auth.digest_length = 32;
+      break;
+    default:
+      return -1;
+  }
+
+  auth_xform->type = RTE_CRYPTO_SYM_XFORM_AUTH;
+
+  return 0;
+}
+
+static_always_inline int
+create_sym_sess(ipsec_sa_t *sa, crypto_sa_session_t *sa_sess, u8 is_outbound)
+{
+  u32 cpu_index = os_get_cpu_number();
+  dpdk_crypto_main_t * dcm = &dpdk_crypto_main;
+  crypto_worker_main_t *cwm = &dcm->workers_main[cpu_index];
+  struct rte_crypto_sym_xform cipher_xform = {0};
+  struct rte_crypto_sym_xform auth_xform = {0};
+  struct rte_crypto_sym_xform *xfs;
+  uword key = 0, *data;
+  crypto_worker_qp_key_t *p_key = (crypto_worker_qp_key_t *)&key;
+
+  cipher_xform.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
+  cipher_xform.cipher.key.data = sa->crypto_key;
+  cipher_xform.cipher.key.length = sa->crypto_key_len;
+
+  auth_xform.type = RTE_CRYPTO_SYM_XFORM_AUTH;
+  auth_xform.auth.key.data = sa->integ_key;
+  auth_xform.auth.key.length = sa->integ_key_len;
+
+  if (translate_crypto_algo(sa->crypto_alg, &cipher_xform) < 0)
+    return -1;
+  p_key->cipher_algo = cipher_xform.cipher.algo;
+
+  if (translate_integ_algo(sa->integ_alg, &auth_xform) < 0)
+    return -1;
+  p_key->auth_algo = auth_xform.auth.algo;
+
+  if (is_outbound)
+    {
+      cipher_xform.cipher.op = RTE_CRYPTO_CIPHER_OP_ENCRYPT;
+      auth_xform.auth.op = RTE_CRYPTO_AUTH_OP_GENERATE;
+      cipher_xform.next = &auth_xform;
+      xfs = &cipher_xform;
+    }
+  else
+    {
+      cipher_xform.cipher.op = RTE_CRYPTO_CIPHER_OP_DECRYPT;
+      auth_xform.auth.op = RTE_CRYPTO_AUTH_OP_VERIFY;
+      auth_xform.next = &cipher_xform;
+      xfs = &auth_xform;
+    }
+
+  p_key->is_outbound = is_outbound;
+
+  data = hash_get(cwm->algo_qp_map, key);
+  if (!data)
+    return -1;
+
+  sa_sess->sess =
+    rte_cryptodev_sym_session_create(cwm->qp_data[*data].dev_id, xfs);
+
+  if (!sa_sess->sess)
+    return -1;
+
+  sa_sess->qp_index = (u8)*data;
+
+  return 0;
+}
+
+#endif /* __DPDK_ESP_H__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/vnet/vnet/devices/dpdk/ipsec/esp_decrypt.c b/vnet/vnet/devices/dpdk/ipsec/esp_decrypt.c
new file mode 100644
index 0000000..c898d05
--- /dev/null
+++ b/vnet/vnet/devices/dpdk/ipsec/esp_decrypt.c
@@ -0,0 +1,549 @@
+/*
+ * esp_decrypt.c : IPSec ESP Decrypt node using DPDK Cryptodev
+ *
+ * Copyright (c) 2016 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 <vnet/vnet.h>
+#include <vnet/api_errno.h>
+#include <vnet/ip/ip.h>
+
+#include <vnet/ipsec/ipsec.h>
+#include <vnet/devices/dpdk/ipsec/ipsec.h>
+#include <vnet/devices/dpdk/ipsec/esp.h>
+
+#define foreach_esp_decrypt_next	       \
+_(DROP, "error-drop")			       \
+_(IP4_INPUT, "ip4-input")		       \
+_(IP6_INPUT, "ip6-input")
+
+#define _(v, s) ESP_DECRYPT_NEXT_##v,
+typedef enum {
+  foreach_esp_decrypt_next
+#undef _
+  ESP_DECRYPT_N_NEXT,
+} esp_decrypt_next_t;
+
+#define foreach_esp_decrypt_error		 \
+ _(RX_PKTS, "ESP pkts received")		 \
+ _(DECRYPTION_FAILED, "ESP decryption failed")   \
+ _(REPLAY, "SA replayed packet")	         \
+ _(NOT_IP, "Not IP packet (dropped)")	         \
+ _(ENQ_FAIL, "Enqueue failed (buffer full)")     \
+ _(NO_CRYPTODEV, "Cryptodev not configured")     \
+ _(BAD_LEN, "Invalid ciphertext length")         \
+ _(UNSUPPORTED, "Cipher/Auth not supported")
+
+
+typedef enum {
+#define _(sym,str) ESP_DECRYPT_ERROR_##sym,
+  foreach_esp_decrypt_error
+#undef _
+  ESP_DECRYPT_N_ERROR,
+} esp_decrypt_error_t;
+
+static char * esp_decrypt_error_strings[] = {
+#define _(sym,string) string,
+  foreach_esp_decrypt_error
+#undef _
+};
+
+vlib_node_registration_t dpdk_esp_decrypt_node;
+
+typedef struct {
+  ipsec_crypto_alg_t crypto_alg;
+  ipsec_integ_alg_t integ_alg;
+} esp_decrypt_trace_t;
+
+/* packet trace format function */
+static u8 * format_esp_decrypt_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_decrypt_trace_t * t = va_arg (*args, esp_decrypt_trace_t *);
+
+  s = format (s, "esp: crypto %U integrity %U",
+	      format_ipsec_crypto_alg, t->crypto_alg,
+	      format_ipsec_integ_alg, t->integ_alg);
+  return s;
+}
+
+static uword
+dpdk_esp_decrypt_node_fn (vlib_main_t * vm,
+	     vlib_node_runtime_t * node,
+	     vlib_frame_t * from_frame)
+{
+  u32 n_left_from, *from, *to_next, next_index;
+  ipsec_main_t *im = &ipsec_main;
+  u32 cpu_index = os_get_cpu_number();
+  dpdk_crypto_main_t * dcm = &dpdk_crypto_main;
+  dpdk_esp_main_t * em = &dpdk_esp_main;
+  u32 i;
+
+  from = vlib_frame_vector_args (from_frame);
+  n_left_from = from_frame->n_vectors;
+
+  if (PREDICT_FALSE(!dcm->workers_main))
+    {
+      vlib_node_increment_counter (vm, dpdk_esp_decrypt_node.index,
+	      ESP_DECRYPT_ERROR_NO_CRYPTODEV, n_left_from);
+      vlib_buffer_free(vm, from, n_left_from);
+      return n_left_from;
+    }
+
+  crypto_worker_main_t *cwm = vec_elt_at_index(dcm->workers_main, cpu_index);
+  u32 n_qps = vec_len(cwm->qp_data);
+  struct rte_crypto_op ** cops_to_enq[n_qps];
+  u32 n_cop_qp[n_qps], * bi_to_enq[n_qps];
+
+  for (i = 0; i < n_qps; i++)
+    {
+      bi_to_enq[i] = cwm->qp_data[i].bi;
+      cops_to_enq[i] = cwm->qp_data[i].cops;
+    }
+
+  memset(n_cop_qp, 0, n_qps * sizeof(u32));
+
+  crypto_alloc_cops();
+
+  next_index = ESP_DECRYPT_NEXT_DROP;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+	{
+	  u32 bi0, sa_index0 = ~0, seq, icv_size;
+	  vlib_buffer_t * b0;
+	  esp_header_t * esp0;
+	  ipsec_sa_t * sa0;
+	  struct rte_mbuf * mb0 = 0;
+	  const int BLOCK_SIZE = 16;
+	  const int IV_SIZE = 16;
+	  crypto_sa_session_t * sa_sess;
+	  void * sess;
+	  u16 qp_index;
+	  struct rte_crypto_op * cop = 0;
+
+	  bi0 = from[0];
+	  from += 1;
+	  n_left_from -= 1;
+
+	  b0 = vlib_get_buffer (vm, bi0);
+	  esp0 = vlib_buffer_get_current (b0);
+
+	  sa_index0 = vnet_buffer(b0)->ipsec.sad_index;
+	  sa0 = pool_elt_at_index (im->sad, sa_index0);
+
+	  seq = clib_host_to_net_u32(esp0->seq);
+
+	  /* anti-replay check */
+	  if (sa0->use_anti_replay)
+	    {
+	      int rv = 0;
+
+	      if (PREDICT_TRUE(sa0->use_esn))
+		rv = esp_replay_check_esn(sa0, seq);
+	      else
+		rv = esp_replay_check(sa0, seq);
+
+	      if (PREDICT_FALSE(rv))
+		{
+		  clib_warning ("anti-replay SPI %u seq %u", sa0->spi, seq);
+		  vlib_node_increment_counter (vm, dpdk_esp_decrypt_node.index,
+					       ESP_DECRYPT_ERROR_REPLAY, 1);
+		  to_next[0] = bi0;
+		  to_next += 1;
+		  n_left_to_next -= 1;
+		  goto trace;
+		}
+	    }
+
+	  if (PREDICT_FALSE(sa0->integ_alg == IPSEC_INTEG_ALG_NONE) ||
+		  PREDICT_FALSE(sa0->crypto_alg == IPSEC_CRYPTO_ALG_NONE))
+	    {
+	      clib_warning ("SPI %u : only cipher + auth supported", sa0->spi);
+	      vlib_node_increment_counter (vm, dpdk_esp_decrypt_node.index,
+					   ESP_DECRYPT_ERROR_UNSUPPORTED, 1);
+	      to_next[0] = bi0;
+	      to_next += 1;
+	      n_left_to_next -= 1;
+	      goto trace;
+	    }
+
+	  sa_sess = pool_elt_at_index(cwm->sa_sess_d[0], sa_index0);
+
+	  if (PREDICT_FALSE(!sa_sess->sess))
+	    {
+	      int ret = create_sym_sess(sa0, sa_sess, 0);
+	      ASSERT(ret == 0);
+	    }
+
+	  sess = sa_sess->sess;
+	  qp_index = sa_sess->qp_index;
+
+	  ASSERT (vec_len (vec_elt (cwm->qp_data, qp_index).free_cops) > 0);
+	  cop = vec_pop (vec_elt (cwm->qp_data, qp_index).free_cops);
+	  ASSERT (cop->status == RTE_CRYPTO_OP_STATUS_NOT_PROCESSED);
+
+	  cops_to_enq[qp_index][0] = cop;
+	  cops_to_enq[qp_index] += 1;
+	  n_cop_qp[qp_index] += 1;
+	  bi_to_enq[qp_index][0] = bi0;
+	  bi_to_enq[qp_index] += 1;
+
+	  rte_crypto_op_attach_sym_session(cop, sess);
+
+	  icv_size = em->esp_integ_algs[sa0->integ_alg].trunc_size;
+
+	  /* Convert vlib buffer to mbuf */
+	  mb0 = rte_mbuf_from_vlib_buffer(b0);
+	  mb0->data_len = b0->current_length;
+	  mb0->pkt_len = b0->current_length;
+	  mb0->data_off = RTE_PKTMBUF_HEADROOM + b0->current_data;
+
+	  /* Outer IP header has already been stripped */
+	  u16 payload_len = rte_pktmbuf_pkt_len(mb0) - sizeof (esp_header_t) -
+	      IV_SIZE - icv_size;
+
+	  if ((payload_len & (BLOCK_SIZE - 1)) || (payload_len <= 0))
+	    {
+	      clib_warning ("payload %u not multiple of %d\n",
+			    payload_len, BLOCK_SIZE);
+	      vlib_node_increment_counter (vm, dpdk_esp_decrypt_node.index,
+					   ESP_DECRYPT_ERROR_BAD_LEN, 1);
+	      vec_add (vec_elt (cwm->qp_data, qp_index).free_cops, &cop, 1);
+	      bi_to_enq[qp_index] -= 1;
+	      cops_to_enq[qp_index] -= 1;
+	      n_cop_qp[qp_index] -= 1;
+	      to_next[0] = bi0;
+	      to_next += 1;
+	      n_left_to_next -= 1;
+	      goto trace;
+	    }
+
+	  struct rte_crypto_sym_op *sym_cop = (struct rte_crypto_sym_op *)(cop + 1);
+
+	  sym_cop->m_src = mb0;
+	  sym_cop->cipher.data.offset = sizeof (esp_header_t) + IV_SIZE;
+	  sym_cop->cipher.data.length = payload_len;
+
+	  sym_cop->cipher.iv.data =
+	    rte_pktmbuf_mtod_offset(mb0, void*, sizeof (esp_header_t));
+	  sym_cop->cipher.iv.phys_addr =
+	    rte_pktmbuf_mtophys_offset(mb0, sizeof (esp_header_t));
+	  sym_cop->cipher.iv.length = IV_SIZE;
+
+	  if (sa0->use_esn)
+	    {
+	      dpdk_cop_priv_t* priv = (dpdk_cop_priv_t*) (sym_cop + 1);
+	      u8* payload_end =
+		rte_pktmbuf_mtod_offset(mb0, u8*, sizeof(esp_header_t) + IV_SIZE +
+					payload_len);
+
+	      memcpy (priv->icv, payload_end, icv_size);
+	      *((u32*) payload_end) = sa0->seq_hi;
+	      sym_cop->auth.data.offset = 0;
+	      sym_cop->auth.data.length =
+		sizeof(esp_header_t) + IV_SIZE + payload_len + sizeof(sa0->seq_hi);
+	      sym_cop->auth.digest.data = priv->icv;
+	      sym_cop->auth.digest.phys_addr =
+		cop->phys_addr + (uintptr_t) priv->icv - (uintptr_t) cop;
+	      sym_cop->auth.digest.length = icv_size;
+	    }
+	  else
+	    {
+	      sym_cop->auth.data.offset = 0;
+	      sym_cop->auth.data.length = sizeof(esp_header_t) +
+		IV_SIZE + payload_len;
+
+	      sym_cop->auth.digest.data =
+		rte_pktmbuf_mtod_offset(mb0, void*,
+					rte_pktmbuf_pkt_len(mb0) - icv_size);
+	      sym_cop->auth.digest.phys_addr =
+		rte_pktmbuf_mtophys_offset(mb0,
+					   rte_pktmbuf_pkt_len(mb0) - icv_size);
+	      sym_cop->auth.digest.length = icv_size;
+	    }
+
+trace:
+	  if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
+	    {
+	      esp_decrypt_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
+	      tr->crypto_alg = sa0->crypto_alg;
+	      tr->integ_alg = sa0->integ_alg;
+	    }
+	}
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+  vlib_node_increment_counter (vm, dpdk_esp_decrypt_node.index,
+			       ESP_DECRYPT_ERROR_RX_PKTS,
+			       from_frame->n_vectors);
+  crypto_qp_data_t *qpd;
+  /* *INDENT-OFF* */
+  vec_foreach_index (i, cwm->qp_data)
+    {
+      u32 enq;
+
+      qpd = vec_elt_at_index(cwm->qp_data, i);
+      enq = rte_cryptodev_enqueue_burst(qpd->dev_id, qpd->qp_id,
+					qpd->cops, n_cop_qp[i]);
+      qpd->inflights += enq;
+
+      if (PREDICT_FALSE(enq < n_cop_qp[i]))
+	{
+	  crypto_free_cop (qpd, &qpd->cops[enq], n_cop_qp[i] - enq);
+	  vlib_buffer_free (vm, &qpd->bi[enq], n_cop_qp[i] - enq);
+
+	  vlib_node_increment_counter (vm, dpdk_esp_decrypt_node.index,
+				       ESP_DECRYPT_ERROR_ENQ_FAIL,
+				       n_cop_qp[i] - enq);
+	}
+    }
+  /* *INDENT-ON* */
+
+  return from_frame->n_vectors;
+}
+
+VLIB_REGISTER_NODE (dpdk_esp_decrypt_node) = {
+  .function = dpdk_esp_decrypt_node_fn,
+  .name = "dpdk-esp-decrypt",
+  .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,
+
+  .n_next_nodes = ESP_DECRYPT_N_NEXT,
+  .next_nodes = {
+#define _(s,n) [ESP_DECRYPT_NEXT_##s] = n,
+    foreach_esp_decrypt_next
+#undef _
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (dpdk_esp_decrypt_node, dpdk_esp_decrypt_node_fn)
+
+/*
+ * Decrypt Post Node
+ */
+
+#define foreach_esp_decrypt_post_error	      \
+ _(PKTS, "ESP post pkts")
+
+typedef enum {
+#define _(sym,str) ESP_DECRYPT_POST_ERROR_##sym,
+  foreach_esp_decrypt_post_error
+#undef _
+  ESP_DECRYPT_POST_N_ERROR,
+} esp_decrypt_post_error_t;
+
+static char * esp_decrypt_post_error_strings[] = {
+#define _(sym,string) string,
+  foreach_esp_decrypt_post_error
+#undef _
+};
+
+vlib_node_registration_t dpdk_esp_decrypt_post_node;
+
+static u8 * format_esp_decrypt_post_trace (u8 * s, va_list * args)
+{
+  return s;
+}
+
+static uword
+dpdk_esp_decrypt_post_node_fn (vlib_main_t * vm,
+	     vlib_node_runtime_t * node,
+	     vlib_frame_t * from_frame)
+{
+  u32 n_left_from, *from, *to_next = 0, next_index;
+  ipsec_sa_t * sa0;
+  u32 sa_index0 = ~0;
+  ipsec_main_t *im = &ipsec_main;
+  dpdk_esp_main_t *em = &dpdk_esp_main;
+
+  from = vlib_frame_vector_args (from_frame);
+  n_left_from = from_frame->n_vectors;
+
+  next_index = node->cached_next_index;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+	{
+	  esp_footer_t * f0;
+	  const u32 IV_SIZE = 16;
+	  u32 bi0, next0, icv_size;
+	  vlib_buffer_t * b0 = 0;
+	  ip4_header_t *ih4 = 0, *oh4 = 0;
+	  ip6_header_t *ih6 = 0, *oh6 = 0;
+	  u8 tunnel_mode = 1;
+	  u8 transport_ip6 = 0;
+
+	  next0 = ESP_DECRYPT_NEXT_DROP;
+
+	  bi0 = from[0];
+	  from += 1;
+	  n_left_from -= 1;
+	  n_left_to_next -= 1;
+
+	  b0 = vlib_get_buffer (vm, bi0);
+
+	  sa_index0 = vnet_buffer(b0)->ipsec.sad_index;
+	  sa0 = pool_elt_at_index (im->sad, sa_index0);
+
+	  to_next[0] = bi0;
+	  to_next += 1;
+
+	  icv_size = em->esp_integ_algs[sa0->integ_alg].trunc_size;
+
+	  if (sa0->use_anti_replay)
+	    {
+	      esp_header_t * esp0 = vlib_buffer_get_current (b0);
+	      u32 seq;
+	      seq = clib_host_to_net_u32(esp0->seq);
+	      if (PREDICT_TRUE(sa0->use_esn))
+		esp_replay_advance_esn(sa0, seq);
+	      else
+		esp_replay_advance(sa0, seq);
+	    }
+
+	  ih4 = (ip4_header_t *) (b0->data + sizeof(ethernet_header_t));
+	  vlib_buffer_advance (b0, sizeof (esp_header_t) + IV_SIZE);
+
+	  b0->current_length -= (icv_size + 2);
+	  b0->flags = VLIB_BUFFER_TOTAL_LENGTH_VALID;
+	  f0 = (esp_footer_t *) ((u8 *) vlib_buffer_get_current (b0) +
+				 b0->current_length);
+	  b0->current_length -= f0->pad_length;
+
+	  /* transport mode */
+	  if (PREDICT_FALSE(!sa0->is_tunnel && !sa0->is_tunnel_ip6))
+	    {
+	      tunnel_mode = 0;
+
+	      if (PREDICT_TRUE((ih4->ip_version_and_header_length & 0xF0) != 0x40))
+		{
+		  if (PREDICT_TRUE((ih4->ip_version_and_header_length & 0xF0) == 0x60))
+		    transport_ip6 = 1;
+		  else
+		    {
+		      clib_warning("next header: 0x%x", f0->next_header);
+		      vlib_node_increment_counter (vm, dpdk_esp_decrypt_node.index,
+						   ESP_DECRYPT_ERROR_NOT_IP, 1);
+		      goto trace;
+		    }
+		}
+	    }
+
+	  if (PREDICT_TRUE (tunnel_mode))
+	    {
+	      if (PREDICT_TRUE(f0->next_header == IP_PROTOCOL_IP_IN_IP))
+		next0 = ESP_DECRYPT_NEXT_IP4_INPUT;
+	      else if (f0->next_header == IP_PROTOCOL_IPV6)
+		next0 = ESP_DECRYPT_NEXT_IP6_INPUT;
+	      else
+		{
+		  clib_warning("next header: 0x%x", f0->next_header);
+		  vlib_node_increment_counter (vm, dpdk_esp_decrypt_node.index,
+					       ESP_DECRYPT_ERROR_DECRYPTION_FAILED,
+					       1);
+		  goto trace;
+		}
+	    }
+	  /* transport mode */
+	  else
+	    {
+	      if (PREDICT_FALSE(transport_ip6))
+		{
+		  next0 = ESP_DECRYPT_NEXT_IP6_INPUT;
+		  ih6 = (ip6_header_t *) (b0->data + sizeof(ethernet_header_t));
+		  vlib_buffer_advance (b0, -sizeof(ip6_header_t));
+		  oh6 = vlib_buffer_get_current (b0);
+		  memmove(oh6, ih6, sizeof(ip6_header_t));
+
+		  oh6->protocol = f0->next_header;
+		  oh6->payload_length =
+		    clib_host_to_net_u16 (vlib_buffer_length_in_chain(vm, b0) -
+					  sizeof (ip6_header_t));
+		}
+	      else
+		{
+		  next0 = ESP_DECRYPT_NEXT_IP4_INPUT;
+		  vlib_buffer_advance (b0, -sizeof(ip4_header_t));
+		  oh4 = vlib_buffer_get_current (b0);
+
+		  oh4->ip_version_and_header_length = 0x45;
+		  oh4->tos = ih4->tos;
+		  oh4->fragment_id = 0;
+		  oh4->flags_and_fragment_offset = 0;
+		  oh4->ttl = ih4->ttl;
+		  oh4->protocol = f0->next_header;
+		  oh4->src_address.as_u32 = ih4->src_address.as_u32;
+		  oh4->dst_address.as_u32 = ih4->dst_address.as_u32;
+		  oh4->length =
+		    clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
+		  oh4->checksum = ip4_header_checksum (oh4);
+		}
+	    }
+
+	  vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32)~0;
+
+trace:
+	  if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
+	    {
+	      esp_decrypt_trace_t *tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
+	      tr->crypto_alg = sa0->crypto_alg;
+	      tr->integ_alg = sa0->integ_alg;
+	    }
+
+	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+					   to_next, n_left_to_next, bi0, next0);
+	}
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+  vlib_node_increment_counter (vm, dpdk_esp_decrypt_post_node.index,
+			       ESP_DECRYPT_POST_ERROR_PKTS,
+			       from_frame->n_vectors);
+
+  return from_frame->n_vectors;
+}
+
+VLIB_REGISTER_NODE (dpdk_esp_decrypt_post_node) = {
+  .function = dpdk_esp_decrypt_post_node_fn,
+  .name = "dpdk-esp-decrypt-post",
+  .vector_size = sizeof (u32),
+  .format_trace = format_esp_decrypt_post_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = ARRAY_LEN(esp_decrypt_post_error_strings),
+  .error_strings = esp_decrypt_post_error_strings,
+
+  .n_next_nodes = ESP_DECRYPT_N_NEXT,
+  .next_nodes = {
+#define _(s,n) [ESP_DECRYPT_NEXT_##s] = n,
+    foreach_esp_decrypt_next
+#undef _
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (dpdk_esp_decrypt_post_node, dpdk_esp_decrypt_post_node_fn)
diff --git a/vnet/vnet/devices/dpdk/ipsec/esp_encrypt.c b/vnet/vnet/devices/dpdk/ipsec/esp_encrypt.c
new file mode 100644
index 0000000..aef4b90
--- /dev/null
+++ b/vnet/vnet/devices/dpdk/ipsec/esp_encrypt.c
@@ -0,0 +1,554 @@
+/*
+ * esp_encrypt.c : IPSec ESP encrypt node using DPDK Cryptodev
+ *
+ * Copyright (c) 2016 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 <vnet/vnet.h>
+#include <vnet/api_errno.h>
+#include <vnet/ip/ip.h>
+
+#include <vnet/ipsec/ipsec.h>
+#include <vnet/devices/dpdk/ipsec/ipsec.h>
+#include <vnet/devices/dpdk/ipsec/esp.h>
+
+#define foreach_esp_encrypt_next                   \
+_(DROP, "error-drop")                              \
+_(IP4_INPUT, "ip4-input")                          \
+_(IP6_INPUT, "ip6-input")                          \
+_(INTERFACE_OUTPUT, "interface-output")
+
+#define _(v, s) ESP_ENCRYPT_NEXT_##v,
+typedef enum
+{
+  foreach_esp_encrypt_next
+#undef _
+    ESP_ENCRYPT_N_NEXT,
+} esp_encrypt_next_t;
+
+#define foreach_esp_encrypt_error                   \
+ _(RX_PKTS, "ESP pkts received")                    \
+ _(SEQ_CYCLED, "sequence number cycled")            \
+ _(ENQ_FAIL, "Enqueue failed (buffer full)")        \
+ _(NO_CRYPTODEV, "Cryptodev not configured")        \
+ _(UNSUPPORTED, "Cipher/Auth not supported")
+
+
+typedef enum
+{
+#define _(sym,str) ESP_ENCRYPT_ERROR_##sym,
+  foreach_esp_encrypt_error
+#undef _
+    ESP_ENCRYPT_N_ERROR,
+} esp_encrypt_error_t;
+
+static char *esp_encrypt_error_strings[] = {
+#define _(sym,string) string,
+  foreach_esp_encrypt_error
+#undef _
+};
+
+vlib_node_registration_t dpdk_esp_encrypt_node;
+
+typedef struct
+{
+  u32 spi;
+  u32 seq;
+  ipsec_crypto_alg_t crypto_alg;
+  ipsec_integ_alg_t integ_alg;
+} esp_encrypt_trace_t;
+
+/* packet trace format function */
+static u8 *
+format_esp_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_trace_t *t = va_arg (*args, esp_encrypt_trace_t *);
+
+  s = format (s, "esp: spi %u seq %u crypto %U integrity %U",
+	      t->spi, t->seq,
+	      format_ipsec_crypto_alg, t->crypto_alg,
+	      format_ipsec_integ_alg, t->integ_alg);
+  return s;
+}
+
+static uword
+dpdk_esp_encrypt_node_fn (vlib_main_t * vm,
+			  vlib_node_runtime_t * node,
+			  vlib_frame_t * from_frame)
+{
+  u32 n_left_from, *from, *to_next, next_index;
+  ipsec_main_t *im = &ipsec_main;
+  u32 cpu_index = os_get_cpu_number ();
+  dpdk_crypto_main_t *dcm = &dpdk_crypto_main;
+  dpdk_esp_main_t *em = &dpdk_esp_main;
+  u32 i;
+
+  from = vlib_frame_vector_args (from_frame);
+  n_left_from = from_frame->n_vectors;
+
+  if (PREDICT_FALSE (!dcm->workers_main))
+    {
+      /* Likely there are not enough cryptodevs, so drop frame */
+      vlib_node_increment_counter (vm, dpdk_esp_encrypt_node.index,
+				   ESP_ENCRYPT_ERROR_NO_CRYPTODEV,
+				   n_left_from);
+      vlib_buffer_free (vm, from, n_left_from);
+      return n_left_from;
+    }
+
+  crypto_worker_main_t *cwm = vec_elt_at_index (dcm->workers_main, cpu_index);
+  u32 n_qps = vec_len (cwm->qp_data);
+  struct rte_crypto_op **cops_to_enq[n_qps];
+  u32 n_cop_qp[n_qps], *bi_to_enq[n_qps];
+
+  for (i = 0; i < n_qps; i++)
+    {
+      bi_to_enq[i] = cwm->qp_data[i].bi;
+      cops_to_enq[i] = cwm->qp_data[i].cops;
+    }
+
+  memset (n_cop_qp, 0, n_qps * sizeof (u32));
+
+  crypto_alloc_cops ();
+
+  next_index = ESP_ENCRYPT_NEXT_DROP;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+	{
+	  u32 bi0, next0;
+	  vlib_buffer_t *b0 = 0;
+	  u32 sa_index0;
+	  ipsec_sa_t *sa0;
+	  ip4_and_esp_header_t *ih0, *oh0 = 0;
+	  ip6_and_esp_header_t *ih6_0, *oh6_0 = 0;
+	  struct rte_mbuf *mb0 = 0;
+	  esp_footer_t *f0;
+	  u8 is_ipv6;
+	  u8 ip_hdr_size;
+	  u8 next_hdr_type;
+	  u8 transport_mode = 0;
+	  const int BLOCK_SIZE = 16;
+	  const int IV_SIZE = 16;
+	  u16 orig_sz;
+	  crypto_sa_session_t *sa_sess;
+	  void *sess;
+	  struct rte_crypto_op *cop = 0;
+	  u16 qp_index;
+
+	  bi0 = from[0];
+	  from += 1;
+	  n_left_from -= 1;
+
+	  b0 = vlib_get_buffer (vm, bi0);
+	  sa_index0 = vnet_buffer (b0)->ipsec.sad_index;
+	  sa0 = pool_elt_at_index (im->sad, sa_index0);
+
+	  if (PREDICT_FALSE (esp_seq_advance (sa0)))
+	    {
+	      clib_warning ("sequence number counter has cycled SPI %u",
+			    sa0->spi);
+	      vlib_node_increment_counter (vm, dpdk_esp_encrypt_node.index,
+					   ESP_ENCRYPT_ERROR_SEQ_CYCLED, 1);
+	      //TODO: rekey SA
+	      to_next[0] = bi0;
+	      to_next += 1;
+	      n_left_to_next -= 1;
+	      goto trace;
+	    }
+
+	  sa_sess = pool_elt_at_index (cwm->sa_sess_d[1], sa_index0);
+	  if (PREDICT_FALSE (!sa_sess->sess))
+	    {
+	      int ret = create_sym_sess (sa0, sa_sess, 1);
+	      ASSERT (ret == 0);
+	    }
+
+	  qp_index = sa_sess->qp_index;
+	  sess = sa_sess->sess;
+
+	  ASSERT (vec_len (vec_elt (cwm->qp_data, qp_index).free_cops) > 0);
+	  cop = vec_pop (vec_elt (cwm->qp_data, qp_index).free_cops);
+	  ASSERT (cop->status == RTE_CRYPTO_OP_STATUS_NOT_PROCESSED);
+
+	  cops_to_enq[qp_index][0] = cop;
+	  cops_to_enq[qp_index] += 1;
+	  n_cop_qp[qp_index] += 1;
+	  bi_to_enq[qp_index][0] = bi0;
+	  bi_to_enq[qp_index] += 1;
+
+	  ssize_t adv;
+	  ih0 = vlib_buffer_get_current (b0);
+	  orig_sz = b0->current_length;
+	  is_ipv6 = (ih0->ip4.ip_version_and_header_length & 0xF0) == 0x60;
+	  /* is ipv6 */
+	  if (PREDICT_TRUE (sa0->is_tunnel))
+	    {
+	      if (PREDICT_TRUE (!is_ipv6))
+		adv = -sizeof (ip4_and_esp_header_t);
+	      else
+		adv = -sizeof (ip6_and_esp_header_t);
+	    }
+	  else
+	    {
+	      adv = -sizeof (esp_header_t);
+	      if (PREDICT_TRUE (!is_ipv6))
+		orig_sz -= sizeof (ip4_header_t);
+	      else
+		orig_sz -= sizeof (ip6_header_t);
+	    }
+
+	  /*transport mode save the eth header before it is overwritten */
+	  if (PREDICT_FALSE (!sa0->is_tunnel))
+	    {
+	      ethernet_header_t *ieh0 = (ethernet_header_t *)
+		((u8 *) vlib_buffer_get_current (b0) -
+		 sizeof (ethernet_header_t));
+	      ethernet_header_t *oeh0 =
+		(ethernet_header_t *) ((u8 *) ieh0 + (adv - IV_SIZE));
+	      clib_memcpy (oeh0, ieh0, sizeof (ethernet_header_t));
+	    }
+
+	  vlib_buffer_advance (b0, adv - IV_SIZE);
+
+	  /* XXX IP6/ip4 and IP4/IP6 not supported, only IP4/IP4 and IP6/IP6 */
+
+	  /* is ipv6 */
+	  if (PREDICT_FALSE (is_ipv6))
+	    {
+	      ih6_0 = (ip6_and_esp_header_t *) ih0;
+	      ip_hdr_size = sizeof (ip6_header_t);
+	      oh6_0 = vlib_buffer_get_current (b0);
+
+	      if (PREDICT_TRUE (sa0->is_tunnel))
+		{
+		  next_hdr_type = IP_PROTOCOL_IPV6;
+		  oh6_0->ip6.ip_version_traffic_class_and_flow_label =
+		    ih6_0->ip6.ip_version_traffic_class_and_flow_label;
+		}
+	      else
+		{
+		  next_hdr_type = ih6_0->ip6.protocol;
+		  memmove (oh6_0, ih6_0, sizeof (ip6_header_t));
+		}
+
+	      oh6_0->ip6.protocol = IP_PROTOCOL_IPSEC_ESP;
+	      oh6_0->ip6.hop_limit = 254;
+	      oh6_0->esp.spi = clib_net_to_host_u32 (sa0->spi);
+	      oh6_0->esp.seq = clib_net_to_host_u32 (sa0->seq);
+	    }
+	  else
+	    {
+	      ip_hdr_size = sizeof (ip4_header_t);
+	      next_hdr_type = IP_PROTOCOL_IP_IN_IP;
+	      oh0 = vlib_buffer_get_current (b0);
+
+	      oh0->ip4.ip_version_and_header_length = 0x45;
+	      oh0->ip4.tos = ih0->ip4.tos;
+	      oh0->ip4.fragment_id = 0;
+	      oh0->ip4.flags_and_fragment_offset = 0;
+	      oh0->ip4.ttl = 254;
+	      oh0->ip4.protocol = IP_PROTOCOL_IPSEC_ESP;
+	      oh0->esp.spi = clib_net_to_host_u32 (sa0->spi);
+	      oh0->esp.seq = clib_net_to_host_u32 (sa0->seq);
+	    }
+
+	  if (PREDICT_TRUE (sa0->is_tunnel && !sa0->is_tunnel_ip6))
+	    {
+	      oh0->ip4.src_address.as_u32 = sa0->tunnel_src_addr.ip4.as_u32;
+	      oh0->ip4.dst_address.as_u32 = sa0->tunnel_dst_addr.ip4.as_u32;
+
+	      /* in tunnel mode send it back to FIB */
+	      next0 = ESP_ENCRYPT_NEXT_IP4_INPUT;
+	      vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
+	    }
+	  else if (sa0->is_tunnel && sa0->is_tunnel_ip6)
+	    {
+	      oh6_0->ip6.src_address.as_u64[0] =
+		sa0->tunnel_src_addr.ip6.as_u64[0];
+	      oh6_0->ip6.src_address.as_u64[1] =
+		sa0->tunnel_src_addr.ip6.as_u64[1];
+	      oh6_0->ip6.dst_address.as_u64[0] =
+		sa0->tunnel_dst_addr.ip6.as_u64[0];
+	      oh6_0->ip6.dst_address.as_u64[1] =
+		sa0->tunnel_dst_addr.ip6.as_u64[1];
+
+	      /* in tunnel mode send it back to FIB */
+	      next0 = ESP_ENCRYPT_NEXT_IP6_INPUT;
+	      vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
+	    }
+	  else
+	    {
+	      next0 = ESP_ENCRYPT_NEXT_INTERFACE_OUTPUT;
+	      transport_mode = 1;
+	      /*ipv6 already handled */
+	      if (PREDICT_TRUE (!is_ipv6))
+		{
+		  next_hdr_type = ih0->ip4.protocol;
+		  oh0->ip4.src_address.as_u32 = ih0->ip4.src_address.as_u32;
+		  oh0->ip4.dst_address.as_u32 = ih0->ip4.dst_address.as_u32;
+		}
+	    }
+
+	  ASSERT (sa0->crypto_alg < IPSEC_CRYPTO_N_ALG);
+	  ASSERT (sa0->crypto_alg != IPSEC_CRYPTO_ALG_NONE);
+
+	  int blocks = 1 + (orig_sz + 1) / BLOCK_SIZE;
+
+	  /* pad packet in input buffer */
+	  u8 pad_bytes = BLOCK_SIZE * blocks - 2 - orig_sz;
+	  u8 i;
+	  u8 *padding = vlib_buffer_get_current (b0) + b0->current_length;
+
+	  for (i = 0; i < pad_bytes; ++i)
+	    padding[i] = i + 1;
+
+	  f0 = vlib_buffer_get_current (b0) + b0->current_length + pad_bytes;
+	  f0->pad_length = pad_bytes;
+	  f0->next_header = next_hdr_type;
+	  b0->current_length += pad_bytes + 2 +
+	    em->esp_integ_algs[sa0->integ_alg].trunc_size;
+
+	  vnet_buffer (b0)->sw_if_index[VLIB_RX] =
+	    vnet_buffer (b0)->sw_if_index[VLIB_RX];
+	  b0->flags |= VLIB_BUFFER_TOTAL_LENGTH_VALID;
+
+	  struct rte_crypto_sym_op *sym_cop;
+	  sym_cop = (struct rte_crypto_sym_op *) (cop + 1);
+
+	  dpdk_cop_priv_t *priv = (dpdk_cop_priv_t *) (sym_cop + 1);
+
+	  vnet_buffer (b0)->unused[0] = next0;
+	  priv->iv[0] = sa0->seq;
+	  priv->iv[1] = sa0->seq_hi;
+
+	  mb0 = rte_mbuf_from_vlib_buffer (b0);
+	  mb0->data_len = b0->current_length;
+	  mb0->pkt_len = b0->current_length;
+	  mb0->data_off = RTE_PKTMBUF_HEADROOM + b0->current_data;
+
+	  rte_crypto_op_attach_sym_session (cop, sess);
+
+	  sym_cop->m_src = mb0;
+	  sym_cop->cipher.data.offset = ip_hdr_size + sizeof (esp_header_t);
+	  sym_cop->cipher.data.length = BLOCK_SIZE * blocks + IV_SIZE;
+
+	  sym_cop->cipher.iv.data = (u8 *) priv->iv;
+	  sym_cop->cipher.iv.phys_addr = cop->phys_addr +
+	    (uintptr_t) priv->iv - (uintptr_t) cop;
+	  sym_cop->cipher.iv.length = IV_SIZE;
+
+	  ASSERT (sa0->integ_alg < IPSEC_INTEG_N_ALG);
+	  ASSERT (sa0->integ_alg != IPSEC_INTEG_ALG_NONE);
+
+	  sym_cop->auth.data.offset = ip_hdr_size;
+	  sym_cop->auth.data.length = b0->current_length - ip_hdr_size -
+	    em->esp_integ_algs[sa0->integ_alg].trunc_size;
+
+	  sym_cop->auth.digest.data = vlib_buffer_get_current (b0) +
+	    b0->current_length -
+	    em->esp_integ_algs[sa0->integ_alg].trunc_size;
+	  sym_cop->auth.digest.phys_addr = rte_pktmbuf_mtophys_offset (mb0,
+								       b0->current_length
+								       -
+								       em->esp_integ_algs
+								       [sa0->integ_alg].trunc_size);
+	  sym_cop->auth.digest.length =
+	    em->esp_integ_algs[sa0->integ_alg].trunc_size;
+
+	  if (PREDICT_FALSE (sa0->use_esn))
+	    {
+	      u8 *payload_end =
+		vlib_buffer_get_current (b0) + b0->current_length;
+	      *((u32 *) payload_end) = sa0->seq_hi;
+	      sym_cop->auth.data.length += sizeof (sa0->seq_hi);
+	    }
+
+	  if (PREDICT_FALSE (is_ipv6))
+	    {
+	      oh6_0->ip6.payload_length =
+		clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0) -
+				      sizeof (ip6_header_t));
+	    }
+	  else
+	    {
+	      oh0->ip4.length =
+		clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
+	      oh0->ip4.checksum = ip4_header_checksum (&oh0->ip4);
+	    }
+
+	  if (transport_mode)
+	    vlib_buffer_advance (b0, -sizeof (ethernet_header_t));
+
+	trace:
+	  if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+	    {
+	      esp_encrypt_trace_t *tr =
+		vlib_add_trace (vm, node, b0, sizeof (*tr));
+	      tr->spi = sa0->spi;
+	      tr->seq = sa0->seq - 1;
+	      tr->crypto_alg = sa0->crypto_alg;
+	      tr->integ_alg = sa0->integ_alg;
+	    }
+	}
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+  vlib_node_increment_counter (vm, dpdk_esp_encrypt_node.index,
+			       ESP_ENCRYPT_ERROR_RX_PKTS,
+			       from_frame->n_vectors);
+  crypto_qp_data_t *qpd;
+  /* *INDENT-OFF* */
+  vec_foreach_index (i, cwm->qp_data)
+    {
+      u32 enq;
+
+      qpd = vec_elt_at_index(cwm->qp_data, i);
+      enq = rte_cryptodev_enqueue_burst(qpd->dev_id, qpd->qp_id,
+					qpd->cops, n_cop_qp[i]);
+      qpd->inflights += enq;
+
+      if (PREDICT_FALSE(enq < n_cop_qp[i]))
+	{
+	  crypto_free_cop (qpd, &qpd->cops[enq], n_cop_qp[i] - enq);
+	  vlib_buffer_free (vm, &qpd->bi[enq], n_cop_qp[i] - enq);
+
+          vlib_node_increment_counter (vm, dpdk_esp_encrypt_node.index,
+				       ESP_ENCRYPT_ERROR_ENQ_FAIL,
+				       n_cop_qp[i] - enq);
+        }
+    }
+  /* *INDENT-ON* */
+
+  return from_frame->n_vectors;
+}
+
+VLIB_REGISTER_NODE (dpdk_esp_encrypt_node) =
+{
+  .function = dpdk_esp_encrypt_node_fn,.name = "dpdk-esp-encrypt",.flags =
+    VLIB_NODE_FLAG_IS_OUTPUT,.vector_size = sizeof (u32),.format_trace =
+    format_esp_encrypt_trace,.n_errors =
+    ARRAY_LEN (esp_encrypt_error_strings),.error_strings =
+    esp_encrypt_error_strings,.n_next_nodes = 1,.next_nodes =
+  {
+  [ESP_ENCRYPT_NEXT_DROP] = "error-drop",}
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (dpdk_esp_encrypt_node, dpdk_esp_encrypt_node_fn)
+/*
+ * ESP Encrypt Post Node
+ */
+#define foreach_esp_encrypt_post_error              \
+ _(PKTS, "ESP post pkts")
+     typedef enum
+     {
+#define _(sym,str) ESP_ENCRYPT_POST_ERROR_##sym,
+       foreach_esp_encrypt_post_error
+#undef _
+	 ESP_ENCRYPT_POST_N_ERROR,
+     } esp_encrypt_post_error_t;
+
+     static char *esp_encrypt_post_error_strings[] = {
+#define _(sym,string) string,
+       foreach_esp_encrypt_post_error
+#undef _
+     };
+
+vlib_node_registration_t dpdk_esp_encrypt_post_node;
+
+static u8 *
+format_esp_encrypt_post_trace (u8 * s, va_list * args)
+{
+  return s;
+}
+
+static uword
+dpdk_esp_encrypt_post_node_fn (vlib_main_t * vm,
+			       vlib_node_runtime_t * node,
+			       vlib_frame_t * from_frame)
+{
+  u32 n_left_from, *from, *to_next = 0, next_index;
+
+  from = vlib_frame_vector_args (from_frame);
+  n_left_from = from_frame->n_vectors;
+
+  next_index = node->cached_next_index;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+	{
+	  u32 bi0, next0;
+	  vlib_buffer_t *b0 = 0;
+
+	  bi0 = from[0];
+	  from += 1;
+	  n_left_from -= 1;
+	  n_left_to_next -= 1;
+
+	  b0 = vlib_get_buffer (vm, bi0);
+
+	  to_next[0] = bi0;
+	  to_next += 1;
+
+	  next0 = vnet_buffer (b0)->unused[0];
+
+	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+					   to_next, n_left_to_next, bi0,
+					   next0);
+	}
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  vlib_node_increment_counter (vm, dpdk_esp_encrypt_post_node.index,
+			       ESP_ENCRYPT_POST_ERROR_PKTS,
+			       from_frame->n_vectors);
+
+  return from_frame->n_vectors;
+}
+
+VLIB_REGISTER_NODE (dpdk_esp_encrypt_post_node) =
+{
+  .function = dpdk_esp_encrypt_post_node_fn,.name =
+    "dpdk-esp-encrypt-post",.vector_size = sizeof (u32),.format_trace =
+    format_esp_encrypt_post_trace,.type = VLIB_NODE_TYPE_INTERNAL,.n_errors =
+    ARRAY_LEN (esp_encrypt_post_error_strings),.error_strings =
+    esp_encrypt_post_error_strings,.n_next_nodes =
+    ESP_ENCRYPT_N_NEXT,.next_nodes =
+  {
+#define _(s,n) [ESP_ENCRYPT_NEXT_##s] = n,
+    foreach_esp_encrypt_next
+#undef _
+  }
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (dpdk_esp_encrypt_post_node,
+			      dpdk_esp_encrypt_post_node_fn)
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/vnet/vnet/devices/dpdk/ipsec/ipsec.c b/vnet/vnet/devices/dpdk/ipsec/ipsec.c
new file mode 100644
index 0000000..de253f0
--- /dev/null
+++ b/vnet/vnet/devices/dpdk/ipsec/ipsec.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2016 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 <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/api_errno.h>
+#include <vnet/devices/dpdk/dpdk.h>
+#include <vnet/devices/dpdk/ipsec/ipsec.h>
+#include <vnet/devices/dpdk/ipsec/esp.h>
+#include <vnet/ipsec/ipsec.h>
+
+#define DPDK_CRYPTO_NB_OBJS	  2048
+#define DPDK_CRYPTO_CACHE_SIZE	  512
+#define DPDK_CRYPTO_PRIV_SIZE	  128
+#define DPDK_CRYPTO_N_QUEUE_DESC  512
+#define DPDK_CRYPTO_NB_COPS	  (1024 * 4)
+
+/*
+ * return:
+ * -1: 	update failed
+ * 0:	already exist
+ * 1:	mapped
+ */
+static int
+update_qp_data (crypto_worker_main_t * cwm,
+		u8 cdev_id, u16 qp_id, u8 is_outbound, u16 * idx)
+{
+  crypto_qp_data_t *qpd;
+
+  /* *INDENT-OFF* */
+  vec_foreach_index (*idx, cwm->qp_data)
+    {
+      qpd = vec_elt_at_index(cwm->qp_data, *idx);
+
+      if (qpd->dev_id == cdev_id && qpd->qp_id == qp_id &&
+	  qpd->is_outbound == is_outbound)
+	  return 0;
+    }
+  /* *INDENT-ON* */
+
+  vec_add2 (cwm->qp_data, qpd, 1);
+
+  qpd->dev_id = cdev_id;
+  qpd->qp_id = qp_id;
+  qpd->is_outbound = is_outbound;
+
+  return 1;
+}
+
+/*
+ * return:
+ * 	-1: error
+ * 	0: already exist
+ * 	1: mapped
+ */
+static int
+add_mapping (crypto_worker_main_t * cwm,
+	     u8 cdev_id, u16 qp, u8 is_outbound,
+	     const struct rte_cryptodev_capabilities *cipher_cap,
+	     const struct rte_cryptodev_capabilities *auth_cap)
+{
+  int mapped;
+  u16 qp_index;
+  uword key = 0, data, *ret;
+  crypto_worker_qp_key_t *p_key = (crypto_worker_qp_key_t *) & key;
+
+  p_key->cipher_algo = (u8) cipher_cap->sym.cipher.algo;
+  p_key->auth_algo = (u8) auth_cap->sym.auth.algo;
+  p_key->is_outbound = is_outbound;
+
+  ret = hash_get (cwm->algo_qp_map, key);
+  if (ret)
+    return 0;
+
+  mapped = update_qp_data (cwm, cdev_id, qp, is_outbound, &qp_index);
+  if (mapped < 0)
+    return -1;
+
+  data = (uword) qp_index;
+
+  ret = hash_set (cwm->algo_qp_map, key, data);
+  if (!ret)
+    rte_panic ("Failed to insert hash table\n");
+
+  return mapped;
+}
+
+/*
+ * return:
+ * 	0: already exist
+ * 	1: mapped
+ */
+static int
+add_cdev_mapping (crypto_worker_main_t * cwm,
+		  struct rte_cryptodev_info *dev_info, u8 cdev_id,
+		  u16 qp, u8 is_outbound)
+{
+  const struct rte_cryptodev_capabilities *i, *j;
+  u32 mapped = 0;
+
+  for (i = dev_info->capabilities; i->op != RTE_CRYPTO_OP_TYPE_UNDEFINED; i++)
+    {
+      if (i->sym.xform_type != RTE_CRYPTO_SYM_XFORM_CIPHER)
+	continue;
+
+      if (check_algo_is_supported (i, NULL) != 0)
+	continue;
+
+      for (j = dev_info->capabilities; j->op != RTE_CRYPTO_OP_TYPE_UNDEFINED;
+	   j++)
+	{
+	  int status = 0;
+
+	  if (j->sym.xform_type != RTE_CRYPTO_SYM_XFORM_AUTH)
+	    continue;
+
+	  if (check_algo_is_supported (j, NULL) != 0)
+	    continue;
+
+	  status = add_mapping (cwm, cdev_id, qp, is_outbound, i, j);
+	  if (status == 1)
+	    mapped += 1;
+	  if (status < 0)
+	    return status;
+	}
+    }
+
+  return mapped;
+}
+
+static int
+check_cryptodev_queues ()
+{
+  u32 n_qs = 0;
+  u8 cdev_id;
+  u32 n_req_qs = 2;
+
+  if (vlib_num_workers () > 0)
+    n_req_qs = vlib_num_workers () * 2;
+
+  for (cdev_id = 0; cdev_id < rte_cryptodev_count (); cdev_id++)
+    {
+      struct rte_cryptodev_info cdev_info;
+
+      rte_cryptodev_info_get (cdev_id, &cdev_info);
+
+      if (!
+	  (cdev_info.feature_flags & RTE_CRYPTODEV_FF_SYM_OPERATION_CHAINING))
+	continue;
+
+      n_qs += cdev_info.max_nb_queue_pairs;
+    }
+
+  if (n_qs >= n_req_qs)
+    return 0;
+  else
+    return -1;
+}
+
+static clib_error_t *
+dpdk_ipsec_init (vlib_main_t * vm)
+{
+  dpdk_crypto_main_t *dcm = &dpdk_crypto_main;
+  vlib_thread_main_t *tm = vlib_get_thread_main ();
+  struct rte_cryptodev_config dev_conf;
+  struct rte_cryptodev_qp_conf qp_conf;
+  struct rte_cryptodev_info cdev_info;
+  struct rte_mempool *rmp;
+  i32 dev_id, ret;
+  u32 i, skip_master;
+
+  if (check_cryptodev_queues () < 0)
+    return clib_error_return (0, "not enough cryptodevs for ipsec");
+
+  vec_alloc (dcm->workers_main, tm->n_vlib_mains);
+  _vec_len (dcm->workers_main) = tm->n_vlib_mains;
+
+  fprintf (stdout, "DPDK Cryptodevs info:\n");
+  fprintf (stdout, "dev_id\tn_qp\tnb_obj\tcache_size\n");
+  /* HW cryptodevs have higher dev_id, use HW first */
+  for (dev_id = rte_cryptodev_count () - 1; dev_id >= 0; dev_id--)
+    {
+      u16 max_nb_qp, qp = 0;
+      skip_master = vlib_num_workers () > 0;
+
+      rte_cryptodev_info_get (dev_id, &cdev_info);
+
+      if (!
+	  (cdev_info.feature_flags & RTE_CRYPTODEV_FF_SYM_OPERATION_CHAINING))
+	continue;
+
+      max_nb_qp = cdev_info.max_nb_queue_pairs;
+
+      for (i = 0; i < tm->n_vlib_mains; i++)
+	{
+	  u8 is_outbound;
+	  crypto_worker_main_t *cwm;
+	  uword *map;
+
+	  if (skip_master)
+	    {
+	      skip_master = 0;
+	      continue;
+	    }
+
+	  cwm = vec_elt_at_index (dcm->workers_main, i);
+	  map = cwm->algo_qp_map;
+
+	  if (!map)
+	    {
+	      map = hash_create (0, sizeof (crypto_worker_qp_key_t));
+	      if (!map)
+		return clib_error_return (0, "unable to create hash table "
+					  "for worker %u",
+					  vlib_mains[i]->cpu_index);
+	      cwm->algo_qp_map = map;
+	    }
+
+	  for (is_outbound = 0; is_outbound < 2 && qp < max_nb_qp;
+	       is_outbound++)
+	    {
+	      int mapped = add_cdev_mapping (cwm, &cdev_info,
+					     dev_id, qp, is_outbound);
+	      if (mapped > 0)
+		qp++;
+
+	      if (mapped < 0)
+		return clib_error_return (0,
+					  "too many queues for one worker");
+	    }
+	}
+
+      if (qp == 0)
+	continue;
+
+      dev_conf.socket_id = rte_cryptodev_socket_id (dev_id);
+      dev_conf.nb_queue_pairs = cdev_info.max_nb_queue_pairs;
+      dev_conf.session_mp.nb_objs = DPDK_CRYPTO_NB_OBJS;
+      dev_conf.session_mp.cache_size = DPDK_CRYPTO_CACHE_SIZE;
+
+      ret = rte_cryptodev_configure (dev_id, &dev_conf);
+      if (ret < 0)
+	return clib_error_return (0, "cryptodev %u config error", dev_id);
+
+      qp_conf.nb_descriptors = DPDK_CRYPTO_N_QUEUE_DESC;
+      for (qp = 0; qp < dev_conf.nb_queue_pairs; qp++)
+	{
+	  ret = rte_cryptodev_queue_pair_setup (dev_id, qp, &qp_conf,
+						dev_conf.socket_id);
+	  if (ret < 0)
+	    return clib_error_return (0, "cryptodev %u qp %u setup error",
+				      dev_id, qp);
+	}
+      fprintf (stdout, "%u\t%u\t%u\t%u\n", dev_id, dev_conf.nb_queue_pairs,
+	       DPDK_CRYPTO_NB_OBJS, DPDK_CRYPTO_CACHE_SIZE);
+    }
+
+  u32 socket_id = rte_socket_id ();
+
+  vec_validate_aligned (dcm->cop_pools, socket_id, CLIB_CACHE_LINE_BYTES);
+
+  /* pool already exists, nothing to do */
+  if (dcm->cop_pools[socket_id])
+    return 0;
+
+  u8 *pool_name = format (0, "crypto_op_pool_socket%u%c", socket_id, 0);
+
+  rmp = rte_crypto_op_pool_create ((char *) pool_name,
+				   RTE_CRYPTO_OP_TYPE_SYMMETRIC,
+				   DPDK_CRYPTO_NB_COPS *
+				   (1 + vlib_num_workers ()),
+				   DPDK_CRYPTO_CACHE_SIZE,
+				   DPDK_CRYPTO_PRIV_SIZE, socket_id);
+  vec_free (pool_name);
+
+  if (!rmp)
+    return clib_error_return (0, "failed to allocate mempool on socket %u",
+			      socket_id);
+  dcm->cop_pools[socket_id] = rmp;
+
+  dpdk_esp_init ();
+
+  if (vec_len (vlib_mains) == 0)
+    vlib_node_set_state (&vlib_global_main, dpdk_crypto_input_node.index,
+			 VLIB_NODE_STATE_POLLING);
+  else
+    for (i = 1; i < tm->n_vlib_mains; i++)
+      vlib_node_set_state (vlib_mains[i], dpdk_crypto_input_node.index,
+			   VLIB_NODE_STATE_POLLING);
+
+  return 0;
+}
+
+VLIB_MAIN_LOOP_ENTER_FUNCTION (dpdk_ipsec_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/vnet/vnet/devices/dpdk/ipsec/ipsec.h b/vnet/vnet/devices/dpdk/ipsec/ipsec.h
new file mode 100644
index 0000000..e103655
--- /dev/null
+++ b/vnet/vnet/devices/dpdk/ipsec/ipsec.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2016 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 __DPDK_IPSEC_H__
+#define __DPDK_IPSEC_H__
+
+#include <vnet/vnet.h>
+
+#undef always_inline
+#include <rte_crypto.h>
+#include <rte_cryptodev.h>
+
+#if CLIB_DEBUG > 0
+#define always_inline static inline
+#else
+#define always_inline static inline __attribute__ ((__always_inline__))
+#endif
+
+
+#define MAX_QP_PER_LCORE 16
+
+typedef struct
+{
+  u32 iv[4];
+  u8 icv[64];
+} dpdk_cop_priv_t;
+
+typedef struct
+{
+  u8 cipher_algo;
+  u8 auth_algo;
+  u8 is_outbound;
+} crypto_worker_qp_key_t;
+
+typedef struct
+{
+  u16 dev_id;
+  u16 qp_id;
+  u16 is_outbound;
+  i16 inflights;
+  u32 bi[VLIB_FRAME_SIZE];
+  struct rte_crypto_op *cops[VLIB_FRAME_SIZE];
+  struct rte_crypto_op **free_cops;
+} crypto_qp_data_t;
+
+typedef struct
+{
+  u8 qp_index;
+  void *sess;
+} crypto_sa_session_t;
+
+typedef struct
+{
+  crypto_sa_session_t *sa_sess_d[2];
+  crypto_qp_data_t *qp_data;
+  uword *algo_qp_map;
+} crypto_worker_main_t;
+
+typedef struct
+{
+  struct rte_mempool **cop_pools;
+  crypto_worker_main_t *workers_main;
+} dpdk_crypto_main_t;
+
+dpdk_crypto_main_t dpdk_crypto_main;
+
+extern vlib_node_registration_t dpdk_crypto_input_node;
+
+#define CRYPTO_N_FREE_COPS (VLIB_FRAME_SIZE * 3)
+
+static_always_inline void
+crypto_alloc_cops ()
+{
+  dpdk_crypto_main_t *dcm = &dpdk_crypto_main;
+  u32 cpu_index = os_get_cpu_number ();
+  crypto_worker_main_t *cwm = &dcm->workers_main[cpu_index];
+  unsigned socket_id = rte_socket_id ();
+  crypto_qp_data_t *qpd;
+
+  /* *INDENT-OFF* */
+  vec_foreach (qpd, cwm->qp_data)
+    {
+      u32 l = vec_len (qpd->free_cops);
+
+      if (PREDICT_FALSE (l < VLIB_FRAME_SIZE))
+	{
+	  u32 n_alloc;
+
+	  if (PREDICT_FALSE (!qpd->free_cops))
+	    vec_alloc (qpd->free_cops, CRYPTO_N_FREE_COPS);
+
+	  n_alloc = rte_crypto_op_bulk_alloc (dcm->cop_pools[socket_id],
+					      RTE_CRYPTO_OP_TYPE_SYMMETRIC,
+					      &qpd->free_cops[l],
+					      CRYPTO_N_FREE_COPS - l - 1);
+
+	  _vec_len (qpd->free_cops) = l + n_alloc;
+	}
+    }
+  /* *INDENT-ON* */
+}
+
+static_always_inline void
+crypto_free_cop (crypto_qp_data_t * qpd, struct rte_crypto_op **cops, u32 n)
+{
+  u32 l = vec_len (qpd->free_cops);
+
+  if (l + n >= CRYPTO_N_FREE_COPS)
+    {
+      l -= VLIB_FRAME_SIZE;
+      rte_mempool_put_bulk (cops[0]->mempool,
+			    (void **) &qpd->free_cops[l], VLIB_FRAME_SIZE);
+    }
+  clib_memcpy (&qpd->free_cops[l], cops, sizeof (*cops) * n);
+
+  _vec_len (qpd->free_cops) = l + n;
+}
+
+static_always_inline int
+check_algo_is_supported (const struct rte_cryptodev_capabilities *cap,
+			 char *name)
+{
+  struct
+  {
+    uint8_t cipher_algo;
+    enum rte_crypto_sym_xform_type type;
+    union
+    {
+      enum rte_crypto_auth_algorithm auth;
+      enum rte_crypto_cipher_algorithm cipher;
+    };
+    char *name;
+  } supported_algo[] =
+  {
+    {
+    .type = RTE_CRYPTO_SYM_XFORM_CIPHER,.cipher =
+	RTE_CRYPTO_CIPHER_NULL,.name = "NULL"},
+    {
+    .type = RTE_CRYPTO_SYM_XFORM_CIPHER,.cipher =
+	RTE_CRYPTO_CIPHER_AES_CBC,.name = "AES_CBC"},
+    {
+    .type = RTE_CRYPTO_SYM_XFORM_CIPHER,.cipher =
+	RTE_CRYPTO_CIPHER_AES_CTR,.name = "AES_CTR"},
+    {
+    .type = RTE_CRYPTO_SYM_XFORM_CIPHER,.cipher =
+	RTE_CRYPTO_CIPHER_3DES_CBC,.name = "3DES-CBC"},
+    {
+    .type = RTE_CRYPTO_SYM_XFORM_CIPHER,.auth =
+	RTE_CRYPTO_CIPHER_AES_GCM,.name = "AES-GCM"},
+    {
+    .type = RTE_CRYPTO_SYM_XFORM_AUTH,.auth =
+	RTE_CRYPTO_AUTH_SHA1_HMAC,.name = "HMAC-SHA1"},
+    {
+    .type = RTE_CRYPTO_SYM_XFORM_AUTH,.auth =
+	RTE_CRYPTO_AUTH_SHA256_HMAC,.name = "HMAC-SHA256"},
+    {
+    .type = RTE_CRYPTO_SYM_XFORM_AUTH,.auth =
+	RTE_CRYPTO_AUTH_SHA384_HMAC,.name = "HMAC-SHA384"},
+    {
+    .type = RTE_CRYPTO_SYM_XFORM_AUTH,.auth =
+	RTE_CRYPTO_AUTH_SHA512_HMAC,.name = "HMAC-SHA512"},
+    {
+    .type = RTE_CRYPTO_SYM_XFORM_AUTH,.auth =
+	RTE_CRYPTO_AUTH_AES_XCBC_MAC,.name = "AES-XCBC-MAC"},
+    {
+    .type = RTE_CRYPTO_SYM_XFORM_AUTH,.auth =
+	RTE_CRYPTO_AUTH_AES_GCM,.name = "AES-GCM"},
+    {
+      /* tail */
+  .type = RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED},};
+  uint32_t i = 0;
+
+  if (cap->op != RTE_CRYPTO_OP_TYPE_SYMMETRIC)
+    return -1;
+
+  while (supported_algo[i].type != RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED)
+    {
+      if (cap->sym.xform_type == supported_algo[i].type)
+	{
+	  if ((cap->sym.xform_type == RTE_CRYPTO_SYM_XFORM_CIPHER &&
+	       cap->sym.cipher.algo == supported_algo[i].cipher) ||
+	      (cap->sym.xform_type == RTE_CRYPTO_SYM_XFORM_AUTH &&
+	       cap->sym.auth.algo == supported_algo[i].auth))
+	    {
+	      if (name)
+		strcpy (name, supported_algo[i].name);
+	      return 0;
+	    }
+	}
+
+      i++;
+    }
+
+  return -1;
+}
+
+#endif /* __DPDK_IPSEC_H__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/vnet/vnet/ipsec-gre/interface.c b/vnet/vnet/ipsec-gre/interface.c
index fae79b9..56832ee 100644
--- a/vnet/vnet/ipsec-gre/interface.c
+++ b/vnet/vnet/ipsec-gre/interface.c
@@ -27,7 +27,14 @@
 #include <vnet/ipsec-gre/ipsec_gre.h>
 #include <vnet/ip/format.h>
 #include <vnet/ipsec/ipsec.h>
+
+#if DPDK_CRYPTO==1
+#include <vnet/devices/dpdk/ipsec/esp.h>
+#define ESP_NODE "dpdk-esp-encrypt"
+#else
 #include <vnet/ipsec/esp.h>
+#define ESP_NODE "esp-encrypt"
+#endif
 
 u8 *
 format_ipsec_gre_tunnel (u8 * s, va_list * args)
@@ -186,7 +193,7 @@
       hash_set (igm->tunnel_by_key, key, t - igm->tunnels);
 
       slot = vlib_node_add_named_next_with_slot
-	(vnm->vlib_main, hi->tx_node_index, "esp-encrypt",
+	(vnm->vlib_main, hi->tx_node_index, ESP_NODE,
 	 IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT);
 
       ASSERT (slot == IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT);
diff --git a/vnet/vnet/ipsec/esp.h b/vnet/vnet/ipsec/esp.h
index b9feacb..50cac80 100644
--- a/vnet/vnet/ipsec/esp.h
+++ b/vnet/vnet/ipsec/esp.h
@@ -12,6 +12,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#ifndef __ESP_H__
+#define __ESP_H__
 
 #include <openssl/hmac.h>
 #include <openssl/rand.h>
@@ -77,6 +79,154 @@
 
 esp_main_t esp_main;
 
+#define ESP_WINDOW_SIZE		(64)
+#define ESP_SEQ_MAX 		(4294967295UL)
+
+
+always_inline int
+esp_replay_check (ipsec_sa_t * sa, u32 seq)
+{
+  u32 diff;
+
+  if (PREDICT_TRUE (seq > sa->last_seq))
+    return 0;
+
+  diff = sa->last_seq - seq;
+
+  if (ESP_WINDOW_SIZE > diff)
+    return (sa->replay_window & (1ULL << diff)) ? 1 : 0;
+  else
+    return 1;
+
+  return 0;
+}
+
+always_inline int
+esp_replay_check_esn (ipsec_sa_t * sa, u32 seq)
+{
+  u32 tl = sa->last_seq;
+  u32 th = sa->last_seq_hi;
+  u32 diff = tl - seq;
+
+  if (PREDICT_TRUE (tl >= (ESP_WINDOW_SIZE - 1)))
+    {
+      if (seq >= (tl - ESP_WINDOW_SIZE + 1))
+	{
+	  sa->seq_hi = th;
+	  if (seq <= tl)
+	    return (sa->replay_window & (1ULL << diff)) ? 1 : 0;
+	  else
+	    return 0;
+	}
+      else
+	{
+	  sa->seq_hi = th + 1;
+	  return 0;
+	}
+    }
+  else
+    {
+      if (seq >= (tl - ESP_WINDOW_SIZE + 1))
+	{
+	  sa->seq_hi = th - 1;
+	  return (sa->replay_window & (1ULL << diff)) ? 1 : 0;
+	}
+      else
+	{
+	  sa->seq_hi = th;
+	  if (seq <= tl)
+	    return (sa->replay_window & (1ULL << diff)) ? 1 : 0;
+	  else
+	    return 0;
+	}
+    }
+
+  return 0;
+}
+
+/* TODO seq increment should be atomic to be accessed by multiple workers */
+always_inline void
+esp_replay_advance (ipsec_sa_t * sa, u32 seq)
+{
+  u32 pos;
+
+  if (seq > sa->last_seq)
+    {
+      pos = seq - sa->last_seq;
+      if (pos < ESP_WINDOW_SIZE)
+	sa->replay_window = ((sa->replay_window) << pos) | 1;
+      else
+	sa->replay_window = 1;
+      sa->last_seq = seq;
+    }
+  else
+    {
+      pos = sa->last_seq - seq;
+      sa->replay_window |= (1ULL << pos);
+    }
+}
+
+always_inline void
+esp_replay_advance_esn (ipsec_sa_t * sa, u32 seq)
+{
+  int wrap = sa->seq_hi - sa->last_seq_hi;
+  u32 pos;
+
+  if (wrap == 0 && seq > sa->last_seq)
+    {
+      pos = seq - sa->last_seq;
+      if (pos < ESP_WINDOW_SIZE)
+	sa->replay_window = ((sa->replay_window) << pos) | 1;
+      else
+	sa->replay_window = 1;
+      sa->last_seq = seq;
+    }
+  else if (wrap > 0)
+    {
+      pos = ~seq + sa->last_seq + 1;
+      if (pos < ESP_WINDOW_SIZE)
+	sa->replay_window = ((sa->replay_window) << pos) | 1;
+      else
+	sa->replay_window = 1;
+      sa->last_seq = seq;
+      sa->last_seq_hi = sa->seq_hi;
+    }
+  else if (wrap < 0)
+    {
+      pos = ~seq + sa->last_seq + 1;
+      sa->replay_window |= (1ULL << pos);
+    }
+  else
+    {
+      pos = sa->last_seq - seq;
+      sa->replay_window |= (1ULL << pos);
+    }
+}
+
+always_inline int
+esp_seq_advance (ipsec_sa_t * sa)
+{
+  if (PREDICT_TRUE (sa->use_esn))
+    {
+      if (PREDICT_FALSE (sa->seq == ESP_SEQ_MAX))
+	{
+	  if (PREDICT_FALSE
+	      (sa->use_anti_replay && sa->seq_hi == ESP_SEQ_MAX))
+	    return 1;
+	  sa->seq_hi++;
+	}
+      sa->seq++;
+    }
+  else
+    {
+      if (PREDICT_FALSE (sa->use_anti_replay && sa->seq == ESP_SEQ_MAX))
+	return 1;
+      sa->seq++;
+    }
+
+  return 0;
+}
+
 always_inline void
 esp_init ()
 {
@@ -159,6 +309,7 @@
   return em->esp_integ_algs[alg].trunc_size;
 }
 
+#endif /* __ESP_H__ */
 
 /*
  * fd.io coding-style-patch-verification: ON
diff --git a/vnet/vnet/ipsec/esp_decrypt.c b/vnet/vnet/ipsec/esp_decrypt.c
index 07a5eda..e69cd85 100644
--- a/vnet/vnet/ipsec/esp_decrypt.c
+++ b/vnet/vnet/ipsec/esp_decrypt.c
@@ -22,8 +22,6 @@
 #include <vnet/ipsec/ipsec.h>
 #include <vnet/ipsec/esp.h>
 
-#define ESP_WINDOW_SIZE 64
-
 #define foreach_esp_decrypt_next                \
 _(DROP, "error-drop")                           \
 _(IP4_INPUT, "ip4-input")                       \
@@ -109,125 +107,6 @@
   EVP_DecryptFinal_ex (ctx, out + out_len, &out_len);
 }
 
-always_inline int
-esp_replay_check (ipsec_sa_t * sa, u32 seq)
-{
-  u32 diff;
-
-  if (PREDICT_TRUE (seq > sa->last_seq))
-    return 0;
-
-  diff = sa->last_seq - seq;
-
-  if (ESP_WINDOW_SIZE > diff)
-    return (sa->replay_window & (1ULL << diff)) ? 1 : 0;
-  else
-    return 1;
-
-  return 0;
-}
-
-always_inline int
-esp_replay_check_esn (ipsec_sa_t * sa, u32 seq)
-{
-  u32 tl = sa->last_seq;
-  u32 th = sa->last_seq_hi;
-  u32 diff = tl - seq;
-
-  if (PREDICT_TRUE (tl >= (ESP_WINDOW_SIZE - 1)))
-    {
-      if (seq >= (tl - ESP_WINDOW_SIZE + 1))
-	{
-	  sa->seq_hi = th;
-	  if (seq <= tl)
-	    return (sa->replay_window & (1ULL << diff)) ? 1 : 0;
-	  else
-	    return 0;
-	}
-      else
-	{
-	  sa->seq_hi = th + 1;
-	  return 0;
-	}
-    }
-  else
-    {
-      if (seq >= (tl - ESP_WINDOW_SIZE + 1))
-	{
-	  sa->seq_hi = th - 1;
-	  return (sa->replay_window & (1ULL << diff)) ? 1 : 0;
-	}
-      else
-	{
-	  sa->seq_hi = th;
-	  if (seq <= tl)
-	    return (sa->replay_window & (1ULL << diff)) ? 1 : 0;
-	  else
-	    return 0;
-	}
-    }
-
-  return 0;
-}
-
-always_inline void
-esp_replay_advance (ipsec_sa_t * sa, u32 seq)
-{
-  u32 pos;
-
-  if (seq > sa->last_seq)
-    {
-      pos = seq - sa->last_seq;
-      if (pos < ESP_WINDOW_SIZE)
-	sa->replay_window = ((sa->replay_window) << pos) | 1;
-      else
-	sa->replay_window = 1;
-      sa->last_seq = seq;
-    }
-  else
-    {
-      pos = sa->last_seq - seq;
-      sa->replay_window |= (1ULL << pos);
-    }
-}
-
-always_inline void
-esp_replay_advance_esn (ipsec_sa_t * sa, u32 seq)
-{
-  int wrap = sa->seq_hi - sa->last_seq_hi;
-  u32 pos;
-
-  if (wrap == 0 && seq > sa->last_seq)
-    {
-      pos = seq - sa->last_seq;
-      if (pos < ESP_WINDOW_SIZE)
-	sa->replay_window = ((sa->replay_window) << pos) | 1;
-      else
-	sa->replay_window = 1;
-      sa->last_seq = seq;
-    }
-  else if (wrap > 0)
-    {
-      pos = ~seq + sa->last_seq + 1;
-      if (pos < ESP_WINDOW_SIZE)
-	sa->replay_window = ((sa->replay_window) << pos) | 1;
-      else
-	sa->replay_window = 1;
-      sa->last_seq = seq;
-      sa->last_seq_hi = sa->seq_hi;
-    }
-  else if (wrap < 0)
-    {
-      pos = ~seq + sa->last_seq + 1;
-      sa->replay_window |= (1ULL << pos);
-    }
-  else
-    {
-      pos = sa->last_seq - seq;
-      sa->replay_window |= (1ULL << pos);
-    }
-}
-
 static uword
 esp_decrypt_node_fn (vlib_main_t * vm,
 		     vlib_node_runtime_t * node, vlib_frame_t * from_frame)
diff --git a/vnet/vnet/ipsec/esp_encrypt.c b/vnet/vnet/ipsec/esp_encrypt.c
index b7186e3..b947611 100644
--- a/vnet/vnet/ipsec/esp_encrypt.c
+++ b/vnet/vnet/ipsec/esp_encrypt.c
@@ -22,7 +22,6 @@
 #include <vnet/ipsec/ipsec.h>
 #include <vnet/ipsec/esp.h>
 
-#define ESP_SEQ_MAX (4294967295UL)
 
 #define foreach_esp_encrypt_next                   \
 _(DROP, "error-drop")                              \
@@ -111,30 +110,6 @@
   EVP_EncryptFinal_ex (ctx, out + out_len, &out_len);
 }
 
-always_inline int
-esp_seq_advance (ipsec_sa_t * sa)
-{
-  if (PREDICT_TRUE (sa->use_esn))
-    {
-      if (PREDICT_FALSE (sa->seq == ESP_SEQ_MAX))
-	{
-	  if (PREDICT_FALSE
-	      (sa->use_anti_replay && sa->seq_hi == ESP_SEQ_MAX))
-	    return 1;
-	  sa->seq_hi++;
-	}
-      sa->seq++;
-    }
-  else
-    {
-      if (PREDICT_FALSE (sa->use_anti_replay && sa->seq == ESP_SEQ_MAX))
-	return 1;
-      sa->seq++;
-    }
-
-  return 0;
-}
-
 static uword
 esp_encrypt_node_fn (vlib_main_t * vm,
 		     vlib_node_runtime_t * node, vlib_frame_t * from_frame)
diff --git a/vnet/vnet/ipsec/ipsec.c b/vnet/vnet/ipsec/ipsec.c
index 223440e..ee85c40 100644
--- a/vnet/vnet/ipsec/ipsec.c
+++ b/vnet/vnet/ipsec/ipsec.c
@@ -21,9 +21,25 @@
 #include <vnet/interface.h>
 
 #include <vnet/ipsec/ipsec.h>
-#include <vnet/ipsec/esp.h>
 #include <vnet/ipsec/ikev2.h>
 
+#if DPDK_CRYPTO==1
+#include <vnet/devices/dpdk/ipsec/esp.h>
+#define ESP_NODE "dpdk-esp-encrypt"
+#else
+#include <vnet/ipsec/esp.h>
+#define ESP_NODE "esp-encrypt"
+#endif
+
+#if DPDK_CRYPTO==0
+/* dummy function */
+static int
+add_del_sa_sess (u32 sa_index, u8 is_add)
+{
+  return 0;
+}
+#endif
+
 u32
 ipsec_get_sa_index_by_sa_id (u32 sa_id)
 {
@@ -433,6 +449,7 @@
 	  return VNET_API_ERROR_SYSCALL_ERROR_1;	/* sa used in policy */
 	}
       hash_unset (im->sa_index_by_sa_id, sa->id);
+      add_del_sa_sess (sa_index, is_add);
       pool_put (im->sad, sa);
     }
   else				/* create new SA */
@@ -441,6 +458,8 @@
       clib_memcpy (sa, new_sa, sizeof (*sa));
       sa_index = sa - im->sad;
       hash_set (im->sa_index_by_sa_id, sa->id, sa_index);
+      if (add_del_sa_sess (sa_index, is_add) < 0)
+	return VNET_API_ERROR_SYSCALL_ERROR_1;
     }
   return 0;
 }
@@ -476,6 +495,12 @@
       sa->integ_key_len = sa_update->integ_key_len;
     }
 
+  if (sa->crypto_key_len + sa->integ_key_len > 0)
+    {
+      if (add_del_sa_sess (sa_index, 0) < 0)
+	return VNET_API_ERROR_SYSCALL_ERROR_1;
+    }
+
   return 0;
 }
 
@@ -522,7 +547,8 @@
   ASSERT (node);
   im->error_drop_node_index = node->index;
 
-  node = vlib_get_node_by_name (vm, (u8 *) "esp-encrypt");
+  node = vlib_get_node_by_name (vm, (u8 *) ESP_NODE);
+
   ASSERT (node);
   im->esp_encrypt_node_index = node->index;
 
@@ -530,7 +556,6 @@
   ASSERT (node);
   im->ip4_lookup_node_index = node->index;
 
-
   if ((error = vlib_call_init_function (vm, ipsec_cli_init)))
     return error;
 
diff --git a/vnet/vnet/ipsec/ipsec.h b/vnet/vnet/ipsec/ipsec.h
index d33df38..65d7bad 100644
--- a/vnet/vnet/ipsec/ipsec.h
+++ b/vnet/vnet/ipsec/ipsec.h
@@ -12,6 +12,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#ifndef __IPSEC_H__
+#define __IPSEC_H__
+
 #define IPSEC_FLAG_IPSEC_GRE_TUNNEL (1 << 0)
 
 #define foreach_ipsec_policy_action \
@@ -307,6 +310,8 @@
   return node->next_nodes[next];
 }
 
+#endif /* __IPSEC_H__ */
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
diff --git a/vnet/vnet/ipsec/ipsec_cli.c b/vnet/vnet/ipsec/ipsec_cli.c
index 785e040..8920924 100644
--- a/vnet/vnet/ipsec/ipsec_cli.c
+++ b/vnet/vnet/ipsec/ipsec_cli.c
@@ -768,7 +768,6 @@
 };
 /* *INDENT-ON* */
 
-
 clib_error_t *
 ipsec_cli_init (vlib_main_t * vm)
 {
diff --git a/vnet/vnet/ipsec/ipsec_if.c b/vnet/vnet/ipsec/ipsec_if.c
index 77d5d19..a8da046 100644
--- a/vnet/vnet/ipsec/ipsec_if.c
+++ b/vnet/vnet/ipsec/ipsec_if.c
@@ -20,6 +20,20 @@
 #include <vnet/ip/ip.h>
 
 #include <vnet/ipsec/ipsec.h>
+#if DPDK_CRYPTO==1
+#include <vnet/devices/dpdk/ipsec/esp.h>
+#else
+#include <vnet/ipsec/esp.h>
+#endif
+
+#if DPDK_CRYPTO==0
+/* dummy function */
+static int
+add_del_sa_sess (u32 sa_index, u8 is_add)
+{
+  return 0;
+}
+#endif
 
 void vl_api_rpc_call_main_thread (void *fp, u8 * data, u32 data_length);
 
@@ -124,6 +138,8 @@
 		       args->remote_crypto_key_len);
 	}
 
+      add_del_sa_sess (t->input_sa_index, args->is_add);
+
       pool_get (im->sad, sa);
       memset (sa, 0, sizeof (*sa));
       t->output_sa_index = sa - im->sad;
@@ -149,6 +165,8 @@
 		       args->local_crypto_key_len);
 	}
 
+      add_del_sa_sess (t->output_sa_index, args->is_add);
+
       hash_set (im->ipsec_if_pool_index_by_key, key,
 		t - im->tunnel_interfaces);
 
@@ -192,8 +210,17 @@
 
       /* delete input and output SA */
       sa = pool_elt_at_index (im->sad, t->input_sa_index);
+
+      if (add_del_sa_sess (t->input_sa_index, args->is_add) < 0)
+	return VNET_API_ERROR_SYSCALL_ERROR_1;
+
       pool_put (im->sad, sa);
+
       sa = pool_elt_at_index (im->sad, t->output_sa_index);
+
+      if (add_del_sa_sess (t->output_sa_index, args->is_add) < 0)
+	return VNET_API_ERROR_SYSCALL_ERROR_1;
+
       pool_put (im->sad, sa);
 
       hash_unset (im->ipsec_if_pool_index_by_key, key);
@@ -282,6 +309,9 @@
       sa->crypto_alg = alg;
       sa->crypto_key_len = vec_len (key);
       clib_memcpy (sa->crypto_key, key, vec_len (key));
+
+      if (add_del_sa_sess (t->input_sa_index, 0) < 0)
+	return VNET_API_ERROR_SYSCALL_ERROR_1;
     }
   else if (type == IPSEC_IF_SET_KEY_TYPE_LOCAL_INTEG)
     {
@@ -289,6 +319,9 @@
       sa->integ_alg = alg;
       sa->integ_key_len = vec_len (key);
       clib_memcpy (sa->integ_key, key, vec_len (key));
+
+      if (add_del_sa_sess (t->output_sa_index, 0) < 0)
+	return VNET_API_ERROR_SYSCALL_ERROR_1;
     }
   else if (type == IPSEC_IF_SET_KEY_TYPE_REMOTE_CRYPTO)
     {
@@ -296,6 +329,9 @@
       sa->crypto_alg = alg;
       sa->crypto_key_len = vec_len (key);
       clib_memcpy (sa->crypto_key, key, vec_len (key));
+
+      if (add_del_sa_sess (t->input_sa_index, 0) < 0)
+	return VNET_API_ERROR_SYSCALL_ERROR_1;
     }
   else if (type == IPSEC_IF_SET_KEY_TYPE_REMOTE_INTEG)
     {
@@ -303,6 +339,9 @@
       sa->integ_alg = alg;
       sa->integ_key_len = vec_len (key);
       clib_memcpy (sa->integ_key, key, vec_len (key));
+
+      if (add_del_sa_sess (t->output_sa_index, 0) < 0)
+	return VNET_API_ERROR_SYSCALL_ERROR_1;
     }
   else
     return VNET_API_ERROR_INVALID_VALUE;
diff --git a/vnet/vnet/ipsec/ipsec_if_in.c b/vnet/vnet/ipsec/ipsec_if_in.c
index 93cedce..db75ab9 100644
--- a/vnet/vnet/ipsec/ipsec_if_in.c
+++ b/vnet/vnet/ipsec/ipsec_if_in.c
@@ -22,6 +22,12 @@
 #include <vnet/ipsec/ipsec.h>
 #include <vnet/ipsec/esp.h>
 
+#if DPDK_CRYPTO==1
+#define ESP_NODE "dpdk-esp-decrypt"
+#else
+#define ESP_NODE "esp-decrypt"
+#endif
+
 /* Statistics (not really errors) */
 #define foreach_ipsec_if_input_error    \
 _(RX, "good packets received")
@@ -153,8 +159,8 @@
   .n_next_nodes = IPSEC_IF_INPUT_N_NEXT,
 
   .next_nodes = {
-        [IPSEC_IF_INPUT_NEXT_ESP_DECRYPT] = "esp-decrypt",
-        [IPSEC_IF_INPUT_NEXT_DROP] = "error-drop",
+	[IPSEC_IF_INPUT_NEXT_ESP_DECRYPT] = ESP_NODE,
+	[IPSEC_IF_INPUT_NEXT_DROP] = "error-drop",
   },
 };
 /* *INDENT-ON* */
diff --git a/vnet/vnet/ipsec/ipsec_if_out.c b/vnet/vnet/ipsec/ipsec_if_out.c
index a605874..8f06282 100644
--- a/vnet/vnet/ipsec/ipsec_if_out.c
+++ b/vnet/vnet/ipsec/ipsec_if_out.c
@@ -21,6 +21,11 @@
 
 #include <vnet/ipsec/ipsec.h>
 
+#if DPDK_CRYPTO==1
+#define ESP_NODE "dpdk-esp-encrypt"
+#else
+#define ESP_NODE "esp-encrypt"
+#endif
 
 /* Statistics (not really errors) */
 #define foreach_ipsec_if_output_error    \
@@ -140,7 +145,7 @@
   .n_next_nodes = IPSEC_IF_OUTPUT_N_NEXT,
 
   .next_nodes = {
-        [IPSEC_IF_OUTPUT_NEXT_ESP_ENCRYPT] = "esp-encrypt",
+        [IPSEC_IF_OUTPUT_NEXT_ESP_ENCRYPT] = ESP_NODE,
         [IPSEC_IF_OUTPUT_NEXT_DROP] = "error-drop",
   },
 };
diff --git a/vnet/vnet/ipsec/ipsec_input.c b/vnet/vnet/ipsec/ipsec_input.c
index 8360a1d..4662c1a 100644
--- a/vnet/vnet/ipsec/ipsec_input.c
+++ b/vnet/vnet/ipsec/ipsec_input.c
@@ -23,9 +23,15 @@
 #include <vnet/ipsec/ipsec.h>
 #include <vnet/ipsec/esp.h>
 
+#if DPDK_CRYPTO==1
+#define ESP_NODE "dpdk-esp-decrypt"
+#else
+#define ESP_NODE "esp-decrypt"
+#endif
+
 #define foreach_ipsec_input_next                \
 _(DROP, "error-drop")                           \
-_(ESP_DECRYPT, "esp-decrypt")
+_(ESP_DECRYPT, ESP_NODE)
 
 #define _(v, s) IPSEC_INPUT_NEXT_##v,
 typedef enum
diff --git a/vnet/vnet/ipsec/ipsec_output.c b/vnet/vnet/ipsec/ipsec_output.c
index 3810520..9797789 100644
--- a/vnet/vnet/ipsec/ipsec_output.c
+++ b/vnet/vnet/ipsec/ipsec_output.c
@@ -21,11 +21,17 @@
 
 #include <vnet/ipsec/ipsec.h>
 
+#if DPDK_CRYPTO==1
+#define ESP_NODE "dpdk-esp-encrypt"
+#else
+#define ESP_NODE "esp-encrypt"
+#endif
+
 #if IPSEC > 0
 
 #define foreach_ipsec_output_next                \
 _(DROP, "error-drop")                            \
-_(ESP_ENCRYPT, "esp-encrypt")
+_(ESP_ENCRYPT, ESP_NODE)
 
 #define _(v, s) IPSEC_OUTPUT_NEXT_##v,
 typedef enum
diff --git a/vpp/Makefile.am b/vpp/Makefile.am
index 52ce065..214cc74 100644
--- a/vpp/Makefile.am
+++ b/vpp/Makefile.am
@@ -13,7 +13,7 @@
 
 AUTOMAKE_OPTIONS = foreign subdir-objects
 
-AM_CFLAGS = -Wall @DPDK@ @IPSEC@ @VCGN@ @IPV6SR@
+AM_CFLAGS = -Wall @DPDK@ @DPDK_CRYPTO@ @IPSEC@ @VCGN@ @IPV6SR@
 
 noinst_PROGRAMS = 
 BUILT_SOURCES =
@@ -110,6 +110,9 @@
 vpp_LDFLAGS += -l:libdpdk.a
 endif
 vpp_LDFLAGS += -Wl,--no-whole-archive
+if WITH_DPDK_CRYPTO
+vpp_LDADD += -L$(AESNI_MULTI_BUFFER_LIB_PATH) -lIPSec_MB
+endif
 endif
 
 vpp_LDADD += -lvppinfra -lm -lpthread -ldl
diff --git a/vpp/configure.ac b/vpp/configure.ac
index 0bc13e6..d36695c 100644
--- a/vpp/configure.ac
+++ b/vpp/configure.ac
@@ -9,6 +9,11 @@
             [with_dpdk=1],
             [with_dpdk=0])
 
+AC_ARG_WITH(dpdk_crypto,
+            AC_HELP_STRING([--with-dpdk-crypto],[Use DPDK cryptodev]),
+            [with_dpdk_crypto=1],
+            [with_dpdk_crypto=0])
+
 AC_ARG_ENABLE(dpdk-shared,
               AC_HELP_STRING([--enable-dpdk-shared],[Link with DPDK shared lib]),
               [enable_dpdk_shared=1],
@@ -32,6 +37,9 @@
 AM_CONDITIONAL(WITH_DPDK, test "$with_dpdk" = "1")
 AM_CONDITIONAL(ENABLE_DPDK_SHARED, test "$enable_dpdk_shared" = "1")
 AC_SUBST(DPDK,["-DDPDK=${with_dpdk} -DDPDK_SHARED_LIB=${enable_dpdk_shared}"])
+AM_CONDITIONAL(WITH_DPDK_CRYPTO, test "$with_dpdk_crypto" = "1")
+AC_SUBST(DPDK_CRYPTO,[-DDPDK_CRYPTO=${with_dpdk_crypto}])
+
 
 AM_COND_IF(
 	[ENABLE_DPDK_SHARED],