dpdk: rework cryptodev ipsec build and setup

Build Cryptodev IPsec support by default when DPDK is enabled but only build
hardware Cryptodev PMDs.

To enable Cryptodev support, a new startup.conf option for dpdk has been
introduced 'enable-cryptodev'.

During VPP init, if Cryptodev support is not enabled or not enough cryptodev
resources are available then default to OpenSSL ipsec implementation.

Change-Id: I5aa7e0d5c2676bdb41d775ef40364536a081956d
Signed-off-by: Sergio Gonzalez Monroy <sergio.gonzalez.monroy@intel.com>
diff --git a/src/vnet/devices/dpdk/ipsec/cli.c b/src/vnet/devices/dpdk/ipsec/cli.c
index 3b634e0..93df4a6 100644
--- a/src/vnet/devices/dpdk/ipsec/cli.c
+++ b/src/vnet/devices/dpdk/ipsec/cli.c
@@ -14,15 +14,23 @@
  */
 
 #include <vnet/vnet.h>
+#include <vnet/devices/dpdk/dpdk.h>
 #include <vnet/devices/dpdk/ipsec/ipsec.h>
 
 static void
 dpdk_ipsec_show_mapping (vlib_main_t * vm, u16 detail_display)
 {
+  dpdk_config_main_t *conf = &dpdk_config_main;
   dpdk_crypto_main_t *dcm = &dpdk_crypto_main;
   vlib_thread_main_t *tm = vlib_get_thread_main ();
   u32 i, skip_master;
 
+  if (!conf->cryptodev)
+    {
+      vlib_cli_output (vm, "DPDK Cryptodev support is disabled\n");
+      return;
+    }
+
   if (detail_display)
     vlib_cli_output (vm, "worker\t%10s\t%15s\tdir\tdev\tqp\n",
 		     "cipher", "auth");
diff --git a/src/vnet/devices/dpdk/ipsec/crypto_node.c b/src/vnet/devices/dpdk/ipsec/crypto_node.c
index 7b32704..e8fef23 100644
--- a/src/vnet/devices/dpdk/ipsec/crypto_node.c
+++ b/src/vnet/devices/dpdk/ipsec/crypto_node.c
@@ -22,6 +22,8 @@
 #include <vnet/ethernet/ethernet.h>
 #include <vnet/ipsec/ipsec.h>
 
+#include <vnet/devices/dpdk/dpdk.h>
+#include <vnet/devices/dpdk/dpdk_priv.h>
 #include <vnet/devices/dpdk/ipsec/ipsec.h>
 
 #define foreach_dpdk_crypto_input_next		\
@@ -183,24 +185,27 @@
   return n_deq;
 }
 
+/* *INDENT-OFF* */
 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 =
+  .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 _
-  }
-,};
+  },
+};
+/* *INDENT-ON* */
 
-#if DPDK_CRYPTO==1
 VLIB_NODE_FUNCTION_MULTIARCH (dpdk_crypto_input_node, dpdk_crypto_input_fn)
