IPSEC; dpdk backend for tunnel interface encryption

Change-Id: Ide2a9df18db371c8428855d7f12f246006d7c04c
Signed-off-by: Neale Ranns <nranns@cisco.com>
diff --git a/src/vnet/api_errno.h b/src/vnet/api_errno.h
index b474e19..c4cb3c9 100644
--- a/src/vnet/api_errno.h
+++ b/src/vnet/api_errno.h
@@ -147,7 +147,8 @@
 _(NON_ETHERNET, -151, "Interface is not an Ethernet interface")         \
 _(BD_ALREADY_HAS_BVI, -152, "Bridge domain already has a BVI interface") \
 _(INVALID_PROTOCOL, -153, "Invalid Protocol")                           \
-_(INVALID_ALGORITHM, -154, "Invalid Algorithm")
+_(INVALID_ALGORITHM, -154, "Invalid Algorithm")                         \
+_(RSRC_IN_USE, -155, "Resource In Use")
 
 typedef enum
 {
diff --git a/src/vnet/ipsec/ipsec.c b/src/vnet/ipsec/ipsec.c
index c530a44..da1a3fe 100644
--- a/src/vnet/ipsec/ipsec.c
+++ b/src/vnet/ipsec/ipsec.c
@@ -100,6 +100,16 @@
   *out_next_index = vlib_node_add_next (vm, prev_node->index, node->index);
 }
 