-#endif
 /*
  * fd.io coding-style-patch-verification: ON
  *
diff --git a/src/vnet/devices/dpdk/ipsec/dpdk_crypto_ipsec_doc.md b/src/vnet/devices/dpdk/ipsec/dpdk_crypto_ipsec_doc.md
index 8089696..fed2fe0 100644
--- a/src/vnet/devices/dpdk/ipsec/dpdk_crypto_ipsec_doc.md
+++ b/src/vnet/devices/dpdk/ipsec/dpdk_crypto_ipsec_doc.md
@@ -7,43 +7,55 @@
 
 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
+When DPDK support is enabled and there are enough Cryptodev resources for all workers, the node graph is reconfigured by adding and changing default next nodes.
 
 The following nodes are added:
 * dpdk-crypto-input : polling input node, basically dequeuing from crypto devices.
+* dpdk-esp-encrypt : internal node.
+* dpdk-esp-decrypt : internal node.
 * dpdk-esp-encrypt-post : internal node.
 * dpdk-esp-decrypt-post : internal node.
 
+Set new default next nodes:
+* for esp encryption: esp-encrypt -> dpdk-esp-encrypt
+* for esp decryption: esp-decrypt -> dpdk-esp-decrypt
+
 
 ### How to enable VPP IPSec with DPDK Cryptodev support
 
-To enable DPDK Cryptodev support (disabled by default), we need the following env option:
+DPDK Cryptodev is supported in DPDK enabled VPP.
+By default, only HW Cryptodev is supported but needs to be explicetly enabled with the following config option:
 
-    vpp_uses_dpdk_cryptodev=yes
+```
+dpdk {
+    enable-cryptodev
+}
+```
+
+To enable SW Cryptodev support (AESNI-MB-PMD and GCM-PMD), we need the following env option:
+
+    vpp_uses_dpdk_cryptodev_sw=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)
+* set the option when building vpp (ie. make vpp_uses_dpdk_cryptodev_sw=yes build-release)
+
+When enabling SW Cryptodev support, it means that you need to pre-build the required crypto libraries needed by those SW Cryptodev PMDs.
 
 
 ### 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:
+* if there are not enough crypto resources for all workers, the graph node is not modifed, therefore the default VPP IPsec implementation based in OpenSSL is used. The following message is displayed:
 
       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.
+To enable DPDK Cryptodev the user just need to provide the startup.conf option
+as mentioned previously.
 
 Example startup.conf:
 
@@ -53,6 +65,7 @@
     num-mbufs 131072
     dev 0000:81:00.0
     dev 0000:81:00.1
+    enable-cryptodev
     dev 0000:85:01.0
     dev 0000:85:01.1
     vdev cryptodev_aesni_mb_pmd,socket_id=1
diff --git a/src/vnet/devices/dpdk/ipsec/esp.h b/src/vnet/devices/dpdk/ipsec/esp.h
index 7ef90c4..d414d67 100644
--- a/src/vnet/devices/dpdk/ipsec/esp.h
+++ b/src/vnet/devices/dpdk/ipsec/esp.h
@@ -97,60 +97,11 @@
 }
 
 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)
+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;
@@ -164,7 +115,7 @@
       break;
     default:
       return -1;
-  }
+    }
 
   cipher_xform->type = RTE_CRYPTO_SYM_XFORM_CIPHER;
 
@@ -172,10 +123,11 @@
 }
 
 static_always_inline int
-translate_integ_algo(ipsec_integ_alg_t integ_alg,
-		     struct rte_crypto_sym_xform *auth_xform, int use_esn)
+translate_integ_algo (ipsec_integ_alg_t integ_alg,
+		      struct rte_crypto_sym_xform *auth_xform, int use_esn)
 {
-  switch (integ_alg) {
+  switch (integ_alg)
+    {
     case IPSEC_INTEG_ALG_NONE:
       auth_xform->auth.algo = RTE_CRYPTO_AUTH_NULL;
       auth_xform->auth.digest_length = 0;
@@ -203,11 +155,11 @@
     case IPSEC_INTEG_ALG_AES_GCM_128:
       auth_xform->auth.algo = RTE_CRYPTO_AUTH_AES_GCM;
       auth_xform->auth.digest_length = 16;
-      auth_xform->auth.add_auth_data_length = use_esn? 12 : 8;
+      auth_xform->auth.add_auth_data_length = use_esn ? 12 : 8;
       break;
     default:
       return -1;
-  }
+    }
 
   auth_xform->type = RTE_CRYPTO_SYM_XFORM_AUTH;
 
@@ -215,25 +167,26 @@
 }
 
 static_always_inline int
-create_sym_sess(ipsec_sa_t *sa, crypto_sa_session_t *sa_sess, u8 is_outbound)
+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;
+  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 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;
+  crypto_worker_qp_key_t *p_key = (crypto_worker_qp_key_t *) & key;
 
   if (sa->crypto_alg == IPSEC_CRYPTO_ALG_AES_GCM_128)
     {
       sa->crypto_key_len -= 4;
-      clib_memcpy(&sa->salt, &sa->crypto_key[sa->crypto_key_len], 4);
+      clib_memcpy (&sa->salt, &sa->crypto_key[sa->crypto_key_len], 4);
     }
   else
     {
-      sa->salt = (u32) rand();
+      sa->salt = (u32) rand ();
     }
 
   cipher_xform.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
@@ -244,11 +197,11 @@
   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)
+  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, sa->use_esn) < 0)
+  if (translate_integ_algo (sa->integ_alg, &auth_xform, sa->use_esn) < 0)
     return -1;
   p_key->auth_algo = auth_xform.auth.algo;
 
@@ -269,17 +222,17 @@
 
   p_key->is_outbound = is_outbound;
 
-  data = hash_get(cwm->algo_qp_map, key);
+  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);
+    rte_cryptodev_sym_session_create (cwm->qp_data[*data].dev_id, xfs);
 
   if (!sa_sess->sess)
     return -1;
 
-  sa_sess->qp_index = (u8)*data;
+  sa_sess->qp_index = (u8) * data;
 
   return 0;
 }
diff --git a/src/vnet/devices/dpdk/ipsec/esp_decrypt.c b/src/vnet/devices/dpdk/ipsec/esp_decrypt.c
index 89ab9f9..53b2d12 100644
--- a/src/vnet/devices/dpdk/ipsec/esp_decrypt.c
+++ b/src/vnet/devices/dpdk/ipsec/esp_decrypt.c
@@ -22,6 +22,8 @@
 #include <vnet/ipsec/ipsec.h>
 #include <vnet/devices/dpdk/ipsec/ipsec.h>
 #include <vnet/devices/dpdk/ipsec/esp.h>
+#include <vnet/devices/dpdk/dpdk.h>
+#include <vnet/devices/dpdk/dpdk_priv.h>
 
 #define foreach_esp_decrypt_next	       \
 _(DROP, "error-drop")			       \
@@ -189,7 +191,14 @@
 	  if (PREDICT_FALSE(!sa_sess->sess))
 	    {
 	      int ret = create_sym_sess(sa0, sa_sess, 0);
-	      ASSERT(ret == 0);
+
+	      if (PREDICT_FALSE (ret))
+		{
+		  to_next[0] = bi0;
+		  to_next += 1;
+		  n_left_to_next -= 1;
+		  goto trace;
+		}
 	    }
 
 	  sess = sa_sess->sess;
diff --git a/src/vnet/devices/dpdk/ipsec/esp_encrypt.c b/src/vnet/devices/dpdk/ipsec/esp_encrypt.c
index 10bb461..b6f0000 100644
--- a/src/vnet/devices/dpdk/ipsec/esp_encrypt.c
+++ b/src/vnet/devices/dpdk/ipsec/esp_encrypt.c
@@ -22,6 +22,8 @@
 #include <vnet/ipsec/ipsec.h>
 #include <vnet/devices/dpdk/ipsec/ipsec.h>
 #include <vnet/devices/dpdk/ipsec/esp.h>
+#include <vnet/devices/dpdk/dpdk.h>
+#include <vnet/devices/dpdk/dpdk_priv.h>
 
 #define foreach_esp_encrypt_next                   \
 _(DROP, "error-drop")                              \
@@ -179,7 +181,14 @@
 	  if (PREDICT_FALSE (!sa_sess->sess))
 	    {
 	      int ret = create_sym_sess (sa0, sa_sess, 1);
-	      ASSERT (ret == 0);
+
+	      if (PREDICT_FALSE (ret))
+		{
+		  to_next[0] = bi0;
+		  to_next += 1;
+		  n_left_to_next -= 1;
+		  goto trace;
+		}
 	    }
 
 	  qp_index = sa_sess->qp_index;
diff --git a/src/vnet/devices/dpdk/ipsec/ipsec.c b/src/vnet/devices/dpdk/ipsec/ipsec.c
index de253f0..05c17c9 100644
--- a/src/vnet/devices/dpdk/ipsec/ipsec.c
+++ b/src/vnet/devices/dpdk/ipsec/ipsec.c
@@ -15,24 +15,69 @@
 #include <vnet/vnet.h>
 #include <vnet/ip/ip.h>
 #include <vnet/api_errno.h>
+#include <vnet/ipsec/ipsec.h>
+#include <vlib/node_funcs.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_NB_SESS_OBJS  20000
 #define DPDK_CRYPTO_CACHE_SIZE	  512
 #define DPDK_CRYPTO_PRIV_SIZE	  128
-#define DPDK_CRYPTO_N_QUEUE_DESC  512
+#define DPDK_CRYPTO_N_QUEUE_DESC  1024
 #define DPDK_CRYPTO_NB_COPS	  (1024 * 4)
 
-/*
- * return:
- * -1: 	update failed
- * 0:	already exist
- * 1:	mapped
- */
 static 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 void
 update_qp_data (crypto_worker_main_t * cwm,
 		u8 cdev_id, u16 qp_id, u8 is_outbound, u16 * idx)
 {
@@ -45,7 +90,7 @@
 
       if (qpd->dev_id == cdev_id && qpd->qp_id == qp_id &&
 	  qpd->is_outbound == is_outbound)
-	  return 0;
+	  return;
     }
   /* *INDENT-ON* */
 
@@ -54,13 +99,10 @@
   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
  */
@@ -70,7 +112,6 @@
 	     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;
@@ -83,17 +124,12 @@
   if (ret)
     return 0;
 
-  mapped = update_qp_data (cwm, cdev_id, qp, is_outbound, &qp_index);
-  if (mapped < 0)
-    return -1;
+  update_qp_data (cwm, cdev_id, qp, is_outbound, &qp_index);
 
   data = (uword) qp_index;
+  hash_set (cwm->algo_qp_map, key, data);
 
-  ret = hash_set (cwm->algo_qp_map, key, data);
-  if (!ret)
-    rte_panic ("Failed to insert hash table\n");
-
-  return mapped;
+  return 1;
 }
 
 /*
@@ -120,19 +156,13 @@
       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;
+	  mapped |= add_mapping (cwm, cdev_id, qp, is_outbound, i, j);
 	}
     }
 
@@ -169,8 +199,33 @@
 }
 
 static clib_error_t *
-dpdk_ipsec_init (vlib_main_t * vm)
+dpdk_ipsec_check_support (ipsec_sa_t * sa)
 {
+  if (sa->crypto_alg == IPSEC_CRYPTO_ALG_AES_GCM_128)
+    {
+      if (sa->integ_alg != IPSEC_INTEG_ALG_NONE)
+	return clib_error_return (0, "unsupported integ-alg %U with "
+				  "crypto-algo aes-gcm-128",
+				  format_ipsec_integ_alg, sa->integ_alg);
+      sa->integ_alg = IPSEC_INTEG_ALG_AES_GCM_128;
+    }
+  else
+    {
+      if (sa->integ_alg == IPSEC_INTEG_ALG_NONE ||
+	  sa->integ_alg == IPSEC_INTEG_ALG_AES_GCM_128)
+	return clib_error_return (0, "unsupported integ-alg %U",
+				  format_ipsec_integ_alg, sa->integ_alg);
+    }
+
+  return 0;
+}
+
+static uword
+dpdk_ipsec_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
+		    vlib_frame_t * f)
+{
+  dpdk_config_main_t *conf = &dpdk_config_main;
+  ipsec_main_t *im = &ipsec_main;
   dpdk_crypto_main_t *dcm = &dpdk_crypto_main;
   vlib_thread_main_t *tm = vlib_get_thread_main ();
   struct rte_cryptodev_config dev_conf;
@@ -180,8 +235,19 @@
   i32 dev_id, ret;
   u32 i, skip_master;
 
+  if (!conf->cryptodev)
+    {
+      clib_warning ("DPDK Cryptodev support is disabled, "
+		    "default to OpenSSL IPsec");
+      return 0;
+    }
+
   if (check_cryptodev_queues () < 0)
-    return clib_error_return (0, "not enough cryptodevs for ipsec");
+    {
+      conf->cryptodev = 0;
+      clib_warning ("not enough Cryptodevs, default to OpenSSL IPsec");
+      return 0;
+    }
 
   vec_alloc (dcm->workers_main, tm->n_vlib_mains);
   _vec_len (dcm->workers_main) = tm->n_vlib_mains;
@@ -221,24 +287,17 @@
 	    {
 	      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);
+		{
+		  clib_warning ("unable to create hash table for worker %u",
+				vlib_mains[i]->cpu_index);
+		  goto error;
+		}
 	      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");
-	    }
+	    qp += add_cdev_mapping (cwm, &cdev_info, dev_id, qp, is_outbound);
 	}
 
       if (qp == 0)
@@ -246,12 +305,15 @@
 
       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.nb_objs = DPDK_CRYPTO_NB_SESS_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);
+	{
+	  clib_warning ("cryptodev %u config error", dev_id);
+	  goto error;
+	}
 
       qp_conf.nb_descriptors = DPDK_CRYPTO_N_QUEUE_DESC;
       for (qp = 0; qp < dev_conf.nb_queue_pairs; qp++)
@@ -259,38 +321,65 @@
 	  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);
+	    {
+	      clib_warning ("cryptodev %u qp %u setup error", dev_id, qp);
+	      goto error;
+	    }
 	}
+      vec_validate_aligned (dcm->cop_pools, dev_conf.socket_id,
+			    CLIB_CACHE_LINE_BYTES);
+
+      if (!vec_elt (dcm->cop_pools, dev_conf.socket_id))
+	{
+	  u8 *pool_name = format (0, "crypto_op_pool_socket%u%c",
+				  dev_conf.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,
+					   dev_conf.socket_id);
+	  vec_free (pool_name);
+
+	  if (!rmp)
+	    {
+	      clib_warning ("failed to allocate mempool on socket %u",
+			    dev_conf.socket_id);
+	      goto error;
+	    }
+	  vec_elt (dcm->cop_pools, dev_conf.socket_id) = rmp;
+	}
+
       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);
+	       DPDK_CRYPTO_NB_SESS_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 ();
 
+  /* Add new next node and set as default */
+  vlib_node_t *node, *next_node;
+
+  next_node = vlib_get_node_by_name (vm, (u8 *) "dpdk-esp-encrypt");
+  ASSERT (next_node);
+  node = vlib_get_node_by_name (vm, (u8 *) "ipsec-output-ip4");
+  ASSERT (node);
+  im->esp_encrypt_node_index = next_node->index;
+  im->esp_encrypt_next_index =
+    vlib_node_add_next (vm, node->index, next_node->index);
+
+  next_node = vlib_get_node_by_name (vm, (u8 *) "dpdk-esp-decrypt");
+  ASSERT (next_node);
+  node = vlib_get_node_by_name (vm, (u8 *) "ipsec-input-ip4");
+  ASSERT (node);
+  im->esp_decrypt_node_index = next_node->index;
+  im->esp_decrypt_next_index =
+    vlib_node_add_next (vm, node->index, next_node->index);
+
+  im->cb.check_support_cb = dpdk_ipsec_check_support;
+  im->cb.add_del_sa_sess_cb = add_del_sa_sess;
+
   if (vec_len (vlib_mains) == 0)
     vlib_node_set_state (&vlib_global_main, dpdk_crypto_input_node.index,
 			 VLIB_NODE_STATE_POLLING);