+static void
+ipsec_add_feature (const char *arc_name,
+		   const char *node_name, u32 * out_feature_index)
+{
+  u8 arc;
+
+  arc = vnet_get_feature_arc_index (arc_name);
+  *out_feature_index = vnet_get_feature_index (arc, node_name);
+}
+
 u32
 ipsec_register_ah_backend (vlib_main_t * vm, ipsec_main_t * im,
 			   const char *name,
@@ -132,13 +142,16 @@
 ipsec_register_esp_backend (vlib_main_t * vm, ipsec_main_t * im,
 			    const char *name,
 			    const char *esp4_encrypt_node_name,
+			    const char *esp4_encrypt_node_tun_name,
 			    const char *esp4_decrypt_node_name,
 			    const char *esp6_encrypt_node_name,
+			    const char *esp6_encrypt_node_tun_name,
 			    const char *esp6_decrypt_node_name,
 			    check_support_cb_t esp_check_support_cb,
 			    add_del_sa_sess_cb_t esp_add_del_sa_sess_cb)
 {
   ipsec_esp_backend_t *b;
+
   pool_get (im->esp_backends, b);
   b->name = format (0, "%s%c", name, 0);
 
@@ -151,27 +164,42 @@
   ipsec_add_node (vm, esp6_decrypt_node_name, "ipsec6-input-feature",
 		  &b->esp6_decrypt_node_index, &b->esp6_decrypt_next_index);
 
+  ipsec_add_feature ("ip4-output", esp4_encrypt_node_tun_name,
+		     &b->esp4_encrypt_tun_feature_index);
+  ipsec_add_feature ("ip6-output", esp6_encrypt_node_tun_name,
+		     &b->esp6_encrypt_tun_feature_index);
+
   b->check_support_cb = esp_check_support_cb;
   b->add_del_sa_sess_cb = esp_add_del_sa_sess_cb;
   return b - im->esp_backends;
 }
 
-static walk_rc_t
-ipsec_sa_restack (ipsec_sa_t * sa, void *ctx)
+clib_error_t *
+ipsec_rsc_in_use (ipsec_main_t * im)
 {
-  ipsec_sa_stack (sa);
+  /* return an error is crypto resource are in use */
+  if (pool_elts (im->sad) > 0)
+    return clib_error_return (0,
+			      "%d SA entries configured",
+			      pool_elts (im->sad));
 
-  return (WALK_CONTINUE);
+  if (pool_elts (im->tunnel_interfaces))
+    return clib_error_return (0,
+			      "%d tunnel-interface entries configured",
+			      pool_elts (im->tunnel_interfaces));
+
+  return (NULL);
 }
 
 int
 ipsec_select_ah_backend (ipsec_main_t * im, u32 backend_idx)
 {
-  if (pool_elts (im->sad) > 0
-      || pool_is_free_index (im->ah_backends, backend_idx))
-    {
-      return -1;
-    }
+  if (ipsec_rsc_in_use (im))
+    return VNET_API_ERROR_RSRC_IN_USE;
+
+  if (pool_is_free_index (im->ah_backends, backend_idx))
+    return VNET_API_ERROR_INVALID_VALUE;
+
   ipsec_ah_backend_t *b = pool_elt_at_index (im->ah_backends, backend_idx);
   im->ah_current_backend = backend_idx;
   im->ah4_encrypt_node_index = b->ah4_encrypt_node_index;
@@ -183,18 +211,18 @@
   im->ah6_encrypt_next_index = b->ah6_encrypt_next_index;
   im->ah6_decrypt_next_index = b->ah6_decrypt_next_index;
 
-  ipsec_sa_walk (ipsec_sa_restack, NULL);
   return 0;
 }
 
 int
 ipsec_select_esp_backend (ipsec_main_t * im, u32 backend_idx)
 {
-  if (pool_elts (im->sad) > 0
-      || pool_is_free_index (im->esp_backends, backend_idx))
-    {
-      return -1;
-    }
+  if (ipsec_rsc_in_use (im))
+    return VNET_API_ERROR_RSRC_IN_USE;
+
+  if (pool_is_free_index (im->esp_backends, backend_idx))
+    return VNET_API_ERROR_INVALID_VALUE;
+
   ipsec_esp_backend_t *b = pool_elt_at_index (im->esp_backends, backend_idx);
   im->esp_current_backend = backend_idx;
   im->esp4_encrypt_node_index = b->esp4_encrypt_node_index;
@@ -206,7 +234,9 @@
   im->esp6_encrypt_next_index = b->esp6_encrypt_next_index;
   im->esp6_decrypt_next_index = b->esp6_decrypt_next_index;
 
-  ipsec_sa_walk (ipsec_sa_restack, NULL);
+  im->esp4_encrypt_tun_feature_index = b->esp4_encrypt_tun_feature_index;
+  im->esp6_encrypt_tun_feature_index = b->esp6_encrypt_tun_feature_index;
+
   return 0;
 }
 
@@ -243,8 +273,10 @@
 
   idx = ipsec_register_esp_backend (vm, im, "default openssl backend",
 				    "esp4-encrypt",
+				    "esp4-encrypt-tun",
 				    "esp4-decrypt",
 				    "esp6-encrypt",
+				    "esp6-encrypt-tun",
 				    "esp6-decrypt",
 				    ipsec_check_esp_support, NULL);
   im->esp_default_backend = idx;
diff --git a/src/vnet/ipsec/ipsec.h b/src/vnet/ipsec/ipsec.h
index 232390c..45576b3 100644
--- a/src/vnet/ipsec/ipsec.h
+++ b/src/vnet/ipsec/ipsec.h
@@ -62,6 +62,8 @@
   u32 esp6_decrypt_node_index;
   u32 esp6_encrypt_next_index;
   u32 esp6_decrypt_next_index;
+  u32 esp4_encrypt_tun_feature_index;
+  u32 esp6_encrypt_tun_feature_index;
 } ipsec_esp_backend_t;
 
 typedef struct
@@ -134,6 +136,10 @@
   u32 ah6_encrypt_next_index;
   u32 ah6_decrypt_next_index;
 
+  /* tun encrypt arcs and feature nodes */
+  u32 esp4_encrypt_tun_feature_index;
+  u32 esp6_encrypt_tun_feature_index;
+
   /* pool of ah backends */
   ipsec_ah_backend_t *ah_backends;
   /* pool of esp backends */
@@ -214,8 +220,10 @@
 u32 ipsec_register_esp_backend (vlib_main_t * vm, ipsec_main_t * im,
 				const char *name,
 				const char *esp4_encrypt_node_name,
+				const char *esp4_encrypt_tun_node_name,
 				const char *esp4_decrypt_node_name,
 				const char *esp6_encrypt_node_name,
+				const char *esp6_encrypt_tun_node_name,
 				const char *esp6_decrypt_node_name,
 				check_support_cb_t esp_check_support_cb,
 				add_del_sa_sess_cb_t esp_add_del_sa_sess_cb);
@@ -223,6 +231,8 @@
 int ipsec_select_ah_backend (ipsec_main_t * im, u32 ah_backend_idx);
 int ipsec_select_esp_backend (ipsec_main_t * im, u32 esp_backend_idx);
 
+clib_error_t *ipsec_rsc_in_use (ipsec_main_t * im);
+
 always_inline ipsec_sa_t *
 ipsec_sa_get (u32 sa_index)
 {
diff --git a/src/vnet/ipsec/ipsec_api.c b/src/vnet/ipsec/ipsec_api.c
index 4a15beb..5683b6c 100644
--- a/src/vnet/ipsec/ipsec_api.c
+++ b/src/vnet/ipsec/ipsec_api.c
@@ -909,23 +909,13 @@
   switch (protocol)
     {
     case IPSEC_PROTOCOL_ESP:
-      if (pool_is_free_index (im->esp_backends, mp->index))
-	{
-	  rv = VNET_API_ERROR_INVALID_VALUE;
-	  break;
-	}
-      ipsec_select_esp_backend (im, mp->index);
+      rv = ipsec_select_esp_backend (im, mp->index);
       break;
     case IPSEC_PROTOCOL_AH:
-      if (pool_is_free_index (im->ah_backends, mp->index))
-	{
-	  rv = VNET_API_ERROR_INVALID_VALUE;
-	  break;
-	}
-      ipsec_select_ah_backend (im, mp->index);
+      rv = ipsec_select_ah_backend (im, mp->index);
       break;
     default:
-      rv = VNET_API_ERROR_INVALID_VALUE;
+      rv = VNET_API_ERROR_INVALID_PROTOCOL;
       break;
     }
 #else
diff --git a/src/vnet/ipsec/ipsec_cli.c b/src/vnet/ipsec/ipsec_cli.c
index 16d9048..4691214 100644
--- a/src/vnet/ipsec/ipsec_cli.c
+++ b/src/vnet/ipsec/ipsec_cli.c
@@ -661,17 +661,16 @@
 				 unformat_input_t * input,
 				 vlib_cli_command_t * cmd)
 {
-  u32 backend_index;
-  ipsec_main_t *im = &ipsec_main;
-
-  if (pool_elts (im->sad) > 0)
-    {
-      return clib_error_return (0,
-				"Cannot change IPsec backend, while %u SA entries are configured",
-				pool_elts (im->sad));
-    }
-
   unformat_input_t _line_input, *line_input = &_line_input;
+  ipsec_main_t *im = &ipsec_main;
+  clib_error_t *error;
+  u32 backend_index;
+
+  error = ipsec_rsc_in_use (im);
+
+  if (error)
+    return error;
+
   /* Get a line of input. */
   if (!unformat_user (input, unformat_line_input, line_input))
     return 0;
diff --git a/src/vnet/ipsec/ipsec_if.c b/src/vnet/ipsec/ipsec_if.c
index 9f64634..8e2b4b5 100644
--- a/src/vnet/ipsec/ipsec_if.c
+++ b/src/vnet/ipsec/ipsec_if.c
@@ -231,18 +231,25 @@
 }
 
 static void