@@ -299,10 +388,38 @@
       vlib_node_set_state (vlib_mains[i], dpdk_crypto_input_node.index,
 			   VLIB_NODE_STATE_POLLING);
 
+  /* TODO cryptodev counters */
+
+  return 0;
+
+error:
+  ;
+  crypto_worker_main_t *cwm;
+  struct rte_mempool **mp;
+  /* *INDENT-OFF* */
+  vec_foreach (cwm, dcm->workers_main)
+    hash_free (cwm->algo_qp_map);
+
+  vec_foreach (mp, dcm->cop_pools)
+    {
+      if (mp)
+	rte_mempool_free (mp[0]);
+    }
+  /* *INDENT-ON* */
+  vec_free (dcm->workers_main);
+  vec_free (dcm->cop_pools);
+
   return 0;
 }
 
-VLIB_MAIN_LOOP_ENTER_FUNCTION (dpdk_ipsec_init);
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (dpdk_ipsec_process_node,static) = {
+    .function = dpdk_ipsec_process,
+    .type = VLIB_NODE_TYPE_PROCESS,
+    .name = "dpdk-ipsec-process",
+    .process_log2_n_stack_bytes = 17,
+};
+/* *INDENT-ON* */
 
 /*
  * fd.io coding-style-patch-verification: ON
diff --git a/src/vnet/devices/dpdk/ipsec/ipsec.h b/src/vnet/devices/dpdk/ipsec/ipsec.h
index e6c7498..3465b36 100644
--- a/src/vnet/devices/dpdk/ipsec/ipsec.h
+++ b/src/vnet/devices/dpdk/ipsec/ipsec.h
@@ -167,7 +167,7 @@
     .type = RTE_CRYPTO_SYM_XFORM_CIPHER,.cipher =
 	RTE_CRYPTO_CIPHER_3DES_CBC,.name = "3DES-CBC"},
     {
-    .type = RTE_CRYPTO_SYM_XFORM_CIPHER,.auth =
+    .type = RTE_CRYPTO_SYM_XFORM_CIPHER,.cipher =
 	RTE_CRYPTO_CIPHER_AES_GCM,.name = "AES-GCM"},
     {
     .type = RTE_CRYPTO_SYM_XFORM_AUTH,.auth =