-ipsec_tunnel_feature_set (ipsec_tunnel_if_t * t, u8 enable)
+ipsec_tunnel_feature_set (ipsec_main_t * im, ipsec_tunnel_if_t * t, u8 enable)
 {
-  vnet_feature_enable_disable ("ip4-output",
-			       "esp4-encrypt-tun",
-			       t->sw_if_index, enable,
-			       &t->output_sa_index,
-			       sizeof (t->output_sa_index));
-  vnet_feature_enable_disable ("ip6-output",
-			       "esp6-encrypt-tun",
-			       t->sw_if_index, enable,
-			       &t->output_sa_index,
-			       sizeof (t->output_sa_index));
+  u8 arc;
+
+  arc = vnet_get_feature_arc_index ("ip4-output");
+
+  vnet_feature_enable_disable_with_index (arc,
+					  im->esp4_encrypt_tun_feature_index,
+					  t->sw_if_index, enable,
+					  &t->output_sa_index,
+					  sizeof (t->output_sa_index));
+
+  arc = vnet_get_feature_arc_index ("ip6-output");
+
+  vnet_feature_enable_disable_with_index (arc,
+					  im->esp6_encrypt_tun_feature_index,
+					  t->sw_if_index, enable,
+					  &t->output_sa_index,
+					  sizeof (t->output_sa_index));
 }
 
 int
@@ -377,7 +384,7 @@
 			       ~0);
       im->ipsec_if_by_sw_if_index[t->sw_if_index] = dev_instance;
 
-      ipsec_tunnel_feature_set (t, 1);
+      ipsec_tunnel_feature_set (im, t, 1);
 
       /*1st interface, register protocol */
       if (pool_elts (im->tunnel_interfaces) == 1)
@@ -402,7 +409,7 @@
       hi = vnet_get_hw_interface (vnm, t->hw_if_index);
       vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 0);	/* admin down */
 
-      ipsec_tunnel_feature_set (t, 0);
+      ipsec_tunnel_feature_set (im, t, 0);
       vnet_delete_hw_interface (vnm, t->hw_if_index);
 
       if (is_ip6)
@@ -663,9 +670,9 @@
        * re-enable the feature to get the new SA in
        * the workers are stopped so no packets are sent in the clear
        */
-      ipsec_tunnel_feature_set (t, 0);
+      ipsec_tunnel_feature_set (im, t, 0);
       t->output_sa_index = sa_index;
-      ipsec_tunnel_feature_set (t, 1);
+      ipsec_tunnel_feature_set (im, t, 1);
     }
 
   /* remove sa_id to sa_index mapping on old SA */
@@ -682,7 +689,6 @@
   return 0;
 }
 
-
 clib_error_t *
 ipsec_tunnel_if_init (vlib_main_t * vm)
 {
diff --git a/src/vnet/ipsec/ipsec_sa.c b/src/vnet/ipsec/ipsec_sa.c
index 2d2e90a..324e7a4 100644
--- a/src/vnet/ipsec/ipsec_sa.c
+++ b/src/vnet/ipsec/ipsec_sa.c
@@ -66,7 +66,7 @@
 /**
  * 'stack' (resolve the recursion for) the SA tunnel destination
  */
-void
+static void
 ipsec_sa_stack (ipsec_sa_t * sa)
 {
   ipsec_main_t *im = &ipsec_main;
diff --git a/src/vnet/ipsec/ipsec_sa.h b/src/vnet/ipsec/ipsec_sa.h
index 4085076..bde0958 100644
--- a/src/vnet/ipsec/ipsec_sa.h
+++ b/src/vnet/ipsec/ipsec_sa.h
@@ -207,7 +207,6 @@
 			 const ip46_address_t * tunnel_dst_addr,
 			 u32 * sa_index);
 extern u32 ipsec_sa_del (u32 id);
-extern void ipsec_sa_stack (ipsec_sa_t * sa);
 extern void ipsec_sa_set_crypto_alg (ipsec_sa_t * sa,
 				     ipsec_crypto_alg_t crypto_alg);
 extern void ipsec_sa_set_integ_alg (ipsec_sa_t * sa,