ipsec: ipsec-tun protect

please consult the new tunnel proposal at:
  https://wiki.fd.io/view/VPP/IPSec

Type: feature

Change-Id: I52857fc92ae068b85f59be08bdbea1bd5932e291
Signed-off-by: Neale Ranns <nranns@cisco.com>
diff --git a/src/scripts/vnet/ipsec_tun_protect b/src/scripts/vnet/ipsec_tun_protect
new file mode 100644
index 0000000..ed81f91
--- /dev/null
+++ b/src/scripts/vnet/ipsec_tun_protect
@@ -0,0 +1,71 @@
+
+create packet-generator interface pg0
+create packet-generator interface pg1
+
+pipe create
+
+ip table add 1
+set int ip table pg1 1
+set int ip table pipe0.1 1
+
+set int ip address pg0 192.168.0.1/24
+set int ip address pg1 192.168.1.1/24
+
+set int ip address pipe0.0 10.0.0.1/24
+set int ip address pipe0.1 10.0.0.2/24
+
+set int state pg0 up
+set int state pg1 up
+set int state pipe0 up
+
+ipsec sa add 20 spi 200 crypto-key 6541686776336961656264656f6f6579 crypto-alg aes-cbc-128
+ipsec sa add 30 spi 300 crypto-key 6541686776336961656264656f6f6579 crypto-alg aes-cbc-128
+
+create ipip tunnel src 10.0.0.1 dst 10.0.0.2
+create ipip tunnel src 10.0.0.2 dst 10.0.0.1
+
+ipsec tunnel protect ipip0 sa-in 20 sa-out 30
+ipsec tunnel protect ipip1 sa-in 30 sa-out 20
+
+set int state ipip0 up
+set int unnum ipip0 use pg0
+
+set int state ipip1 up
+set int ip table ipip1 1
+set int unnum ipip1 use pg1
+
+ip route add 192.168.1.0/24 via ipip0
+set ip arp pg1 192.168.1.2 00:11:22:33:44:55
+ip route add table 1 192.168.0.0/24 via ipip1
+set ip arp pg0 192.168.0.2 00:11:22:33:44:66
+
+trace add pg-input 100
+
+packet-generator new {
+  name ipsec1
+  limit 1
+  rate 1e4
+  node ip4-input
+  interface pg0
+  size 100-100
+  data {
+   UDP: 192.168.0.2 -> 192.168.1.2
+   UDP: 4321 -> 1234
+    length 72
+    incrementing 100
+  }
+}
+packet-generator new {
+  name ipsec2
+  limit 1
+  rate 1e4
+  node ip4-input
+  interface pg1
+  size 100-100
+  data {
+   UDP: 192.168.1.2 -> 192.168.0.2
+   UDP: 4321 -> 1234
+    length 72
+    incrementing 100
+  }
+}
diff --git a/src/vat/api_format.c b/src/vat/api_format.c
index fe1a87f..536c4b0 100644
--- a/src/vat/api_format.c
+++ b/src/vat/api_format.c
@@ -5084,41 +5084,6 @@
   vat_json_object_add_uint (node, "table_index", ntohl (mp->table_index));
 }
 
-static void vl_api_ipsec_gre_tunnel_add_del_reply_t_handler
-  (vl_api_ipsec_gre_tunnel_add_del_reply_t * mp)
-{
-  vat_main_t *vam = &vat_main;
-  i32 retval = ntohl (mp->retval);
-  if (vam->async_mode)
-    {
-      vam->async_errors += (retval < 0);
-    }
-  else
-    {
-      vam->retval = retval;
-      vam->sw_if_index = ntohl (mp->sw_if_index);
-      vam->result_ready = 1;
-    }
-  vam->regenerate_interface_table = 1;
-}
-
-static void vl_api_ipsec_gre_tunnel_add_del_reply_t_handler_json
-  (vl_api_ipsec_gre_tunnel_add_del_reply_t * mp)
-{
-  vat_main_t *vam = &vat_main;
-  vat_json_node_t node;
-
-  vat_json_init_object (&node);
-  vat_json_object_add_int (&node, "retval", ntohl (mp->retval));
-  vat_json_object_add_uint (&node, "sw_if_index", ntohl (mp->sw_if_index));
-
-  vat_json_print (vam->ofp, &node);
-  vat_json_free (&node);
-
-  vam->retval = ntohl (mp->retval);
-  vam->result_ready = 1;
-}
-
 static void vl_api_flow_classify_details_t_handler
   (vl_api_flow_classify_details_t * mp)
 {
@@ -5599,8 +5564,6 @@
  ip_source_and_port_range_check_add_del_reply)                          \
 _(IP_SOURCE_AND_PORT_RANGE_CHECK_INTERFACE_ADD_DEL_REPLY,               \
  ip_source_and_port_range_check_interface_add_del_reply)                \
-_(IPSEC_GRE_TUNNEL_ADD_DEL_REPLY, ipsec_gre_tunnel_add_del_reply)       \
-_(IPSEC_GRE_TUNNEL_DETAILS, ipsec_gre_tunnel_details)                   \
 _(DELETE_SUBIF_REPLY, delete_subif_reply)                               \
 _(L2_INTERFACE_PBB_TAG_REWRITE_REPLY, l2_interface_pbb_tag_rewrite_reply) \
 _(SET_PUNT_REPLY, set_punt_reply)                                       \
@@ -20087,52 +20050,6 @@
 }
 
 static int
-api_ipsec_gre_tunnel_add_del (vat_main_t * vam)
-{
-  unformat_input_t *i = vam->input;
-  vl_api_ipsec_gre_tunnel_add_del_t *mp;
-  u32 local_sa_id = 0;
-  u32 remote_sa_id = 0;
-  vl_api_ip4_address_t src_address;
-  vl_api_ip4_address_t dst_address;
-  u8 is_add = 1;
-  int ret;
-
-  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
-    {
-      if (unformat (i, "local_sa %d", &local_sa_id))
-	;
-      else if (unformat (i, "remote_sa %d", &remote_sa_id))
-	;
-      else
-	if (unformat (i, "src %U", unformat_vl_api_ip4_address, &src_address))
-	;
-      else
-	if (unformat (i, "dst %U", unformat_vl_api_ip4_address, &dst_address))
-	;
-      else if (unformat (i, "del"))
-	is_add = 0;
-      else
-	{
-	  clib_warning ("parse error '%U'", format_unformat_error, i);
-	  return -99;
-	}
-    }
-
-  M (IPSEC_GRE_TUNNEL_ADD_DEL, mp);
-
-  mp->tunnel.local_sa_id = ntohl (local_sa_id);
-  mp->tunnel.remote_sa_id = ntohl (remote_sa_id);
-  clib_memcpy (mp->tunnel.src, &src_address, sizeof (src_address));
-  clib_memcpy (mp->tunnel.dst, &dst_address, sizeof (dst_address));
-  mp->is_add = is_add;
-
-  S (mp);
-  W (ret);
-  return ret;
-}
-
-static int
 api_set_punt (vat_main_t * vam)
 {
   unformat_input_t *i = vam->input;
@@ -20173,99 +20090,6 @@
   return ret;
 }
 
-static void vl_api_ipsec_gre_tunnel_details_t_handler
-  (vl_api_ipsec_gre_tunnel_details_t * mp)
-{
-  vat_main_t *vam = &vat_main;
-
-  print (vam->ofp, "%11d%15U%15U%14d%14d",
-	 ntohl (mp->tunnel.sw_if_index),
-	 format_vl_api_ip4_address, mp->tunnel.src,
-	 format_vl_api_ip4_address, mp->tunnel.dst,
-	 ntohl (mp->tunnel.local_sa_id), ntohl (mp->tunnel.remote_sa_id));
-}
-
-static void
-vat_json_object_add_vl_api_ip4 (vat_json_node_t * node,
-				const char *name,
-				const vl_api_ip4_address_t addr)
-{
-  struct in_addr ip4;
-
-  clib_memcpy (&ip4, addr, sizeof (ip4));
-  vat_json_object_add_ip4 (node, name, ip4);
-}
-
-static void vl_api_ipsec_gre_tunnel_details_t_handler_json
-  (vl_api_ipsec_gre_tunnel_details_t * mp)
-{
-  vat_main_t *vam = &vat_main;
-  vat_json_node_t *node = NULL;
-
-  if (VAT_JSON_ARRAY != vam->json_tree.type)
-    {
-      ASSERT (VAT_JSON_NONE == vam->json_tree.type);
-      vat_json_init_array (&vam->json_tree);
-    }
-  node = vat_json_array_add (&vam->json_tree);
-
-  vat_json_init_object (node);
-  vat_json_object_add_uint (node, "sw_if_index",
-			    ntohl (mp->tunnel.sw_if_index));
-  vat_json_object_add_vl_api_ip4 (node, "src", mp->tunnel.src);
-  vat_json_object_add_vl_api_ip4 (node, "src", mp->tunnel.dst);
-  vat_json_object_add_uint (node, "local_sa_id",
-			    ntohl (mp->tunnel.local_sa_id));
-  vat_json_object_add_uint (node, "remote_sa_id",
-			    ntohl (mp->tunnel.remote_sa_id));
-}
-
-static int
-api_ipsec_gre_tunnel_dump (vat_main_t * vam)
-{
-  unformat_input_t *i = vam->input;
-  vl_api_ipsec_gre_tunnel_dump_t *mp;
-  vl_api_control_ping_t *mp_ping;
-  u32 sw_if_index;
-  u8 sw_if_index_set = 0;
-  int ret;
-
-  /* Parse args required to build the message */
-  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
-    {
-      if (unformat (i, "sw_if_index %d", &sw_if_index))
-	sw_if_index_set = 1;
-      else
-	break;
-    }
-
-  if (sw_if_index_set == 0)
-    {
-      sw_if_index = ~0;
-    }
-
-  if (!vam->json_output)
-    {
-      print (vam->ofp, "%11s%15s%15s%14s%14s",
-	     "sw_if_index", "src_address", "dst_address",
-	     "local_sa_id", "remote_sa_id");
-    }
-
-  /* Get list of gre-tunnel interfaces */
-  M (IPSEC_GRE_TUNNEL_DUMP, mp);
-
-  mp->sw_if_index = htonl (sw_if_index);
-
-  S (mp);
-
-  /* Use a control ping for synchronization */
-  MPING (CONTROL_PING, mp_ping);
-  S (mp_ping);
-
-  W (ret);
-  return ret;
-}
-
 static int
 api_delete_subif (vat_main_t * vam)
 {
@@ -22409,9 +22233,6 @@
 _(ip_source_and_port_range_check_interface_add_del,                     \
   "<intf> | sw_if_index <nn> [tcp-out-vrf <id>] [tcp-in-vrf <id>]"      \
   "[udp-in-vrf <id>] [udp-out-vrf <id>]")                               \
-_(ipsec_gre_tunnel_add_del,                                             \
-  "src <addr> dst <addr> local_sa <sa-id> remote_sa <sa-id> [del]")     \
-_(ipsec_gre_tunnel_dump, "[sw_if_index <nn>]")                          \
 _(delete_subif,"<intfc> | sw_if_index <nn>")                            \
 _(l2_interface_pbb_tag_rewrite,                                         \
   "<intfc> | sw_if_index <nn> \n"                                       \
diff --git a/src/vnet/CMakeLists.txt b/src/vnet/CMakeLists.txt
index 5465d71..462ced4 100644
--- a/src/vnet/CMakeLists.txt
+++ b/src/vnet/CMakeLists.txt
@@ -566,6 +566,8 @@
   ipsec/ipsec_sa.c
   ipsec/ipsec_spd.c
   ipsec/ipsec_spd_policy.c
+  ipsec/ipsec_tun.c
+  ipsec/ipsec_tun_in.c
   ipsec/esp_format.c
   ipsec/esp_encrypt.c
   ipsec/esp_decrypt.c
@@ -582,6 +584,7 @@
   ipsec/ipsec_if_in.c
   ipsec/ipsec_output.c
   ipsec/ipsec_input.c
+  ipsec/ipsec_tun_in.c
 )
 
 list(APPEND VNET_API_FILES ipsec/ipsec.api)
@@ -847,28 +850,6 @@
 list(APPEND VNET_API_FILES vxlan-gpe/vxlan_gpe.api)
 
 ##############################################################################
-# Tunnel protocol: ipsec+gre
-##############################################################################
-list(APPEND VNET_SOURCES
-  ipsec-gre/ipsec_gre.c
-  ipsec-gre/node.c
-  ipsec-gre/interface.c
-  ipsec-gre/ipsec_gre_api.c
-)
-
-list(APPEND VNET_MULTIARCH_SOURCES
-  ipsec-gre/node.c
-  ipsec-gre/ipsec_gre.c
-)
-
-list(APPEND VNET_HEADERS
-  ipsec-gre/ipsec_gre.h
-  ipsec-gre/error.def
-)
-
-list(APPEND VNET_API_FILES ipsec-gre/ipsec_gre.api)
-
-##############################################################################
 # LISP control plane: lisp-cp
 ##############################################################################
 
diff --git a/src/vnet/buffer.h b/src/vnet/buffer.h
index 6738f3c..812fe74 100644
--- a/src/vnet/buffer.h
+++ b/src/vnet/buffer.h
@@ -276,6 +276,7 @@
     struct
     {
       u32 sad_index;
+      u32 protect_index;
     } ipsec;
 
     /* MAP */
diff --git a/src/vnet/gre/gre.c b/src/vnet/gre/gre.c
index 72c76fc..ab2567d 100644
--- a/src/vnet/gre/gre.c
+++ b/src/vnet/gre/gre.c
@@ -530,6 +530,29 @@
   return s;
 }
 
+static int
+gre_tunnel_desc (u32 sw_if_index,
+		 ip46_address_t * src, ip46_address_t * dst, u8 * is_l2)
+{
+  gre_main_t *gm = &gre_main;
+  gre_tunnel_t *t;
+  u32 ti;
+
+  ti = gm->tunnel_index_by_sw_if_index[sw_if_index];
+
+  if (~0 == ti)
+    /* not one of ours */
+    return -1;
+
+  t = pool_elt_at_index (gm->tunnels, ti);
+
+  *src = t->tunnel_src;
+  *dst = t->tunnel_dst.fp_addr;
+  *is_l2 = t->type == GRE_TUNNEL_TYPE_TEB;
+
+  return (0);
+}
+
 /* *INDENT-OFF* */
 VNET_DEVICE_CLASS (gre_device_class) = {
   .name = "GRE tunnel device",
@@ -537,6 +560,7 @@
   .format_device = format_gre_device,
   .format_tx_trace = format_gre_tx_trace,
   .admin_up_down_function = gre_interface_admin_up_down,
+  .ip_tun_desc = gre_tunnel_desc,
 #ifdef SOON
   .clear counter = 0;
 #endif
diff --git a/src/vnet/interface.c b/src/vnet/interface.c
index 8af2b58..1702cdc 100644
--- a/src/vnet/interface.c
+++ b/src/vnet/interface.c
@@ -515,6 +515,30 @@
      VNET_INTERFACE_SET_FLAGS_HELPER_WANT_REDISTRIBUTE);
 }
 
+void
+vnet_sw_interface_admin_up (vnet_main_t * vnm, u32 sw_if_index)
+{
+  u32 flags = vnet_sw_interface_get_flags (vnm, sw_if_index);
+
+  if (!(flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
+    {
+      flags |= VNET_SW_INTERFACE_FLAG_ADMIN_UP;
+      vnet_sw_interface_set_flags (vnm, sw_if_index, flags);
+    }
+}
+
+void
+vnet_sw_interface_admin_down (vnet_main_t * vnm, u32 sw_if_index)
+{
+  u32 flags = vnet_sw_interface_get_flags (vnm, sw_if_index);
+
+  if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
+    {
+      flags &= ~(VNET_SW_INTERFACE_FLAG_ADMIN_UP);
+      vnet_sw_interface_set_flags (vnm, sw_if_index, flags);
+    }
+}
+
 static u32
 vnet_create_sw_interface_no_callbacks (vnet_main_t * vnm,
 				       vnet_sw_interface_t * template)
diff --git a/src/vnet/interface.h b/src/vnet/interface.h
index 5419fff..72a9326 100644
--- a/src/vnet/interface.h
+++ b/src/vnet/interface.h
@@ -47,7 +47,7 @@
 struct vnet_main_t;
 struct vnet_hw_interface_t;
 struct vnet_sw_interface_t;
-struct ip46_address_t;
+union ip46_address_t_;
 
 typedef enum
 {
@@ -174,6 +174,14 @@
 #define VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION_PRIO(f,p)     	\
   _VNET_INTERFACE_FUNCTION_DECL_PRIO(f,sw_interface_admin_up_down, p)
 
+/**
+ * Tunnel description parameters
+ */
+typedef int (*vnet_dev_class_ip_tunnel_desc_t) (u32 sw_if_index,
+						union ip46_address_t_ * src,
+						union ip46_address_t_ * dst,
+						u8 * is_l2);
+
 /* A class of hardware interface devices. */
 typedef struct _vnet_device_class
 {
@@ -235,6 +243,8 @@
   /* Format flow offload entry */
   format_function_t *format_flow;
 
+  vnet_dev_class_ip_tunnel_desc_t ip_tun_desc;
+
   /* Function to clear hardware counters for device. */
   void (*clear_counters) (u32 dev_class_instance);
 
diff --git a/src/vnet/interface_funcs.h b/src/vnet/interface_funcs.h
index b862f48..6d404b4 100644
--- a/src/vnet/interface_funcs.h
+++ b/src/vnet/interface_funcs.h
@@ -356,6 +356,9 @@
 clib_error_t *vnet_sw_interface_set_flags (vnet_main_t * vnm, u32 sw_if_index,
 					   vnet_sw_interface_flags_t flags);
 
+void vnet_sw_interface_admin_up (vnet_main_t * vnm, u32 sw_if_index);
+void vnet_sw_interface_admin_down (vnet_main_t * vnm, u32 sw_if_index);
+
 /* Change interface class. */
 clib_error_t *vnet_hw_interface_set_class (vnet_main_t * vnm, u32 hw_if_index,
 					   u32 new_hw_class_index);
diff --git a/src/vnet/ip/ip6_packet.h b/src/vnet/ip/ip6_packet.h
index ff47830..c8bc4c8 100644
--- a/src/vnet/ip/ip6_packet.h
+++ b/src/vnet/ip/ip6_packet.h
@@ -75,7 +75,7 @@
 } ip46_type_t;
 
 /* *INDENT-OFF* */
-typedef CLIB_PACKED (union {
+typedef CLIB_PACKED (union ip46_address_t_ {
   struct {
     u32 pad[3];
     ip4_address_t ip4;
@@ -95,6 +95,21 @@
                                          && ((a1)->as_u64[1] == (a2)->as_u64[1]))
 #define ip46_address_initializer {{{ 0 }}}
 
+static_always_inline int
+ip46_address_is_equal_v4 (const ip46_address_t * ip46,
+			  const ip4_address_t * ip4)
+{
+  return (ip46->ip4.as_u32 == ip4->as_u32);
+}
+
+static_always_inline int
+ip46_address_is_equal_v6 (const ip46_address_t * ip46,
+			  const ip6_address_t * ip6)
+{
+  return ((ip46->ip6.as_u64[0] == ip6->as_u64[0]) &&
+	  (ip46->ip6.as_u64[1] == ip6->as_u64[1]));
+}
+
 static_always_inline void
 ip46_address_copy (ip46_address_t * dst, const ip46_address_t * src)
 {
diff --git a/src/vnet/ipip/ipip.c b/src/vnet/ipip/ipip.c
index 5d40708..66c945e 100644
--- a/src/vnet/ipip/ipip.c
+++ b/src/vnet/ipip/ipip.c
@@ -297,6 +297,23 @@
   return /* no error */ 0;
 }
 
+static int
+ipip_tunnel_desc (u32 sw_if_index,
+		  ip46_address_t * src, ip46_address_t * dst, u8 * is_l2)
+{
+  ipip_tunnel_t *t;
+
+  t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index);
+  if (!t)
+    return -1;
+
+  *src = t->tunnel_src;
+  *dst = t->tunnel_dst;
+  *is_l2 = 0;
+
+  return (0);
+}
+
 /* *INDENT-OFF* */
 VNET_DEVICE_CLASS(ipip_device_class) = {
     .name = "IPIP tunnel device",
@@ -304,6 +321,7 @@
     .format_device = format_ipip_device,
     .format_tx_trace = format_ipip_tx_trace,
     .admin_up_down_function = ipip_interface_admin_up_down,
+    .ip_tun_desc = ipip_tunnel_desc,
 #ifdef SOON
     .clear counter = 0;
 #endif
diff --git a/src/vnet/ipip/ipip_api.c b/src/vnet/ipip/ipip_api.c
index 35b846e..62a9965 100644
--- a/src/vnet/ipip/ipip_api.c
+++ b/src/vnet/ipip/ipip_api.c
@@ -150,7 +150,9 @@
     {
     /* *INDENT-OFF* */
     pool_foreach(t, gm->tunnels,
-                 ({ send_ipip_tunnel_details(t, reg, mp->context); }));
+    ({
+      send_ipip_tunnel_details(t, reg, mp->context);
+    }));
     /* *INDENT-ON* */
     }
   else
diff --git a/src/vnet/ipsec-gre/dir.dox b/src/vnet/ipsec-gre/dir.dox
deleted file mode 100644
index e6ffd10..0000000
--- a/src/vnet/ipsec-gre/dir.dox
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/**
- @dir vnet/vnet/ipsec-gre
- @brief L2-GRE over IPSec tunnel interface implementation
-*/
diff --git a/src/vnet/ipsec-gre/error.def b/src/vnet/ipsec-gre/error.def
deleted file mode 100644
index d84e8ed..0000000
--- a/src/vnet/ipsec-gre/error.def
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/**
- * @file
- * @brief L2-GRE over IPSec errors.
- */
-
-
-ipsec_gre_error (NONE, "no error")
-ipsec_gre_error (UNKNOWN_PROTOCOL, "unknown protocol")
-ipsec_gre_error (UNSUPPORTED_VERSION, "unsupported version")
-ipsec_gre_error (PKTS_DECAP, "GRE input packets decapsulated")
-ipsec_gre_error (PKTS_ENCAP, "GRE output packets encapsulated")
-ipsec_gre_error (NO_SUCH_TUNNEL, "GRE input packets dropped due to missing tunnel")
diff --git a/src/vnet/ipsec-gre/interface.c b/src/vnet/ipsec-gre/interface.c
deleted file mode 100644
index 6a8bb7d..0000000
--- a/src/vnet/ipsec-gre/interface.c
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * gre_interface.c: gre interfaces
- *
- * Copyright (c) 2016 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/**
- * @file
- * @brief L2-GRE over IPSec tunnel interface.
- *
- * Creates ipsec-gre tunnel interface.
- * Provides a command line interface so humans can interact with VPP.
- */
-
-#include <vnet/vnet.h>
-#include <vnet/pg/pg.h>
-#include <vnet/ipsec-gre/ipsec_gre.h>
-#include <vnet/ip/format.h>
-#include <vnet/ipsec/ipsec.h>
-#include <vnet/l2/l2_input.h>
-
-#include <vnet/ipsec/esp.h>
-
-u8 *
-format_ipsec_gre_tunnel (u8 * s, va_list * args)
-{
-  ipsec_gre_tunnel_t *t = va_arg (*args, ipsec_gre_tunnel_t *);
-  ipsec_gre_main_t *gm = &ipsec_gre_main;
-
-  s = format (s,
-	      "[%d] %U (src) %U (dst) local-sa %d remote-sa %d",
-	      t - gm->tunnels,
-	      format_ip4_address, &t->tunnel_src,
-	      format_ip4_address, &t->tunnel_dst,
-	      t->local_sa_id, t->remote_sa_id);
-  return s;
-}
-
-static clib_error_t *
-show_ipsec_gre_tunnel_command_fn (vlib_main_t * vm,
-				  unformat_input_t * input,
-				  vlib_cli_command_t * cmd)
-{
-  ipsec_gre_main_t *igm = &ipsec_gre_main;
-  ipsec_gre_tunnel_t *t;
-
-  if (pool_elts (igm->tunnels) == 0)
-    vlib_cli_output (vm, "No IPSec GRE tunnels configured...");
-
-  /* *INDENT-OFF* */
-  pool_foreach (t, igm->tunnels,
-  ({
-    vlib_cli_output (vm, "%U", format_ipsec_gre_tunnel, t);
-  }));
-  /* *INDENT-ON* */
-
-  return 0;
-}
-
-/* *INDENT-OFF* */
-VLIB_CLI_COMMAND (show_ipsec_gre_tunnel_command, static) = {
-    .path = "show ipsec gre tunnel",
-    .function = show_ipsec_gre_tunnel_command_fn,
-};
-/* *INDENT-ON* */
-
-/* force inclusion from application's main.c */
-clib_error_t *
-ipsec_gre_interface_init (vlib_main_t * vm)
-{
-  return 0;
-}
-
-VLIB_INIT_FUNCTION (ipsec_gre_interface_init);
-
-/**
- * @brief Add or delete ipsec-gre tunnel interface.
- *
- * @param *a vnet_ipsec_gre_tunnel_add_del_args_t - tunnel interface parameters
- * @param *sw_if_indexp u32 - software interface index
- * @return int - 0 if success otherwise <code>VNET_API_ERROR_</code>
- */
-int
-vnet_ipsec_gre_tunnel_add_del (const ipsec_gre_tunnel_add_del_args_t * a,
-			       u32 * sw_if_indexp)
-{
-  ipsec_gre_main_t *igm = &ipsec_gre_main;
-  vnet_main_t *vnm = igm->vnet_main;
-  ipsec_main_t *im = &ipsec_main;
-  ipsec_gre_tunnel_t *t;
-  vnet_hw_interface_t *hi;
-  u32 hw_if_index, sw_if_index;
-  u32 slot;
-  uword *p;
-  u64 key;
-
-  key = (u64) a->src.as_u32 << 32 | (u64) a->dst.as_u32;
-  p = hash_get (igm->tunnel_by_key, key);
-
-  if (a->is_add)
-    {
-      /* check if same src/dst pair exists */
-      if (p)
-	return VNET_API_ERROR_INVALID_VALUE;
-
-      pool_get_aligned (igm->tunnels, t, CLIB_CACHE_LINE_BYTES);
-      clib_memset (t, 0, sizeof (*t));
-
-      if (vec_len (igm->free_ipsec_gre_tunnel_hw_if_indices) > 0)
-	{
-	  vnet_interface_main_t *im = &vnm->interface_main;
-
-	  hw_if_index = igm->free_ipsec_gre_tunnel_hw_if_indices
-	    [vec_len (igm->free_ipsec_gre_tunnel_hw_if_indices) - 1];
-	  _vec_len (igm->free_ipsec_gre_tunnel_hw_if_indices) -= 1;
-
-	  hi = vnet_get_hw_interface (vnm, hw_if_index);
-	  hi->dev_instance = t - igm->tunnels;
-	  hi->hw_instance = hi->dev_instance;
-
-	  /* clear old stats of freed tunnel before reuse */
-	  sw_if_index = hi->sw_if_index;
-	  vnet_interface_counter_lock (im);
-	  vlib_zero_combined_counter
-	    (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_TX],
-	     sw_if_index);
-	  vlib_zero_combined_counter
-	    (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_RX],
-	     sw_if_index);
-	  vlib_zero_simple_counter
-	    (&im->sw_if_counters[VNET_INTERFACE_COUNTER_DROP], sw_if_index);
-	  vnet_interface_counter_unlock (im);
-	}
-      else
-	{
-	  hw_if_index = vnet_register_interface
-	    (vnm, ipsec_gre_device_class.index, t - igm->tunnels,
-	     ipsec_gre_hw_interface_class.index, t - igm->tunnels);
-	  hi = vnet_get_hw_interface (vnm, hw_if_index);
-	  sw_if_index = hi->sw_if_index;
-	}
-
-      t->hw_if_index = hw_if_index;
-      t->sw_if_index = sw_if_index;
-      t->local_sa_id = a->local_sa_id;
-      t->remote_sa_id = a->remote_sa_id;
-      t->local_sa = ipsec_get_sa_index_by_sa_id (t->local_sa_id);
-      t->remote_sa = ipsec_get_sa_index_by_sa_id (t->remote_sa_id);
-
-      ip4_sw_interface_enable_disable (sw_if_index, 1);
-
-      vec_validate_init_empty (igm->tunnel_index_by_sw_if_index,
-			       sw_if_index, ~0);
-      igm->tunnel_index_by_sw_if_index[sw_if_index] = t - igm->tunnels;
-
-      hi->min_packet_bytes = 64 + sizeof (gre_header_t) +
-	sizeof (ip4_header_t) + sizeof (esp_header_t) + sizeof (esp_footer_t);
-
-      /* Standard default gre MTU. */
-      /* TODO: Should take tunnel overhead into consideration */
-      vnet_sw_interface_set_mtu (vnm, sw_if_index, 9000);
-
-      clib_memcpy (&t->tunnel_src, &a->src, sizeof (t->tunnel_src));
-      clib_memcpy (&t->tunnel_dst, &a->dst, sizeof (t->tunnel_dst));
-
-      hash_set (igm->tunnel_by_key, key, t - igm->tunnels);
-
-      slot = vlib_node_add_next_with_slot
-	(vnm->vlib_main, hi->tx_node_index, im->esp4_encrypt_node_index,
-	 IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT);
-
-      ASSERT (slot == IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT);
-    }
-  else
-    {				/* !is_add => delete */
-      /* tunnel needs to exist */
-      if (!p)
-	return VNET_API_ERROR_NO_SUCH_ENTRY;
-
-      t = pool_elt_at_index (igm->tunnels, p[0]);
-
-      sw_if_index = t->sw_if_index;
-      ip4_sw_interface_enable_disable (sw_if_index, 0);
-      vnet_sw_interface_set_flags (vnm, sw_if_index, 0 /* down */ );
-      /* make sure tunnel is removed from l2 bd or xconnect */
-      set_int_l2_mode (igm->vlib_main, vnm, MODE_L3, sw_if_index, 0,
-		       L2_BD_PORT_TYPE_NORMAL, 0, 0);
-      vec_add1 (igm->free_ipsec_gre_tunnel_hw_if_indices, t->hw_if_index);
-      igm->tunnel_index_by_sw_if_index[sw_if_index] = ~0;
-
-      hash_unset (igm->tunnel_by_key, key);
-      pool_put (igm->tunnels, t);
-    }
-
-  if (sw_if_indexp)
-    *sw_if_indexp = sw_if_index;
-
-  return ipsec_add_del_ipsec_gre_tunnel (vnm, a);
-}
-
-static clib_error_t *
-create_ipsec_gre_tunnel_command_fn (vlib_main_t * vm,
-				    unformat_input_t * input,
-				    vlib_cli_command_t * cmd)
-{
-  unformat_input_t _line_input, *line_input = &_line_input;
-  u32 num_m_args = 0;
-  ipsec_gre_tunnel_add_del_args_t _a, *a = &_a;
-  int rv;
-  u32 sw_if_index;
-  clib_error_t *error = NULL;
-
-  clib_memset (a, 0, sizeof (*a));
-  a->is_add = 1;
-
-  /* Get a line of input. */
-  if (!unformat_user (input, unformat_line_input, line_input))
-    return 0;
-
-  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
-    {
-      if (unformat (line_input, "del"))
-	a->is_add = 0;
-      else if (unformat (line_input, "src %U", unformat_ip4_address, &a->src))
-	num_m_args++;
-      else if (unformat (line_input, "dst %U", unformat_ip4_address, &a->dst))
-	num_m_args++;
-      else if (unformat (line_input, "local-sa %d", &a->local_sa_id))
-	num_m_args++;
-      else if (unformat (line_input, "remote-sa %d", &a->remote_sa_id))
-	num_m_args++;
-      else
-	{
-	  error = clib_error_return (0, "unknown input `%U'",
-				     format_unformat_error, line_input);
-	  goto done;
-	}
-    }
-
-  if (num_m_args < 4)
-    {
-      error = clib_error_return (0, "mandatory argument(s) missing");
-      goto done;
-    }
-
-  if (memcmp (&a->src, &a->dst, sizeof (a->src)) == 0)
-    {
-      error = clib_error_return (0, "src and dst are identical");
-      goto done;
-    }
-
-  rv = vnet_ipsec_gre_tunnel_add_del (a, &sw_if_index);
-
-  switch (rv)
-    {
-    case 0:
-      vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name,
-		       vnet_get_main (), sw_if_index);
-      break;
-    case VNET_API_ERROR_INVALID_VALUE:
-      error = clib_error_return (0, "GRE tunnel already exists...");
-      goto done;
-    default:
-      error = clib_error_return (0,
-				 "vnet_ipsec_gre_tunnel_add_del returned %d",
-				 rv);
-      goto done;
-    }
-
-done:
-  unformat_free (line_input);
-
-  return error;
-}
-
-/* *INDENT-OFF* */
-VLIB_CLI_COMMAND (create_ipsec_gre_tunnel_command, static) = {
-  .path = "create ipsec gre tunnel",
-  .short_help = "create ipsec gre tunnel src <addr> dst <addr> "
-                "local-sa <id> remote-sa <id> [del]",
-  .function = create_ipsec_gre_tunnel_command_fn,
-};
-/* *INDENT-ON* */
-
-/*
-* fd.io coding-style-patch-verification: ON
-*
-* Local Variables:
-* eval: (c-set-style "gnu")
-* End:
-*/
diff --git a/src/vnet/ipsec-gre/ipsec_gre.api b/src/vnet/ipsec-gre/ipsec_gre.api
deleted file mode 100644
index b495009..0000000
--- a/src/vnet/ipsec-gre/ipsec_gre.api
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (c) 2015-2016 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-option version = "1.1.0";
-
-import "vnet/ip/ip_types.api";
-
-/** \brief Add / del ipsec gre tunnel request
-    @param client_index - opaque cookie to identify the sender
-    @param context - sender context, to match reply w/ request
-    @param local_sa_id - local/output SA id
-    @param remote_sa_id - remote/input SA id
-    @param is_add - 1 if adding the tunnel, 0 if deleting
-    @param sw_if_index - software index of the ipsec gre tunnel
-                         ignored on create. set in dump/details
-    @param src - tunnel source address
-    @param dst - tunnel destination address
-*/
-typedef ipsec_gre_tunnel {
-    u32 client_index;
-    u32 context;
-    u32 local_sa_id;
-    u32 remote_sa_id;
-    u8 is_add;
-    u32 sw_if_index;
-    vl_api_ip4_address_t src;
-    vl_api_ip4_address_t dst;
-};
-
-define ipsec_gre_tunnel_add_del {
-    u32 client_index;
-    u32 context;
-    u8 is_add;
-    vl_api_ipsec_gre_tunnel_t tunnel;
-};
-
-/** \brief Reply for add / del ipsec gre tunnel request
-    @param context - returned sender context, to match reply w/ request
-    @param retval - return code
-    @param sw_if_index - software index of the new ipsec gre tunnel
-*/
-define ipsec_gre_tunnel_add_del_reply {
-    u32 context;
-    i32 retval;
-    u32 sw_if_index;
-};
-
-/** \brief Dump ipsec gre tunnel table
-    @param client_index - opaque cookie to identify the sender
-    @param context - sender context, to match reply w/ request
-    @param tunnel_index - gre tunnel identifier or -1 in case of all tunnels
-*/
-define ipsec_gre_tunnel_dump {
-    u32 client_index;
-    u32 context;
-    u32 sw_if_index;
-};
-
-/** \brief ipsec gre tunnel operational state response
-    @param context - returned sender context, to match reply w/ request
-    @param local_sa_id - local SA id
-    @param remote_sa_id - remote SA id
-    @param src_address - tunnel source address
-    @param dst_address - tunnel destination address
-*/
-define ipsec_gre_tunnel_details {
-    u32 context;
-    vl_api_ipsec_gre_tunnel_t tunnel;
-};
-
-/*
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
- 
diff --git a/src/vnet/ipsec-gre/ipsec_gre.c b/src/vnet/ipsec-gre/ipsec_gre.c
deleted file mode 100644
index cdb23dd..0000000
--- a/src/vnet/ipsec-gre/ipsec_gre.c
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/**
- * @file
- * @brief L2-GRE over IPSec packet processing.
- *
- * Add GRE header to thr packet and send it to the esp-encrypt node.
-*/
-
-#include <vnet/vnet.h>
-#include <vnet/ipsec-gre/ipsec_gre.h>
-
-extern ipsec_gre_main_t ipsec_gre_main;
-
-#ifndef CLIB_MARCH_VARIANT
-ipsec_gre_main_t ipsec_gre_main;
-#endif /* CLIB_MARCH_VARIANT */
-
-/**
- * @brief IPv4 and GRE header union.
- *
-*/
-typedef struct
-{
-  union
-  {
-    ip4_and_gre_header_t ip4_and_gre;
-    u64 as_u64[3];
-  };
-} ip4_and_gre_union_t;
-
-/**
- * @brief Packet trace.
- *
-*/
-typedef struct
-{
-  u32 tunnel_id; /**< Tunnel-id / index in tunnel vector */
-
-  u32 length; /**< pkt length */
-
-  ip4_address_t src; /**< tunnel src IPv4 address */
-  ip4_address_t dst; /**< tunnel dst IPv4 address */
-
-  u32 sa_id; /**< tunnel IPSec SA id */
-} ipsec_gre_tx_trace_t;
-
-static u8 *
-format_ipsec_gre_tx_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 *);
-  ipsec_gre_tx_trace_t *t = va_arg (*args, ipsec_gre_tx_trace_t *);
-
-  s = format (s, "GRE: tunnel %d len %d src %U dst %U sa-id %d",
-	      t->tunnel_id, clib_net_to_host_u16 (t->length),
-	      format_ip4_address, &t->src.as_u8,
-	      format_ip4_address, &t->dst.as_u8, t->sa_id);
-  return s;
-}
-
-/**
- * @brief IPSec-GRE tunnel interface tx function.
- *
- * Add GRE header to the packet.
- *
- * @param vm vlib_main_t corresponding to the current thread.
- * @param node vlib_node_runtime_t data for this node.
- * @param frame vlib_frame_t whose contents should be dispatched.
- *
- * @par Graph mechanics: buffer metadata, next index usage
- *
- * <em>Uses:</em>
- * - <code>node->runtime_data</code>
- *     - Match tunnel by <code>rd->dev_instance</code> in IPSec-GRE tunnels
- *       pool.
- *
- * <em>Sets:</em>
- * - <code>vnet_buffer(b)->output_features.ipsec_sad_index</code>
- *     - Set IPSec Security Association for packet encryption.
- * - <code>vnet_buffer(b)->sw_if_index[VLIB_TX]</code>
- *     - Reset output sw_if_index.
- *
- * <em>Next Index:</em>
- * - Dispatches the packet to the esp-encrypt node.
-*/
-VNET_DEVICE_CLASS_TX_FN (ipsec_gre_device_class) (vlib_main_t * vm,
-						  vlib_node_runtime_t * node,
-						  vlib_frame_t * frame)
-{
-  ipsec_gre_main_t *igm = &ipsec_gre_main;
-  u32 next_index;
-  u32 *from, *to_next, n_left_from, n_left_to_next;
-  vnet_interface_output_runtime_t *rd = (void *) node->runtime_data;
-  ipsec_gre_tunnel_t *t = pool_elt_at_index (igm->tunnels, rd->dev_instance);
-
-  u16 l2_gre_protocol_ethertype = clib_net_to_host_u16 (GRE_PROTOCOL_teb);
-
-  /* Vector of buffer / pkt indices we're supposed to process */
-  from = vlib_frame_vector_args (frame);
-
-  /* Number of buffers / pkts */
-  n_left_from = frame->n_vectors;
-
-  /* Speculatively send the first buffer to the last disposition we used */
-  next_index = node->cached_next_index;
-
-  while (n_left_from > 0)
-    {
-      /* set up to enqueue to our disposition with index = next_index */
-      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
-
-      /*
-       * As long as we have enough pkts left to process two pkts
-       * and prefetch two pkts...
-       */
-      while (n_left_from >= 4 && n_left_to_next >= 2)
-	{
-	  vlib_buffer_t *b0, *b1;
-	  ip4_header_t *ip0, *ip1;
-	  ip4_and_gre_union_t *h0, *h1;
-	  u32 bi0, next0, bi1, next1;
-	  __attribute__ ((unused)) u8 error0, error1;
-
-	  /* Prefetch the next iteration */
-	  {
-	    vlib_buffer_t *p2, *p3;
-
-	    p2 = vlib_get_buffer (vm, from[2]);
-	    p3 = vlib_get_buffer (vm, from[3]);
-
-	    vlib_prefetch_buffer_header (p2, LOAD);
-	    vlib_prefetch_buffer_header (p3, LOAD);
-
-	    /*
-	     * Prefetch packet data. We expect to overwrite
-	     * the inbound L2 header with an ip header and a
-	     * gre header. Might want to prefetch the last line
-	     * of rewrite space as well; need profile data
-	     */
-	    CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
-	    CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
-	  }
-
-	  /* Pick up the next two buffer indices */
-	  bi0 = from[0];
-	  bi1 = from[1];
-
-	  /* Speculatively enqueue them where we sent the last buffer */
-	  to_next[0] = bi0;
-	  to_next[1] = bi1;
-	  from += 2;
-	  to_next += 2;
-	  n_left_to_next -= 2;
-	  n_left_from -= 2;
-
-	  b0 = vlib_get_buffer (vm, bi0);
-	  b1 = vlib_get_buffer (vm, bi1);
-
-	  vlib_buffer_advance (b0, -sizeof (*h0));
-	  vlib_buffer_advance (b1, -sizeof (*h1));
-
-	  h0 = vlib_buffer_get_current (b0);
-	  h1 = vlib_buffer_get_current (b1);
-	  h0->as_u64[0] = 0;
-	  h0->as_u64[1] = 0;
-	  h0->as_u64[2] = 0;
-
-	  h1->as_u64[0] = 0;
-	  h1->as_u64[1] = 0;
-	  h1->as_u64[2] = 0;
-
-	  ip0 = &h0->ip4_and_gre.ip4;
-	  h0->ip4_and_gre.gre.protocol = l2_gre_protocol_ethertype;
-	  ip0->ip_version_and_header_length = 0x45;
-	  ip0->ttl = 254;
-	  ip0->protocol = IP_PROTOCOL_GRE;
-
-	  ip1 = &h1->ip4_and_gre.ip4;
-	  h1->ip4_and_gre.gre.protocol = l2_gre_protocol_ethertype;
-	  ip1->ip_version_and_header_length = 0x45;
-	  ip1->ttl = 254;
-	  ip1->protocol = IP_PROTOCOL_GRE;
-
-	  ip0->length =
-	    clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
-	  ip1->length =
-	    clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1));
-	  ip0->src_address.as_u32 = t->tunnel_src.as_u32;
-	  ip1->src_address.as_u32 = t->tunnel_src.as_u32;
-	  ip0->dst_address.as_u32 = t->tunnel_dst.as_u32;
-	  ip1->dst_address.as_u32 = t->tunnel_dst.as_u32;
-	  ip0->checksum = ip4_header_checksum (ip0);
-	  ip1->checksum = ip4_header_checksum (ip1);
-
-	  vnet_buffer (b0)->sw_if_index[VLIB_RX] =
-	    vnet_buffer (b0)->sw_if_index[VLIB_TX];
-	  vnet_buffer (b1)->sw_if_index[VLIB_RX] =
-	    vnet_buffer (b1)->sw_if_index[VLIB_TX];
-
-	  vnet_buffer (b0)->ipsec.sad_index = t->local_sa;
-	  vnet_buffer (b1)->ipsec.sad_index = t->local_sa;
-
-	  vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
-	  vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0;
-
-	  next0 = IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT;
-	  next1 = IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT;
-	  error0 = IPSEC_GRE_ERROR_NONE;
-	  error1 = IPSEC_GRE_ERROR_NONE;
-
-	  if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
-	    {
-	      ipsec_gre_tx_trace_t *tr = vlib_add_trace (vm, node,
-							 b0, sizeof (*tr));
-	      tr->tunnel_id = t - igm->tunnels;
-	      tr->length = ip0->length;
-	      tr->src.as_u32 = ip0->src_address.as_u32;
-	      tr->dst.as_u32 = ip0->dst_address.as_u32;
-	      tr->sa_id = t->local_sa_id;
-	    }
-
-	  if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
-	    {
-	      ipsec_gre_tx_trace_t *tr = vlib_add_trace (vm, node,
-							 b1, sizeof (*tr));
-	      tr->tunnel_id = t - igm->tunnels;
-	      tr->length = ip1->length;
-	      tr->src.as_u32 = ip1->src_address.as_u32;
-	      tr->dst.as_u32 = ip1->dst_address.as_u32;
-	      tr->sa_id = t->local_sa_id;
-	    }
-
-	  vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
-					   to_next, n_left_to_next,
-					   bi0, bi1, next0, next1);
-	}
-
-      while (n_left_from > 0 && n_left_to_next > 0)
-	{
-	  vlib_buffer_t *b0;
-	  ip4_header_t *ip0;
-	  ip4_and_gre_union_t *h0;
-	  u32 bi0, next0;
-	  __attribute__ ((unused)) u8 error0;
-
-	  bi0 = to_next[0] = from[0];
-	  from += 1;
-	  n_left_from -= 1;
-	  to_next += 1;
-	  n_left_to_next -= 1;
-
-	  b0 = vlib_get_buffer (vm, bi0);
-
-	  vlib_buffer_advance (b0, -sizeof (*h0));
-
-	  h0 = vlib_buffer_get_current (b0);
-	  h0->as_u64[0] = 0;
-	  h0->as_u64[1] = 0;
-	  h0->as_u64[2] = 0;
-
-	  ip0 = &h0->ip4_and_gre.ip4;
-	  h0->ip4_and_gre.gre.protocol = l2_gre_protocol_ethertype;
-	  ip0->ip_version_and_header_length = 0x45;
-	  ip0->ttl = 254;
-	  ip0->protocol = IP_PROTOCOL_GRE;
-	  ip0->length =
-	    clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
-	  ip0->src_address.as_u32 = t->tunnel_src.as_u32;
-	  ip0->dst_address.as_u32 = t->tunnel_dst.as_u32;
-	  ip0->checksum = ip4_header_checksum (ip0);
-
-	  vnet_buffer (b0)->sw_if_index[VLIB_RX] =
-	    vnet_buffer (b0)->sw_if_index[VLIB_TX];
-	  vnet_buffer (b0)->ipsec.sad_index = t->local_sa;
-	  vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
-
-	  next0 = IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT;
-	  error0 = IPSEC_GRE_ERROR_NONE;
-
-	  if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
-	    {
-	      ipsec_gre_tx_trace_t *tr = vlib_add_trace (vm, node,
-							 b0, sizeof (*tr));
-	      tr->tunnel_id = t - igm->tunnels;
-	      tr->length = ip0->length;
-	      tr->src.as_u32 = ip0->src_address.as_u32;
-	      tr->dst.as_u32 = ip0->dst_address.as_u32;
-	      tr->sa_id = t->local_sa_id;
-	    }
-
-	  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, ipsec_gre_input_node.index,
-			       IPSEC_GRE_ERROR_PKTS_ENCAP, frame->n_vectors);
-
-  return frame->n_vectors;
-}
-
-static clib_error_t *
-ipsec_gre_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index,
-				   u32 flags)
-{
-  if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
-    vnet_hw_interface_set_flags (vnm, hw_if_index,
-				 VNET_HW_INTERFACE_FLAG_LINK_UP);
-  else
-    vnet_hw_interface_set_flags (vnm, hw_if_index, 0 /* down */ );
-
-  return /* no error */ 0;
-}
-
-static u8 *
-format_ipsec_gre_tunnel_name (u8 * s, va_list * args)
-{
-  u32 dev_instance = va_arg (*args, u32);
-  return format (s, "ipsec-gre%d", dev_instance);
-}
-
-static u8 *
-format_ipsec_gre_device (u8 * s, va_list * args)
-{
-  u32 dev_instance = va_arg (*args, u32);
-  CLIB_UNUSED (int verbose) = va_arg (*args, int);
-
-  s = format (s, "IPSEC-GRE tunnel: id %d\n", dev_instance);
-  return s;
-}
-
-/* *INDENT-OFF* */
-VNET_DEVICE_CLASS (ipsec_gre_device_class) = {
-  .name = "IPSec GRE tunnel device",
-  .format_device_name = format_ipsec_gre_tunnel_name,
-  .format_device = format_ipsec_gre_device,
-  .format_tx_trace = format_ipsec_gre_tx_trace,
-  .admin_up_down_function = ipsec_gre_interface_admin_up_down,
-};
-
-
-#ifndef CLIB_MARCH_VARIANT
-VNET_HW_INTERFACE_CLASS (ipsec_gre_hw_interface_class) = {
-  .name = "IPSEC-GRE",
-};
-#endif /* CLIB_MARCH_VARIANT */
-/* *INDENT-ON* */
-
-static clib_error_t *
-ipsec_gre_init (vlib_main_t * vm)
-{
-  ipsec_gre_main_t *igm = &ipsec_gre_main;
-  clib_error_t *error;
-
-  clib_memset (igm, 0, sizeof (igm[0]));
-  igm->vlib_main = vm;
-  igm->vnet_main = vnet_get_main ();
-
-  if ((error = vlib_call_init_function (vm, ip_main_init)))
-    return error;
-
-  if ((error = vlib_call_init_function (vm, ip4_lookup_init)))
-    return error;
-
-  igm->tunnel_by_key = hash_create (0, sizeof (uword));
-
-  return vlib_call_init_function (vm, ipsec_gre_input_init);
-}
-
-VLIB_INIT_FUNCTION (ipsec_gre_init);
-
-/*
-* fd.io coding-style-patch-verification: ON
-*
-* Local Variables:
-* eval: (c-set-style "gnu")
-* End:
-*/
diff --git a/src/vnet/ipsec-gre/ipsec_gre.h b/src/vnet/ipsec-gre/ipsec_gre.h
deleted file mode 100644
index 730cd71..0000000
--- a/src/vnet/ipsec-gre/ipsec_gre.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/**
- * @file
- * @brief L2-GRE over IPSec packet processing.
-*/
-
-#ifndef included_ipsec_gre_h
-#define included_ipsec_gre_h
-
-#include <vnet/vnet.h>
-#include <vnet/gre/packet.h>
-#include <vnet/gre/gre.h>
-#include <vnet/ip/ip.h>
-#include <vnet/ip/ip4.h>
-#include <vnet/ip/ip4_packet.h>
-#include <vnet/pg/pg.h>
-#include <vnet/ip/format.h>
-#include <vnet/ipsec/ipsec.h>
-#include <vnet/ipsec/ipsec_if.h>
-
-extern vnet_hw_interface_class_t ipsec_gre_hw_interface_class;
-
-/**
- * @brief IPSec-GRE errors.
- *
-*/
-typedef enum
-{
-#define ipsec_gre_error(n,s) IPSEC_GRE_ERROR_##n,
-#include <vnet/ipsec-gre/error.def>
-#undef ipsec_gre_error
-  IPSEC_GRE_N_ERROR,
-} ipsec_gre_error_t;
-
-/**
- * @brief IPSec-GRE tunnel parameters.
- *
-*/
-typedef struct
-{
-  /* Required for pool_get_aligned */
-  CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
-  ip4_address_t tunnel_src; /**< tunnel IPv4 src address */
-  ip4_address_t tunnel_dst; /**< tunnel IPv4 dst address */
-  u32 local_sa;		    /**< local IPSec SA index */
-  u32 remote_sa;	    /**< remote IPSec SA index */
-  u32 local_sa_id;	    /**< local IPSec SA id */
-  u32 remote_sa_id;	    /**< remote IPSec SA id */
-  u32 hw_if_index;;	    /**< hardware interface index */
-  u32 sw_if_index;;	    /**< software interface index */
-} ipsec_gre_tunnel_t;
-
-/**
- * @brief IPSec-GRE state.
- *
-*/
-typedef struct
-{
-  ipsec_gre_tunnel_t *tunnels; /**< pool of tunnel instances */
-
-  uword *tunnel_by_key;	 /**< hash mapping src/dst addr pair to tunnel */
-
-  u32 *free_ipsec_gre_tunnel_hw_if_indices;  /**< free vlib hw_if_indices */
-
-  u32 *tunnel_index_by_sw_if_index;  /**< mapping from sw_if_index to tunnel
-                                          index */
-
-  vlib_main_t *vlib_main;  /**< convenience */
-  vnet_main_t *vnet_main;  /**< convenience */
-} ipsec_gre_main_t;
-
-extern ipsec_gre_main_t ipsec_gre_main;
-
-extern vlib_node_registration_t ipsec_gre_input_node;
-extern vnet_device_class_t ipsec_gre_device_class;
-
-/* manually added to the interface output node in ipsec_gre.c */
-#define IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT 0
-
-extern int vnet_ipsec_gre_tunnel_add_del (const
-					  ipsec_gre_tunnel_add_del_args_t * a,
-					  u32 * sw_if_indexp);
-
-#endif /* included_ipsec_gre_h */
-
-/*
-* fd.io coding-style-patch-verification: ON
-*
-* Local Variables:
-* eval: (c-set-style "gnu")
-* End:
-*/
diff --git a/src/vnet/ipsec-gre/ipsec_gre_api.c b/src/vnet/ipsec-gre/ipsec_gre_api.c
deleted file mode 100644
index 9e5d615..0000000
--- a/src/vnet/ipsec-gre/ipsec_gre_api.c
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- *------------------------------------------------------------------
- * ipsec_gre_api.c - ipsec_gre api
- *
- * Copyright (c) 2016 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *------------------------------------------------------------------
- */
-
-#include <vnet/vnet.h>
-#include <vlibmemory/api.h>
-
-#include <vnet/interface.h>
-#include <vnet/api_errno.h>
-#include <vnet/ipsec-gre/ipsec_gre.h>
-#include <vnet/ip/ip_types_api.h>
-
-#include <vnet/vnet_msg_enum.h>
-
-#define vl_typedefs		/* define message structures */
-#include <vnet/vnet_all_api_h.h>
-#undef vl_typedefs
-
-#define vl_endianfun		/* define message structures */
-#include <vnet/vnet_all_api_h.h>
-#undef vl_endianfun
-
-/* instantiate all the print functions we know about */
-#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
-#define vl_printfun
-#include <vnet/vnet_all_api_h.h>
-#undef vl_printfun
-
-#include <vlibapi/api_helper_macros.h>
-
-#define foreach_vpe_api_msg                             \
-_(IPSEC_GRE_TUNNEL_ADD_DEL, ipsec_gre_tunnel_add_del)                   \
-_(IPSEC_GRE_TUNNEL_DUMP, ipsec_gre_tunnel_dump)
-
-static void
-vl_api_ipsec_gre_tunnel_add_del_t_handler (vl_api_ipsec_gre_tunnel_add_del_t *
-					   mp)
-{
-  vl_api_ipsec_gre_tunnel_add_del_reply_t *rmp;
-  int rv = 0;
-  ipsec_gre_tunnel_add_del_args_t _a, *a = &_a;
-  u32 sw_if_index = ~0;
-
-  clib_memset (a, 0, sizeof (*a));
-
-  ip4_address_decode (mp->tunnel.src, &a->src);
-  ip4_address_decode (mp->tunnel.dst, &a->dst);
-
-  /* Check src & dst are different */
-  if (a->src.as_u32 == a->dst.as_u32)
-    {
-      rv = VNET_API_ERROR_SAME_SRC_DST;
-      goto out;
-    }
-
-  a->is_add = mp->is_add;
-  a->local_sa_id = ntohl (mp->tunnel.local_sa_id);
-  a->remote_sa_id = ntohl (mp->tunnel.remote_sa_id);
-
-  rv = vnet_ipsec_gre_tunnel_add_del (a, &sw_if_index);
-
-out:
-  /* *INDENT-OFF* */
-  REPLY_MACRO2(VL_API_IPSEC_GRE_TUNNEL_ADD_DEL_REPLY,
-  ({
-    rmp->sw_if_index = ntohl (sw_if_index);
-  }));
-  /* *INDENT-ON* */
-}
-
-static void send_ipsec_gre_tunnel_details
-  (ipsec_gre_tunnel_t * t, vl_api_registration_t * reg, u32 context)
-{
-  vl_api_ipsec_gre_tunnel_details_t *rmp;
-
-  rmp = vl_msg_api_alloc (sizeof (*rmp));
-  clib_memset (rmp, 0, sizeof (*rmp));
-  rmp->_vl_msg_id = ntohs (VL_API_IPSEC_GRE_TUNNEL_DETAILS);
-
-  ip4_address_encode (&t->tunnel_src, rmp->tunnel.src);
-  ip4_address_encode (&t->tunnel_dst, rmp->tunnel.dst);
-  rmp->tunnel.sw_if_index = htonl (t->sw_if_index);
-  rmp->tunnel.local_sa_id = htonl (t->local_sa_id);
-  rmp->tunnel.remote_sa_id = htonl (t->remote_sa_id);
-  rmp->context = context;
-
-  vl_api_send_msg (reg, (u8 *) rmp);
-}
-
-static void vl_api_ipsec_gre_tunnel_dump_t_handler
-  (vl_api_ipsec_gre_tunnel_dump_t * mp)
-{
-  vl_api_registration_t *reg;
-  ipsec_gre_main_t *igm = &ipsec_gre_main;
-  ipsec_gre_tunnel_t *t;
-  u32 sw_if_index;
-
-  reg = vl_api_client_index_to_registration (mp->client_index);
-  if (!reg)
-    return;
-
-  sw_if_index = ntohl (mp->sw_if_index);
-
-  if (~0 == sw_if_index)
-    {
-        /* *INDENT-OFF* */
-        pool_foreach (t, igm->tunnels,
-        ({
-            send_ipsec_gre_tunnel_details(t, reg, mp->context);
-        }));
-        /* *INDENT-ON* */
-    }
-  else
-    {
-      if ((sw_if_index >= vec_len (igm->tunnel_index_by_sw_if_index)) ||
-	  (~0 == igm->tunnel_index_by_sw_if_index[sw_if_index]))
-	{
-	  return;
-	}
-      t = &igm->tunnels[igm->tunnel_index_by_sw_if_index[sw_if_index]];
-      send_ipsec_gre_tunnel_details (t, reg, mp->context);
-    }
-}
-
-/*
- * ipsec_gre_api_hookup
- * Add vpe's API message handlers to the table.
- * vlib has already mapped shared memory and
- * added the client registration handlers.
- * See .../vlib-api/vlibmemory/memclnt_vlib.c:memclnt_process()
- */
-#define vl_msg_name_crc_list
-#include <vnet/vnet_all_api_h.h>
-#undef vl_msg_name_crc_list
-
-static void
-setup_message_id_table (api_main_t * am)
-{
-#define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id);
-  foreach_vl_msg_name_crc_ipsec_gre;
-#undef _
-}
-
-static clib_error_t *
-ipsec_gre_api_hookup (vlib_main_t * vm)
-{
-  api_main_t *am = &api_main;
-
-#define _(N,n)                                                  \
-    vl_msg_api_set_handlers(VL_API_##N, #n,                     \
-                           vl_api_##n##_t_handler,              \
-                           vl_noop_handler,                     \
-                           vl_api_##n##_t_endian,               \
-                           vl_api_##n##_t_print,                \
-                           sizeof(vl_api_##n##_t), 1);
-  foreach_vpe_api_msg;
-#undef _
-
-  /*
-   * Set up the (msg_name, crc, message-id) table
-   */
-  setup_message_id_table (am);
-
-  return 0;
-}
-
-VLIB_API_INIT_FUNCTION (ipsec_gre_api_hookup);
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
diff --git a/src/vnet/ipsec-gre/ipsec_gre_doc.md b/src/vnet/ipsec-gre/ipsec_gre_doc.md
deleted file mode 100644
index e1bb9cd..0000000
--- a/src/vnet/ipsec-gre/ipsec_gre_doc.md
+++ /dev/null
@@ -1,74 +0,0 @@
-# VPP L2-GRE over IPsec implementation    {#ipsec_gre_doc}
-
-This is a memo intended to contain documentation of the VPP L2-GRE over IPsec implementation.
-Everything that is not directly obvious should come here.
-
-
-## L2-GRE over IPsec
-GRE encapsulate layer 2 traffic and IPSec encrypt what is encapsulated by GRE. The whole point of L2-GRE over IPSec is to tunnel layer 2 over GRE and IPSec by bridging the physical interface with IPSec-GRE tunnel interface.
-
-There are 2 dedicated nodes for encapsulation:
-* ipsec-gre<n>-tx - add GRE header
-* esp-encrypt - encrypt GRE packet to ESP packet
-
-There are 3 dedicated nodes for decapsulation:
-* ipsec-if-input - match IPSec SA by source IP address and SPI in ESP packet
-* esp-decrypt - decrypt ESP packet
-* ipsec-gre-input - remove GRE header
-
-
-### Configuration
-
-L2-GRE over IPsec support the following CLI configuration command:
-    create ipsec gre tunnel src <addr> dst <addr> local-sa <id> remote-sa <id> [del]
-
-src: tunnel source IPv4 address
-dst: tunnel destination IPv4 address
-local-sa: tunnel local IPSec Security Association
-remote-sa: tunnel remote IPSec Security Association
-del: delete IPSec-GRE tunnel
-
-L2-GRE over IPsec support the following API configuration command:
-    ipsec_gre_add_del_tunnel src <addr> dst <addr> local_sa <sa-id> remote_sa <sa-id> [del]
-
-src: tunnel source IPv4 address
-dst: tunnel destination IPv4 address
-local_sa: tunnel local IPSec Security Association
-remote_sa: tunnel remote IPSec Security Association
-del: delete IPSec-GRE tunnel
-
-
-### Configuration example
-
-Interface GigabitEthernet0/9/0 is in bridge with ipsec-gre0 tunnel interface, interface GigabitEthernet0/8/0 sending encapsulated and encrypted traffic.
-
-Configure IPv4 address on sending interface:
-set int ip address GigabitEthernet0/8/0 192.168.1.1/24
-
-Configure IPSec Security Associations:
-ipsec sa add 10 spi 1001 esp crypto-key 4a506a794f574265564551694d653768 crypto-alg aes-cbc-128 integ-key 4339314b55523947594d6d3547666b45764e6a58 integ-alg sha1-96
-ipsec sa add 20 spi 1000 esp crypto-key 49517065716d6235726c734a4372466c crypto-alg aes-cbc-128 integ-key 307439636a5542735133595835546f68534e4f64 integ-alg sha1-96
-
-Create IPSec-GRE tunnel:
-create ipsec gre tunnel src 192.168.1.1 dst 192.168.1.2 local-sa 10 remote-sa 20
-
-Set interfaces state:
-set int state GigabitEthernet0/8/0 up
-set int state GigabitEthernet0/9/0 up
-set int state ipsec-gre0 up
-
-Bridge physical interface with IPSec-GRE tunnel interface:
-set interface l2 bridge GigabitEthernet0/9/0 1
-set interface l2 bridge ipsec-gre0 1
-
-
-### Operational data
-
-L2-GRE over IPsec support the following CLI show command:
-    show ipsec gre tunnel
-
-L2-GRE over IPsec support the following API dump command:
-    ipsec_gre_tunnel_dump [sw_if_index <nn>]
-
-sw_if_index: software interface index of the IPSec-GRE tunnel interface
-
diff --git a/src/vnet/ipsec-gre/node.c b/src/vnet/ipsec-gre/node.c
deleted file mode 100644
index 6a3aaa1..0000000
--- a/src/vnet/ipsec-gre/node.c
+++ /dev/null
@@ -1,426 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/**
- * @file
- * @brief L2-GRE over IPSec packet processing.
- *
- * Removes GRE header from the packet and sends it to the l2-input node.
-*/
-
-#include <vlib/vlib.h>
-#include <vnet/pg/pg.h>
-#include <vnet/ipsec-gre/ipsec_gre.h>
-#include <vppinfra/sparse_vec.h>
-
-#define foreach_ipsec_gre_input_next		\
-_(PUNT, "error-punt")                           \
-_(DROP, "error-drop")                           \
-_(L2_INPUT, "l2-input")
-
-typedef enum {
-#define _(s,n) IPSEC_GRE_INPUT_NEXT_##s,
-  foreach_ipsec_gre_input_next
-#undef _
-  IPSEC_GRE_INPUT_N_NEXT,
-} ipsec_gre_input_next_t;
-
-typedef struct {
-  u32 tunnel_id;
-  u32 length;
-  ip4_address_t src;
-  ip4_address_t dst;
-} ipsec_gre_rx_trace_t;
-
-static u8 * format_ipsec_gre_rx_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 *);
-  ipsec_gre_rx_trace_t * t = va_arg (*args, ipsec_gre_rx_trace_t *);
-
-  s = format (s, "GRE: tunnel %d len %d src %U dst %U",
-              t->tunnel_id, clib_net_to_host_u16(t->length),
-              format_ip4_address, &t->src.as_u8,
-              format_ip4_address, &t->dst.as_u8);
-  return s;
-}
-
-/**
- * @brief L2-GRE over IPSec input node.
- * @node ipsec-gre-input
- *
- * This node remove GRE header.
- *
- * @param vm         vlib_main_t corresponding to the current thread.
- * @param node       vlib_node_runtime_t data for this node.
- * @param from_frame vlib_frame_t whose contents should be dispatched.
- *
- * @par Graph mechanics: buffer metadata, next index usage
- *
- * <em>Uses:</em>
- * - <code>ip->src_address</code> and <code>ip->dst_address</code>
- *     - Match tunnel by source and destination addresses in GRE IP header.
- *
- * <em>Sets:</em>
- * - <code>vnet_buffer(b)->gre.src</code>
- *     - Save tunnel source IPv4 address.
- * - <code>vnet_buffer(b)->gre.dst</code>
- *     - Save tunnel destination IPv4 address.
- * - <code>vnet_buffer(b)->sw_if_index[VLIB_RX]</code>
- *     - Set input sw_if_index to IPSec-GRE tunnel for learning.
- *
- * <em>Next Index:</em>
- * - Dispatches the packet to the l2-input node.
-*/
-VLIB_NODE_FN (ipsec_gre_input_node) (vlib_main_t * vm,
-                 vlib_node_runtime_t * node,
-                 vlib_frame_t * from_frame)
-{
-  ipsec_gre_main_t * igm = &ipsec_gre_main;
-  u32 n_left_from, next_index, * from, * to_next;
-  u64 cached_tunnel_key = (u64) ~0;
-  u32 cached_tunnel_sw_if_index = 0, tunnel_sw_if_index;
-  u32 tun_src0, tun_dst0;
-  u32 tun_src1, tun_dst1;
-
-  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 >= 4 && n_left_to_next >= 2)
-	{
-	  u32 bi0, bi1;
-	  vlib_buffer_t * b0, * b1;
-	  gre_header_t * h0, * h1;
-          u16 version0, version1, protocol0, protocol1;
-          int verr0, verr1;
-	  u32 next0, next1;
-          ip4_header_t *ip0, *ip1;
-
-	  /* Prefetch next iteration. */
-	  {
-	    vlib_buffer_t * p2, * p3;
-
-	    p2 = vlib_get_buffer (vm, from[2]);
-	    p3 = vlib_get_buffer (vm, from[3]);
-
-	    vlib_prefetch_buffer_header (p2, LOAD);
-	    vlib_prefetch_buffer_header (p3, LOAD);
-
-	    CLIB_PREFETCH (p2->data, sizeof (h0[0]), LOAD);
-	    CLIB_PREFETCH (p3->data, sizeof (h1[0]), LOAD);
-	  }
-
-	  bi0 = from[0];
-	  bi1 = from[1];
-	  to_next[0] = bi0;
-	  to_next[1] = bi1;
-	  from += 2;
-	  to_next += 2;
-	  n_left_to_next -= 2;
-	  n_left_from -= 2;
-
-	  b0 = vlib_get_buffer (vm, bi0);
-	  b1 = vlib_get_buffer (vm, bi1);
-
-          /* ip4_local hands us the ip header, not the gre header */
-          ip0 = vlib_buffer_get_current (b0);
-          ip1 = vlib_buffer_get_current (b1);
-
-          /* Save src + dst ip4 address */
-          tun_src0 = ip0->src_address.as_u32;
-          tun_dst0 = ip0->dst_address.as_u32;
-          tun_src1 = ip1->src_address.as_u32;
-          tun_dst1 = ip1->dst_address.as_u32;
-
-          vlib_buffer_advance (b0, sizeof (*ip0));
-          vlib_buffer_advance (b1, sizeof (*ip1));
-
-	  h0 = vlib_buffer_get_current (b0);
-	  h1 = vlib_buffer_get_current (b1);
-
-          protocol0 = clib_net_to_host_u16 (h0->protocol);
-          protocol1 = clib_net_to_host_u16 (h1->protocol);
-          if (PREDICT_TRUE(protocol0 == GRE_PROTOCOL_teb))
-            {
-              next0 = IPSEC_GRE_INPUT_NEXT_L2_INPUT;
-              b0->error = node->errors[IPSEC_GRE_ERROR_NONE];
-            }
-          else
-            {
-              b0->error = node->errors[IPSEC_GRE_ERROR_UNKNOWN_PROTOCOL];
-              next0 = IPSEC_GRE_INPUT_NEXT_DROP;
-            }
-          if (PREDICT_TRUE(protocol1 == GRE_PROTOCOL_teb))
-            {
-              next1 = IPSEC_GRE_INPUT_NEXT_L2_INPUT;
-              b1->error = node->errors[IPSEC_GRE_ERROR_NONE];
-            }
-          else
-            {
-              b1->error = node->errors[IPSEC_GRE_ERROR_UNKNOWN_PROTOCOL];
-              next1 = IPSEC_GRE_INPUT_NEXT_DROP;
-            }
-
-          version0 = clib_net_to_host_u16 (h0->flags_and_version);
-          verr0 =  version0 & GRE_VERSION_MASK;
-          version1 = clib_net_to_host_u16 (h1->flags_and_version);
-          verr1 =  version1 & GRE_VERSION_MASK;
-
-          b0->error = verr0 ? node->errors[IPSEC_GRE_ERROR_UNSUPPORTED_VERSION]
-              : b0->error;
-          next0 = verr0 ? IPSEC_GRE_INPUT_NEXT_DROP : next0;
-          b1->error = verr1 ? node->errors[IPSEC_GRE_ERROR_UNSUPPORTED_VERSION]
-              : b1->error;
-          next1 = verr1 ? IPSEC_GRE_INPUT_NEXT_DROP : next1;
-
-          /* For L2 payload set input sw_if_index to GRE tunnel for learning */
-          if (PREDICT_TRUE(next0 == IPSEC_GRE_INPUT_NEXT_L2_INPUT))
-            {
-              u64 key = ((u64)(tun_dst0) << 32) | (u64)(tun_src0);
-
-              if (cached_tunnel_key != key)
-                {
-                  vnet_hw_interface_t * hi;
-                  ipsec_gre_tunnel_t * t;
-                  uword * p;
-
-                  p = hash_get (igm->tunnel_by_key, key);
-                  if (!p)
-                    {
-                      next0 = IPSEC_GRE_INPUT_NEXT_DROP;
-                      b0->error = node->errors[IPSEC_GRE_ERROR_NO_SUCH_TUNNEL];
-                      goto drop0;
-                    }
-                  t = pool_elt_at_index (igm->tunnels, p[0]);
-                  hi = vnet_get_hw_interface (igm->vnet_main,
-                            t->hw_if_index);
-                  tunnel_sw_if_index = hi->sw_if_index;
-                  cached_tunnel_sw_if_index = tunnel_sw_if_index;
-                }
-              else
-                {
-                  tunnel_sw_if_index = cached_tunnel_sw_if_index;
-                }
-              vnet_buffer(b0)->sw_if_index[VLIB_RX] = tunnel_sw_if_index;
-            }
-
-drop0:
-          /* For L2 payload set input sw_if_index to GRE tunnel for learning */
-          if (PREDICT_TRUE(next1 == IPSEC_GRE_INPUT_NEXT_L2_INPUT))
-            {
-              u64 key = ((u64)(tun_dst1) << 32) | (u64)(tun_src1);
-
-              if (cached_tunnel_key != key)
-                {
-                  vnet_hw_interface_t * hi;
-                  ipsec_gre_tunnel_t * t;
-                  uword * p;
-
-                  p = hash_get (igm->tunnel_by_key, key);
-                  if (!p)
-                    {
-                      next1 = IPSEC_GRE_INPUT_NEXT_DROP;
-                      b1->error = node->errors[IPSEC_GRE_ERROR_NO_SUCH_TUNNEL];
-                      goto drop1;
-                    }
-                  t = pool_elt_at_index (igm->tunnels, p[0]);
-                  hi = vnet_get_hw_interface (igm->vnet_main,
-                            t->hw_if_index);
-                  tunnel_sw_if_index = hi->sw_if_index;
-                  cached_tunnel_sw_if_index = tunnel_sw_if_index;
-                }
-              else
-                {
-                  tunnel_sw_if_index = cached_tunnel_sw_if_index;
-                }
-              vnet_buffer(b1)->sw_if_index[VLIB_RX] = tunnel_sw_if_index;
-            }
-
-drop1:
-          if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
-            {
-              ipsec_gre_rx_trace_t *tr = vlib_add_trace (vm, node,
-                                                   b0, sizeof (*tr));
-              tr->tunnel_id = ~0;
-              tr->length = ip0->length;
-              tr->src.as_u32 = ip0->src_address.as_u32;
-              tr->dst.as_u32 = ip0->dst_address.as_u32;
-            }
-
-          if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
-            {
-              ipsec_gre_rx_trace_t *tr = vlib_add_trace (vm, node,
-                                                   b1, sizeof (*tr));
-              tr->tunnel_id = ~0;
-              tr->length = ip1->length;
-              tr->src.as_u32 = ip1->src_address.as_u32;
-              tr->dst.as_u32 = ip1->dst_address.as_u32;
-            }
-
-          vlib_buffer_advance (b0, sizeof (*h0));
-          vlib_buffer_advance (b1, sizeof (*h1));
-
-	  vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
-					   to_next, n_left_to_next,
-					   bi0, bi1, next0, next1);
-	}
-
-      while (n_left_from > 0 && n_left_to_next > 0)
-	{
-	  u32 bi0;
-	  vlib_buffer_t * b0;
-	  gre_header_t * h0;
-          ip4_header_t * ip0;
-          u16 version0, protocol0;
-          int verr0;
-	  u32 next0;
-	  u32 tun_src0, tun_dst0;
-
-	  bi0 = from[0];
-	  to_next[0] = bi0;
-	  from += 1;
-	  to_next += 1;
-	  n_left_from -= 1;
-	  n_left_to_next -= 1;
-
-	  b0 = vlib_get_buffer (vm, bi0);
-          ip0 = vlib_buffer_get_current (b0);
-
-          tun_src0 = ip0->src_address.as_u32;
-          tun_dst0 = ip0->dst_address.as_u32;
-
-          vlib_buffer_advance (b0, sizeof (*ip0));
-
-	  h0 = vlib_buffer_get_current (b0);
-
-          protocol0 = clib_net_to_host_u16 (h0->protocol);
-          if (PREDICT_TRUE(protocol0 == GRE_PROTOCOL_teb))
-            {
-              next0 = IPSEC_GRE_INPUT_NEXT_L2_INPUT;
-              b0->error = node->errors[IPSEC_GRE_ERROR_NONE];
-            }
-          else
-            {
-              b0->error = node->errors[IPSEC_GRE_ERROR_UNKNOWN_PROTOCOL];
-              next0 = IPSEC_GRE_INPUT_NEXT_DROP;
-            }
-
-          version0 = clib_net_to_host_u16 (h0->flags_and_version);
-          verr0 =  version0 & GRE_VERSION_MASK;
-          b0->error = verr0 ? node->errors[IPSEC_GRE_ERROR_UNSUPPORTED_VERSION]
-              : b0->error;
-          next0 = verr0 ? IPSEC_GRE_INPUT_NEXT_DROP : next0;
-
-          /* For L2 payload set input sw_if_index to GRE tunnel for learning */
-          if (PREDICT_TRUE(next0 == IPSEC_GRE_INPUT_NEXT_L2_INPUT))
-            {
-              u64 key = ((u64)(tun_dst0) << 32) | (u64)(tun_src0);
-
-              if (cached_tunnel_key != key)
-                {
-                  vnet_hw_interface_t * hi;
-                  ipsec_gre_tunnel_t * t;
-                  uword * p;
-
-                  p = hash_get (igm->tunnel_by_key, key);
-                  if (!p)
-                    {
-                      next0 = IPSEC_GRE_INPUT_NEXT_DROP;
-                      b0->error = node->errors[IPSEC_GRE_ERROR_NO_SUCH_TUNNEL];
-                      goto drop;
-                    }
-                  t = pool_elt_at_index (igm->tunnels, p[0]);
-                  hi = vnet_get_hw_interface (igm->vnet_main,
-                            t->hw_if_index);
-                  tunnel_sw_if_index = hi->sw_if_index;
-                  cached_tunnel_sw_if_index = tunnel_sw_if_index;
-                }
-              else
-                {
-                  tunnel_sw_if_index = cached_tunnel_sw_if_index;
-                }
-              vnet_buffer(b0)->sw_if_index[VLIB_RX] = tunnel_sw_if_index;
-            }
-
-drop:
-          if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
-            {
-              ipsec_gre_rx_trace_t *tr = vlib_add_trace (vm, node,
-                                                   b0, sizeof (*tr));
-              tr->tunnel_id = ~0;
-              tr->length = ip0->length;
-              tr->src.as_u32 = ip0->src_address.as_u32;
-              tr->dst.as_u32 = ip0->dst_address.as_u32;
-            }
-
-          vlib_buffer_advance (b0, sizeof (*h0));
-
-	  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, ipsec_gre_input_node.index,
-                               IPSEC_GRE_ERROR_PKTS_DECAP, from_frame->n_vectors);
-  return from_frame->n_vectors;
-}
-
-static char * ipsec_gre_error_strings[] = {
-#define ipsec_gre_error(n,s) s,
-#include "error.def"
-#undef ipsec_gre_error
-};
-
-VLIB_REGISTER_NODE (ipsec_gre_input_node) = {
-  .name = "ipsec-gre-input",
-  /* Takes a vector of packets. */
-  .vector_size = sizeof (u32),
-
-  .n_errors = IPSEC_GRE_N_ERROR,
-  .error_strings = ipsec_gre_error_strings,
-
-  .n_next_nodes = IPSEC_GRE_INPUT_N_NEXT,
-  .next_nodes = {
-#define _(s,n) [IPSEC_GRE_INPUT_NEXT_##s] = n,
-    foreach_ipsec_gre_input_next
-#undef _
-  },
-
-  .format_trace = format_ipsec_gre_rx_trace,
-};
-
-static clib_error_t * ipsec_gre_input_init (vlib_main_t * vm)
-{
-  {
-    clib_error_t * error;
-    error = vlib_call_init_function (vm, ipsec_gre_init);
-    if (error)
-      clib_error_report (error);
-  }
-
-  return 0;
-}
-
-VLIB_INIT_FUNCTION (ipsec_gre_input_init);
diff --git a/src/vnet/ipsec/ah_decrypt.c b/src/vnet/ipsec/ah_decrypt.c
index d27d423..741fa91 100644
--- a/src/vnet/ipsec/ah_decrypt.c
+++ b/src/vnet/ipsec/ah_decrypt.c
@@ -27,8 +27,7 @@
 #define foreach_ah_decrypt_next \
   _ (DROP, "error-drop")        \
   _ (IP4_INPUT, "ip4-input")    \
-  _ (IP6_INPUT, "ip6-input")    \
-  _ (IPSEC_GRE_INPUT, "ipsec-gre-input")
+  _ (IP6_INPUT, "ip6-input")
 
 #define _(v, s) AH_DECRYPT_NEXT_##v,
 typedef enum
@@ -371,10 +370,6 @@
 	    }
 	}
 
-      /* for IPSec-GRE tunnel next node is ipsec-gre-input */
-      if (PREDICT_FALSE (ipsec_sa_is_set_IS_GRE (sa0)))
-	next[0] = AH_DECRYPT_NEXT_IPSEC_GRE_INPUT;
-
       vnet_buffer (b[0])->sw_if_index[VLIB_TX] = (u32) ~ 0;
     trace:
       if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
diff --git a/src/vnet/ipsec/esp_decrypt.c b/src/vnet/ipsec/esp_decrypt.c
index 710c8f1..48f08f4 100644
--- a/src/vnet/ipsec/esp_decrypt.c
+++ b/src/vnet/ipsec/esp_decrypt.c
@@ -22,12 +22,12 @@
 #include <vnet/ipsec/ipsec.h>
 #include <vnet/ipsec/esp.h>
 #include <vnet/ipsec/ipsec_io.h>
+#include <vnet/ipsec/ipsec_tun.h>
 
 #define foreach_esp_decrypt_next                \
 _(DROP, "error-drop")                           \
 _(IP4_INPUT, "ip4-input-no-checksum")           \
-_(IP6_INPUT, "ip6-input")                       \
-_(IPSEC_GRE_INPUT, "ipsec-gre-input")
+_(IP6_INPUT, "ip6-input")
 
 #define _(v, s) ESP_DECRYPT_NEXT_##v,
 typedef enum
@@ -93,7 +93,7 @@
     {
       u8 icv_sz;
       u8 iv_sz;
-      ipsec_sa_flags_t flags:8;
+      ipsec_sa_flags_t flags;
       u32 sa_index;
     };
     u64 sa_data;
@@ -111,7 +111,7 @@
 always_inline uword
 esp_decrypt_inline (vlib_main_t * vm,
 		    vlib_node_runtime_t * node, vlib_frame_t * from_frame,
-		    int is_ip6)
+		    int is_ip6, int is_tun)
 {
   ipsec_main_t *im = &ipsec_main;
   u32 thread_index = vm->thread_index;
@@ -378,7 +378,7 @@
       u16 adv = pd->iv_sz + esp_sz;
       u16 tail = sizeof (esp_footer_t) + f->pad_length + pd->icv_sz;
 
-      if ((pd->flags & tun_flags) == 0)	/* transport mode */
+      if ((pd->flags & tun_flags) == 0 && !is_tun)	/* transport mode */
 	{
 	  u8 udp_sz = (is_ip6 == 0 && pd->flags & IPSEC_SA_FLAG_UDP_ENCAP) ?
 	    sizeof (udp_header_t) : 0;
@@ -437,12 +437,50 @@
 	    {
 	      next[0] = ESP_DECRYPT_NEXT_DROP;
 	      b[0]->error = node->errors[ESP_DECRYPT_ERROR_DECRYPTION_FAILED];
+	      goto trace;
+	    }
+	  if (is_tun)
+	    {
+	      if (ipsec_sa_is_set_IS_PROTECT (sa0))
+		{
+		  /*
+		   * Check that the reveal IP header matches that
+		   * of the tunnel we are protecting
+		   */
+		  const ipsec_tun_protect_t *itp;
+
+		  itp =
+		    ipsec_tun_protect_get (vnet_buffer (b[0])->
+					   ipsec.protect_index);
+		  if (PREDICT_TRUE (f->next_header == IP_PROTOCOL_IP_IN_IP))
+		    {
+		      const ip4_header_t *ip4;
+
+		      ip4 = vlib_buffer_get_current (b[0]);
+
+		      if (!ip46_address_is_equal_v4 (&itp->itp_tun.src,
+						     &ip4->dst_address) ||
+			  !ip46_address_is_equal_v4 (&itp->itp_tun.dst,
+						     &ip4->src_address))
+			next[0] = ESP_DECRYPT_NEXT_DROP;
+
+		    }
+		  else if (f->next_header == IP_PROTOCOL_IPV6)
+		    {
+		      const ip6_header_t *ip6;
+
+		      ip6 = vlib_buffer_get_current (b[0]);
+
+		      if (!ip46_address_is_equal_v6 (&itp->itp_tun.src,
+						     &ip6->dst_address) ||
+			  !ip46_address_is_equal_v6 (&itp->itp_tun.dst,
+						     &ip6->src_address))
+			next[0] = ESP_DECRYPT_NEXT_DROP;
+		    }
+		}
 	    }
 	}
 
-      if (PREDICT_FALSE (ipsec_sa_is_set_IS_GRE (sa0)))
-	next[0] = ESP_DECRYPT_NEXT_IPSEC_GRE_INPUT;
-
     trace:
       if (PREDICT_FALSE (b[0]->flags & VLIB_BUFFER_IS_TRACED))
 	{
@@ -477,7 +515,28 @@
 				  vlib_node_runtime_t * node,
 				  vlib_frame_t * from_frame)
 {
-  return esp_decrypt_inline (vm, node, from_frame, 0 /* is_ip6 */ );
+  return esp_decrypt_inline (vm, node, from_frame, 0, 0);
+}
+
+VLIB_NODE_FN (esp4_decrypt_tun_node) (vlib_main_t * vm,
+				      vlib_node_runtime_t * node,
+				      vlib_frame_t * from_frame)
+{
+  return esp_decrypt_inline (vm, node, from_frame, 0, 1);
+}
+
+VLIB_NODE_FN (esp6_decrypt_node) (vlib_main_t * vm,
+				  vlib_node_runtime_t * node,
+				  vlib_frame_t * from_frame)
+{
+  return esp_decrypt_inline (vm, node, from_frame, 1, 0);
+}
+
+VLIB_NODE_FN (esp6_decrypt_tun_node) (vlib_main_t * vm,
+				      vlib_node_runtime_t * node,
+				      vlib_frame_t * from_frame)
+{
+  return esp_decrypt_inline (vm, node, from_frame, 1, 1);
 }
 
 /* *INDENT-OFF* */
@@ -497,16 +556,7 @@
 #undef _
   },
 };
-/* *INDENT-ON* */
 
-VLIB_NODE_FN (esp6_decrypt_node) (vlib_main_t * vm,
-				  vlib_node_runtime_t * node,
-				  vlib_frame_t * from_frame)
-{
-  return esp_decrypt_inline (vm, node, from_frame, 1 /* is_ip6 */ );
-}
-
-/* *INDENT-OFF* */
 VLIB_REGISTER_NODE (esp6_decrypt_node) = {
   .name = "esp6-decrypt",
   .vector_size = sizeof (u32),
@@ -523,6 +573,40 @@
 #undef _
   },
 };
+
+VLIB_REGISTER_NODE (esp4_decrypt_tun_node) = {
+  .name = "esp4-decrypt-tun",
+  .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_REGISTER_NODE (esp6_decrypt_tun_node) = {
+  .name = "esp6-decrypt-tun",
+  .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 _
+  },
+};
 /* *INDENT-ON* */
 
 /*
diff --git a/src/vnet/ipsec/esp_encrypt.c b/src/vnet/ipsec/esp_encrypt.c
index d7cda05..cf48548 100644
--- a/src/vnet/ipsec/esp_encrypt.c
+++ b/src/vnet/ipsec/esp_encrypt.c
@@ -408,12 +408,18 @@
 	  ip_hdr = payload - hdr_len;
 
 	  /* L2 header */
-	  l2_len = vnet_buffer (b[0])->ip.save_rewrite_length;
-	  hdr_len += l2_len;
-	  l2_hdr = payload - hdr_len;
+	  if (!is_tun)
+	    {
+	      l2_len = vnet_buffer (b[0])->ip.save_rewrite_length;
+	      hdr_len += l2_len;
+	      l2_hdr = payload - hdr_len;
 
-	  /* copy l2 and ip header */
-	  clib_memcpy_le32 (l2_hdr, old_ip_hdr - l2_len, l2_len);
+	      /* copy l2 and ip header */
+	      clib_memcpy_le32 (l2_hdr, old_ip_hdr - l2_len, l2_len);
+	    }
+	  else
+	    l2_len = 0;
+
 	  clib_memcpy_le64 (ip_hdr, old_ip_hdr, ip_len);
 
 	  if (is_ip6)
@@ -440,7 +446,8 @@
 		esp_update_ip4_hdr (ip4, len, /* is_transport */ 1, 0);
 	    }
 
-	  next[0] = ESP_ENCRYPT_NEXT_INTERFACE_OUTPUT;
+	  if (!is_tun)
+	    next[0] = ESP_ENCRYPT_NEXT_INTERFACE_OUTPUT;
 	}
 
       esp->spi = spi;
@@ -618,6 +625,13 @@
   .node_name = "esp4-encrypt-tun",
   .runs_before = VNET_FEATURES ("adj-midchain-tx"),
 };
+
+VNET_FEATURE_INIT (esp4_ethernet_encrypt_tun_feat_node, static) =
+{
+  .arc_name = "ethernet-output",
+  .node_name = "esp4-encrypt-tun",
+  .runs_before = VNET_FEATURES ("adj-midchain-tx", "adj-midchain-tx-no-count"),
+};
 /* *INDENT-ON* */
 
 VLIB_NODE_FN (esp6_encrypt_tun_node) (vlib_main_t * vm,
diff --git a/src/vnet/ipsec/ipsec.api b/src/vnet/ipsec/ipsec.api
index bb9e805..12bdad0 100644
--- a/src/vnet/ipsec/ipsec.api
+++ b/src/vnet/ipsec/ipsec.api
@@ -17,6 +17,7 @@
 option version = "3.0.0";
 
 import "vnet/ip/ip_types.api";
+import "vnet/interface_types.api";
 
 /** \brief IPsec: Add/delete Security Policy Database
     @param client_index - opaque cookie to identify the sender
@@ -305,6 +306,81 @@
   u32 stat_index;
 };
 
+/** \brief Add or Update Protection for a tunnel with IPSEC
+
+    Tunnel protection directly associates an SA with all packets
+    ingress and egress on the tunnel. This could also be achieved by
+    assigning an SPD to the tunnel, but that would incur an unnessccary
+    SPD entry lookup.
+
+    For tunnels the ESP acts on the post-encapsulated packet. So if this
+    packet:
+      +---------+------+
+      | Payload | O-IP |
+      +---------+------+
+    where O-IP is the overlay IP addrees that was routed into the tunnel,
+    the resulting encapsulated packet will be:
+      +---------+------+------+
+      | Payload | O-IP | T-IP |
+      +---------+------+------+
+    where T-IP is the tunnel's src.dst IP addresses.
+    If the SAs used for protection are in transport mode then the ESP is
+    inserted before T-IP, i.e.:
+      +---------+------+-----+------+
+      | Payload | O-IP | ESP | T-IP |
+      +---------+------+-----+------+
+    If the SAs used for protection are in tunnel mode then another
+    encapsulation occurs, i.e.:
+      +---------+------+------+-----+------+
+      | Payload | O-IP | T-IP | ESP | C-IP |
+      +---------+------+------+-----+------+
+    where C-IP are the crypto endpoint IP addresses defined as the tunnel
+    endpoints in the SA.
+    The mode for the inbound and outbound SA must be the same.
+
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param sw_id_index - Tunnel interface to protect
+    @param sa_in - The ID [set] of inbound SAs
+    @param sa_out - The ID of outbound SA
+*/
+typedef ipsec_tunnel_protect
+{
+  vl_api_interface_index_t sw_if_index;
+  u32 sa_out;
+  u8 n_sa_in;
+  u32 sa_in[n_sa_in];
+};
+
+autoreply define ipsec_tunnel_protect_update
+{
+  u32 client_index;
+  u32 context;
+
+  vl_api_ipsec_tunnel_protect_t tunnel;
+};
+
+autoreply define ipsec_tunnel_protect_del
+{
+  u32 client_index;
+  u32 context;
+
+  vl_api_interface_index_t sw_if_index;
+};
+
+define ipsec_tunnel_protect_dump
+{
+  u32 client_index;
+  u32 context;
+  vl_api_interface_index_t sw_if_index;
+};
+
+define ipsec_tunnel_protect_details
+{
+  u32 context;
+  vl_api_ipsec_tunnel_protect_t tun;
+};
+
 /** \brief IPsec: Get SPD interfaces
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
diff --git a/src/vnet/ipsec/ipsec.h b/src/vnet/ipsec/ipsec.h
index 45576b3..c77d0fe 100644
--- a/src/vnet/ipsec/ipsec.h
+++ b/src/vnet/ipsec/ipsec.h
@@ -115,6 +115,8 @@
   uword *ipsec6_if_pool_index_by_key;
   uword *ipsec_if_real_dev_by_show_dev;
   uword *ipsec_if_by_sw_if_index;
+  uword *tun4_protect_by_key;
+  uword *tun6_protect_by_key;
 
   /* node indices */
   u32 error_drop_node_index;
diff --git a/src/vnet/ipsec/ipsec_api.c b/src/vnet/ipsec/ipsec_api.c
index 2c7c0d9..99e25f1 100644
--- a/src/vnet/ipsec/ipsec_api.c
+++ b/src/vnet/ipsec/ipsec_api.c
@@ -30,6 +30,7 @@
 
 #if WITH_LIBSSL > 0
 #include <vnet/ipsec/ipsec.h>
+#include <vnet/ipsec/ipsec_tun.h>
 #endif /* IPSEC */
 
 #define vl_typedefs		/* define message structures */
@@ -60,7 +61,10 @@
 _(IPSEC_TUNNEL_IF_ADD_DEL, ipsec_tunnel_if_add_del)             \
 _(IPSEC_TUNNEL_IF_SET_SA, ipsec_tunnel_if_set_sa)               \
 _(IPSEC_SELECT_BACKEND, ipsec_select_backend)                   \
-_(IPSEC_BACKEND_DUMP, ipsec_backend_dump)
+_(IPSEC_BACKEND_DUMP, ipsec_backend_dump)                       \
+_(IPSEC_TUNNEL_PROTECT_UPDATE, ipsec_tunnel_protect_update)     \
+_(IPSEC_TUNNEL_PROTECT_DEL, ipsec_tunnel_protect_del)           \
+_(IPSEC_TUNNEL_PROTECT_DUMP, ipsec_tunnel_protect_dump)
 
 static void
 vl_api_ipsec_spd_add_del_t_handler (vl_api_ipsec_spd_add_del_t * mp)
@@ -104,6 +108,132 @@
   REPLY_MACRO (VL_API_IPSEC_INTERFACE_ADD_DEL_SPD_REPLY);
 }
 
+static void vl_api_ipsec_tunnel_protect_update_t_handler
+  (vl_api_ipsec_tunnel_protect_update_t * mp)
+{
+  vlib_main_t *vm __attribute__ ((unused)) = vlib_get_main ();
+  vl_api_ipsec_tunnel_protect_update_reply_t *rmp;
+  u32 sw_if_index, ii, *sa_ins = NULL;
+  int rv;
+
+  sw_if_index = ntohl (mp->tunnel.sw_if_index);
+
+  VALIDATE_SW_IF_INDEX (&(mp->tunnel));
+
+#if WITH_LIBSSL > 0
+
+  for (ii = 0; ii < mp->tunnel.n_sa_in; ii++)
+    vec_add1 (sa_ins, ntohl (mp->tunnel.sa_in[ii]));
+
+  rv = ipsec_tun_protect_update (sw_if_index,
+				 ntohl (mp->tunnel.sa_out), sa_ins);
+#else
+  rv = VNET_API_ERROR_UNIMPLEMENTED;
+#endif
+
+  BAD_SW_IF_INDEX_LABEL;
+
+  REPLY_MACRO (VL_API_IPSEC_TUNNEL_PROTECT_UPDATE_REPLY);
+}
+
+static void vl_api_ipsec_tunnel_protect_del_t_handler
+  (vl_api_ipsec_tunnel_protect_del_t * mp)
+{
+  vlib_main_t *vm __attribute__ ((unused)) = vlib_get_main ();
+  vl_api_ipsec_tunnel_protect_del_reply_t *rmp;
+  int rv;
+  u32 sw_if_index;
+
+  sw_if_index = ntohl (mp->sw_if_index);
+
+  VALIDATE_SW_IF_INDEX (mp);
+
+#if WITH_LIBSSL > 0
+  rv = ipsec_tun_protect_del (sw_if_index);
+#else
+  rv = VNET_API_ERROR_UNIMPLEMENTED;
+#endif
+
+  BAD_SW_IF_INDEX_LABEL;
+
+  REPLY_MACRO (VL_API_IPSEC_TUNNEL_PROTECT_DEL_REPLY);
+}
+
+typedef struct ipsec_tunnel_protect_walk_ctx_t_
+{
+  vl_api_registration_t *reg;
+  u32 context;
+} ipsec_tunnel_protect_walk_ctx_t;
+
+static walk_rc_t
+send_ipsec_tunnel_protect_details (index_t itpi, void *arg)
+{
+  ipsec_tunnel_protect_walk_ctx_t *ctx = arg;
+  vl_api_ipsec_tunnel_protect_details_t *mp;
+  ipsec_tun_protect_t *itp;
+  u32 sai, ii = 0;
+
+  itp = ipsec_tun_protect_get (itpi);
+
+
+  mp = vl_msg_api_alloc (sizeof (*mp) + (sizeof (u32) * itp->itp_n_sa_in));
+  clib_memset (mp, 0, sizeof (*mp));
+  mp->_vl_msg_id = ntohs (VL_API_IPSEC_TUNNEL_PROTECT_DETAILS);
+  mp->context = ctx->context;
+
+  mp->tun.sw_if_index = htonl (itp->itp_sw_if_index);
+
+  mp->tun.sa_out = htonl (itp->itp_out_sa);
+  mp->tun.n_sa_in = itp->itp_n_sa_in;
+  /* *INDENT-OFF* */
+  FOR_EACH_IPSEC_PROTECT_INPUT_SAI(itp, sai,
+  ({
+    mp->tun.sa_in[ii++] = htonl (sai);
+  }));
+  /* *INDENT-ON* */
+
+  vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+  return (WALK_CONTINUE);
+}
+
+static void
+vl_api_ipsec_tunnel_protect_dump_t_handler (vl_api_ipsec_tunnel_protect_dump_t
+					    * mp)
+{
+  vl_api_registration_t *reg;
+  u32 sw_if_index;
+
+#if WITH_LIBSSL > 0
+  reg = vl_api_client_index_to_registration (mp->client_index);
+  if (!reg)
+    return;
+
+  ipsec_tunnel_protect_walk_ctx_t ctx = {
+    .reg = reg,
+    .context = mp->context,
+  };
+
+  sw_if_index = ntohl (mp->sw_if_index);
+
+  if (~0 == sw_if_index)
+    {
+      ipsec_tun_protect_walk (send_ipsec_tunnel_protect_details, &ctx);
+    }
+  else
+    {
+      index_t itpi;
+
+      itpi = ipsec_tun_protect_find (sw_if_index);
+
+      if (INDEX_INVALID != itpi)
+	send_ipsec_tunnel_protect_details (itpi, &ctx);
+    }
+#else
+  clib_warning ("unimplemented");
+#endif
+}
+
 static int
 ipsec_spd_action_decode (vl_api_ipsec_spd_action_t in,
 			 ipsec_policy_action_t * out)
@@ -879,6 +1009,13 @@
 #undef _
 
   /*
+   * Adding and deleting SAs is MP safe since when they are added/delete
+   * no traffic is using them
+   */
+  am->is_mp_safe[VL_API_IPSEC_SAD_ENTRY_ADD_DEL] = 1;
+  am->is_mp_safe[VL_API_IPSEC_SAD_ENTRY_ADD_DEL_REPLY] = 1;
+
+  /*
    * Set up the (msg_name, crc, message-id) table
    */
   setup_message_id_table (am);
diff --git a/src/vnet/ipsec/ipsec_cli.c b/src/vnet/ipsec/ipsec_cli.c
index 4172e10..1648179 100644
--- a/src/vnet/ipsec/ipsec_cli.c
+++ b/src/vnet/ipsec/ipsec_cli.c
@@ -22,6 +22,7 @@
 #include <vnet/fib/fib.h>
 
 #include <vnet/ipsec/ipsec.h>
+#include <vnet/ipsec/ipsec_tun.h>
 
 static clib_error_t *
 set_interface_spd_command_fn (vlib_main_t * vm,
@@ -105,7 +106,7 @@
 	is_add = 0;
       else if (unformat (line_input, "spi %u", &spi))
 	;
-      else if (unformat (line_input, "salt %u", &salt))
+      else if (unformat (line_input, "salt 0x%x", &salt))
 	;
       else if (unformat (line_input, "esp"))
 	proto = IPSEC_PROTOCOL_ESP;
@@ -446,12 +447,52 @@
   return 0;
 }
 
+static clib_error_t *
+clear_ipsec_sa_command_fn (vlib_main_t * vm,
+			   unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  ipsec_main_t *im = &ipsec_main;
+  u32 sai = ~0;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "%u", &sai))
+	;
+      else
+	break;
+    }
+
+  if (~0 == sai)
+    {
+      /* *INDENT-OFF* */
+      pool_foreach_index (sai, im->sad, ({
+        ipsec_sa_clear(sai);
+      }));
+      /* *INDENT-ON* */
+    }
+  else
+    {
+      if (pool_is_free_index (im->sad, sai))
+	return clib_error_return (0, "unknown SA index: %d", sai);
+      else
+	ipsec_sa_clear (sai);
+    }
+
+  return 0;
+}
+
 /* *INDENT-OFF* */
 VLIB_CLI_COMMAND (show_ipsec_sa_command, static) = {
     .path = "show ipsec sa",
     .short_help = "show ipsec sa [index]",
     .function = show_ipsec_sa_command_fn,
 };
+
+VLIB_CLI_COMMAND (clear_ipsec_sa_command, static) = {
+    .path = "clear ipsec sa",
+    .short_help = "clear ipsec sa [index]",
+    .function = clear_ipsec_sa_command_fn,
+};
 /* *INDENT-ON* */
 
 static clib_error_t *
@@ -823,6 +864,88 @@
 };
 /* *INDENT-ON* */
 
+static clib_error_t *
+ipsec_tun_protect_cmd (vlib_main_t * vm,
+		       unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  unformat_input_t _line_input, *line_input = &_line_input;
+  u32 sw_if_index, is_del, sa_in, sa_out, *sa_ins = NULL;
+  vnet_main_t *vnm;
+
+  is_del = 0;
+  sw_if_index = ~0;
+  vnm = vnet_get_main ();
+
+  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, "del"))
+	is_del = 1;
+      else if (unformat (line_input, "add"))
+	is_del = 0;
+      else if (unformat (line_input, "sa-in %d", &sa_in))
+	vec_add1 (sa_ins, sa_in);
+      else if (unformat (line_input, "sa-out %d", &sa_out))
+	;
+      else if (unformat (line_input, "%U",
+			 unformat_vnet_sw_interface, vnm, &sw_if_index))
+	;
+      else
+	return (clib_error_return (0, "unknown input '%U'",
+				   format_unformat_error, line_input));
+    }
+
+  if (!is_del)
+    ipsec_tun_protect_update (sw_if_index, sa_out, sa_ins);
+
+  unformat_free (line_input);
+  return NULL;
+}
+
+/**
+ * Protect tunnel with IPSEC
+ */
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (ipsec_tun_protect_cmd_node, static) =
+{
+  .path = "ipsec tunnel protect",
+  .function = ipsec_tun_protect_cmd,
+  .short_help = "ipsec tunnel protect <interface> input-sa <SA> output-sa <SA>",
+    // this is not MP safe
+};
+/* *INDENT-ON* */
+
+static walk_rc_t
+ipsec_tun_protect_show_one (index_t itpi, void *ctx)
+{
+  vlib_cli_output (ctx, "%U", format_ipsec_tun_protect, itpi);
+
+  return (WALK_CONTINUE);
+}
+
+static clib_error_t *
+ipsec_tun_protect_show (vlib_main_t * vm,
+			unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  ipsec_tun_protect_walk (ipsec_tun_protect_show_one, vm);
+
+  return NULL;
+}
+
+/**
+ * show IPSEC tunnel protection
+ */
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (ipsec_tun_protect_show_node, static) =
+{
+  .path = "show ipsec protect",
+  .function = ipsec_tun_protect_show,
+  .short_help =  "show ipsec protect",
+};
+/* *INDENT-ON* */
+
 clib_error_t *
 ipsec_cli_init (vlib_main_t * vm)
 {
diff --git a/src/vnet/ipsec/ipsec_format.c b/src/vnet/ipsec/ipsec_format.c
index d0d073b..1e6e2d5 100644
--- a/src/vnet/ipsec/ipsec_format.c
+++ b/src/vnet/ipsec/ipsec_format.c
@@ -22,6 +22,7 @@
 #include <vnet/fib/fib_table.h>
 
 #include <vnet/ipsec/ipsec.h>
+#include <vnet/ipsec/ipsec_tun.h>
 
 u8 *
 format_ipsec_policy_action (u8 * s, va_list * args)
@@ -368,6 +369,40 @@
   return (s);
 }
 
+u8 *
+format_ipsec_tun_protect (u8 * s, va_list * args)
+{
+  u32 itpi = va_arg (*args, u32);
+  ipsec_tun_protect_t *itp;
+  u32 sai;
+
+  if (pool_is_free_index (ipsec_protect_pool, itpi))
+    {
+      s = format (s, "No such tunnel index: %d", itpi);
+      goto done;
+    }
+
+  itp = pool_elt_at_index (ipsec_protect_pool, itpi);
+
+  s = format (s, "%U", format_vnet_sw_if_index_name,
+	      vnet_get_main (), itp->itp_sw_if_index);
+  s = format (s, "\n output-sa:");
+  s =
+    format (s, "\n  %U", format_ipsec_sa, itp->itp_out_sa,
+	    IPSEC_FORMAT_BRIEF);
+
+  s = format (s, "\n input-sa:");
+  /* *INDENT-OFF* */
+  FOR_EACH_IPSEC_PROTECT_INPUT_SAI(itp, sai,
+  ({
+  s = format (s, "\n  %U", format_ipsec_sa, sai, IPSEC_FORMAT_BRIEF);
+  }));
+  /* *INDENT-ON* */
+
+done:
+  return (s);
+}
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
diff --git a/src/vnet/ipsec/ipsec_if.c b/src/vnet/ipsec/ipsec_if.c
index 6627d29..8e4f3f1 100644
--- a/src/vnet/ipsec/ipsec_if.c
+++ b/src/vnet/ipsec/ipsec_if.c
@@ -84,7 +84,7 @@
 }
 
 /**
- * @brief Call back when restacking all adjacencies on a GRE interface
+ * @brief Call back when restacking all adjacencies on a IPSec interface
  */
 static adj_walk_rc_t
 ipsec_if_adj_walk_cb (adj_index_t ai, void *ctx)
@@ -100,7 +100,7 @@
   fib_protocol_t proto;
 
   /*
-   * walk all the adjacencies on th GRE interface and restack them
+   * walk all the adjacencies on the IPSec interface and restack them
    */
   FOR_EACH_FIB_IP_PROTOCOL (proto)
   {
@@ -434,86 +434,6 @@
 }
 
 int
-ipsec_add_del_ipsec_gre_tunnel (vnet_main_t * vnm,
-				const ipsec_gre_tunnel_add_del_args_t * args)
-{
-  ipsec_tunnel_if_t *t = 0;
-  ipsec_main_t *im = &ipsec_main;
-  uword *p;
-  ipsec_sa_t *sa;
-  ipsec4_tunnel_key_t key;
-  u32 isa, osa;
-
-  p = hash_get (im->sa_index_by_sa_id, args->local_sa_id);
-  if (!p)
-    return VNET_API_ERROR_INVALID_VALUE;
-  osa = p[0];
-  sa = pool_elt_at_index (im->sad, p[0]);
-  ipsec_sa_set_IS_GRE (sa);
-
-  p = hash_get (im->sa_index_by_sa_id, args->remote_sa_id);
-  if (!p)
-    return VNET_API_ERROR_INVALID_VALUE;
-  isa = p[0];
-  sa = pool_elt_at_index (im->sad, p[0]);
-  ipsec_sa_set_IS_GRE (sa);
-
-  /* we form the key from the input/remote SA whose tunnel is srouce
-   * at the remote end */
-  if (ipsec_sa_is_set_IS_TUNNEL (sa))
-    {
-      key.remote_ip = sa->tunnel_src_addr.ip4.as_u32;
-      key.spi = clib_host_to_net_u32 (sa->spi);
-    }
-  else
-    {
-      key.remote_ip = args->src.as_u32;
-      key.spi = clib_host_to_net_u32 (sa->spi);
-    }
-
-  p = hash_get (im->ipsec4_if_pool_index_by_key, key.as_u64);
-
-  if (args->is_add)
-    {
-      /* check if same src/dst pair exists */
-      if (p)
-	return VNET_API_ERROR_INVALID_VALUE;
-
-      pool_get_aligned (im->tunnel_interfaces, t, CLIB_CACHE_LINE_BYTES);
-      clib_memset (t, 0, sizeof (*t));
-
-      t->input_sa_index = isa;
-      t->output_sa_index = osa;
-      t->hw_if_index = ~0;
-      hash_set (im->ipsec4_if_pool_index_by_key, key.as_u64,
-		t - im->tunnel_interfaces);
-
-      /*1st interface, register protocol */
-      if (pool_elts (im->tunnel_interfaces) == 1)
-	{
-	  ip4_register_protocol (IP_PROTOCOL_IPSEC_ESP,
-				 ipsec4_if_input_node.index);
-	  /* TBD, GRE IPSec6
-	   *
-	   ip6_register_protocol (IP_PROTOCOL_IPSEC_ESP,
-	   ipsec6_if_input_node.index);
-	   */
-	}
-    }
-  else
-    {
-      /* check if exists */
-      if (!p)
-	return VNET_API_ERROR_INVALID_VALUE;
-
-      t = pool_elt_at_index (im->tunnel_interfaces, p[0]);
-      hash_unset (im->ipsec4_if_pool_index_by_key, key.as_u64);
-      pool_put (im->tunnel_interfaces, t);
-    }
-  return 0;
-}
-
-int
 ipsec_set_interface_sa (vnet_main_t * vnm, u32 hw_if_index, u32 sa_id,
 			u8 is_outbound)
 {
diff --git a/src/vnet/ipsec/ipsec_if.h b/src/vnet/ipsec/ipsec_if.h
index 4086710..042ddde 100644
--- a/src/vnet/ipsec/ipsec_if.h
+++ b/src/vnet/ipsec/ipsec_if.h
@@ -84,23 +84,10 @@
 }) ipsec6_tunnel_key_t;
 /* *INDENT-ON* */
 
-typedef struct
-{
-  u8 is_add;
-  u32 local_sa_id;
-  u32 remote_sa_id;
-  ip4_address_t src;
-  ip4_address_t dst;
-} ipsec_gre_tunnel_add_del_args_t;
-
 extern int ipsec_add_del_tunnel_if_internal (vnet_main_t * vnm,
 					     ipsec_add_del_tunnel_args_t *
 					     args, u32 * sw_if_index);
 extern int ipsec_add_del_tunnel_if (ipsec_add_del_tunnel_args_t * args);
-extern int ipsec_add_del_ipsec_gre_tunnel (vnet_main_t * vnm,
-					   const
-					   ipsec_gre_tunnel_add_del_args_t *
-					   args);
 
 extern int ipsec_set_interface_sa (vnet_main_t * vnm, u32 hw_if_index,
 				   u32 sa_id, u8 is_outbound);
diff --git a/src/vnet/ipsec/ipsec_io.h b/src/vnet/ipsec/ipsec_io.h
index 32668a0..b17d3d7 100644
--- a/src/vnet/ipsec/ipsec_io.h
+++ b/src/vnet/ipsec/ipsec_io.h
@@ -30,9 +30,9 @@
   _ (PUNT, "punt-dispatch")        \
   _ (DROP, "error-drop")
 
-#define _(v, s) IPSEC_INPUT_NEXT_##v,
 typedef enum
 {
+#define _(v, s) IPSEC_INPUT_NEXT_##v,
   foreach_ipsec_input_next
 #undef _
     IPSEC_INPUT_N_NEXT,
diff --git a/src/vnet/ipsec/ipsec_sa.c b/src/vnet/ipsec/ipsec_sa.c
index e8a0159..afdecfe 100644
--- a/src/vnet/ipsec/ipsec_sa.c
+++ b/src/vnet/ipsec/ipsec_sa.c
@@ -17,6 +17,7 @@
 #include <vnet/ipsec/esp.h>
 #include <vnet/udp/udp.h>
 #include <vnet/fib/fib_table.h>
+#include <vnet/ipsec/ipsec_tun.h>
 
 /**
  * @brief
@@ -292,7 +293,7 @@
     {
       clib_warning ("sa_id %u used in policy", sa->id);
       /* sa used in policy */
-      return VNET_API_ERROR_SYSCALL_ERROR_1;
+      return VNET_API_ERROR_RSRC_IN_USE;
     }
   hash_unset (im->sa_index_by_sa_id, sa->id);
   err = ipsec_call_add_del_callbacks (im, sa, sa_index, 0);
@@ -313,12 +314,20 @@
   return 0;
 }
 
+void
+ipsec_sa_clear (index_t sai)
+{
+  vlib_zero_combined_counter (&ipsec_sa_counters, sai);
+}
+
 u8
 ipsec_is_sa_used (u32 sa_index)
 {
   ipsec_main_t *im = &ipsec_main;
+  ipsec_tun_protect_t *itp;
   ipsec_tunnel_if_t *t;
   ipsec_policy_t *p;
+  u32 sai;
 
   /* *INDENT-OFF* */
   pool_foreach(p, im->policies, ({
@@ -335,8 +344,20 @@
     if (t->output_sa_index == sa_index)
       return 1;
   }));
+
+  /* *INDENT-OFF* */
+  pool_foreach(itp, ipsec_protect_pool, ({
+    FOR_EACH_IPSEC_PROTECT_INPUT_SAI(itp, sai,
+    ({
+      if (sai == sa_index)
+        return 1;
+    }));
+    if (itp->itp_out_sa == sa_index)
+      return 1;
+  }));
   /* *INDENT-ON* */
 
+
   return 0;
 }
 
@@ -415,7 +436,7 @@
 }
 
 /*
- * Virtual function table registered by MPLS GRE tunnels
+ * Virtual function table registered by SAs
  * for participation in the FIB object graph.
  */
 const static fib_node_vft_t ipsec_sa_vft = {
diff --git a/src/vnet/ipsec/ipsec_sa.h b/src/vnet/ipsec/ipsec_sa.h
index 53035aa..2848267 100644
--- a/src/vnet/ipsec/ipsec_sa.h
+++ b/src/vnet/ipsec/ipsec_sa.h
@@ -93,7 +93,7 @@
   _ (4, IS_TUNNEL, "tunnel")                              \
   _ (8, IS_TUNNEL_V6, "tunnel-v6")                        \
   _ (16, UDP_ENCAP, "udp-encap")                          \
-  _ (32, IS_GRE, "GRE")                                   \
+  _ (32, IS_PROTECT, "Protect")                           \
   _ (64, IS_INBOUND, "inboud")                            \
   _ (128, IS_AEAD, "aead")                                \
 
@@ -183,6 +183,13 @@
   }
   foreach_ipsec_sa_flags
 #undef _
+#define _(a,v,s)                                                        \
+  always_inline int                                                     \
+  ipsec_sa_unset_##v (ipsec_sa_t *sa) {                                 \
+    return (sa->flags &= ~IPSEC_SA_FLAG_##v);                           \
+  }
+  foreach_ipsec_sa_flags
+#undef _
 /**
  * @brief
  * SA packet & bytes counters
@@ -205,6 +212,7 @@
 			 const ip46_address_t * tunnel_dst_addr,
 			 u32 * sa_index);
 extern u32 ipsec_sa_del (u32 id);
+extern void ipsec_sa_clear (index_t sai);
 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,
diff --git a/src/vnet/ipsec/ipsec_tun.c b/src/vnet/ipsec/ipsec_tun.c
new file mode 100644
index 0000000..a389cef
--- /dev/null
+++ b/src/vnet/ipsec/ipsec_tun.c
@@ -0,0 +1,398 @@
+/*
+ * ipsec_tun.h : IPSEC tunnel protection
+ *
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/ipsec/ipsec_tun.h>
+#include <vnet/ipsec/esp.h>
+#include <vnet/udp/udp.h>
+
+/**
+ * Pool of tunnel protection objects
+ */
+ipsec_tun_protect_t *ipsec_protect_pool;
+
+/**
+ * DB of protected tunnels
+ */
+typedef struct ipsec_protect_db_t_
+{
+  u32 *tunnels;
+  u32 count;
+} ipsec_protect_db_t;
+
+static ipsec_protect_db_t ipsec_protect_db;
+
+static int
+ipsec_tun_protect_feature_set (ipsec_tun_protect_t * itp, u8 enable)
+{
+  u32 sai = itp->itp_out_sa;
+  int is_ip4, is_l2, rv;
+
+  is_ip4 = ip46_address_is_ip4 (&itp->itp_tun.src);
+  is_l2 = itp->itp_flags & IPSEC_PROTECT_L2;
+
+  if (is_ip4)
+    {
+      if (is_l2)
+	rv = vnet_feature_enable_disable ("ethernet-output",
+					  "esp4-encrypt-tun",
+					  itp->itp_sw_if_index, enable,
+					  &sai, sizeof (sai));
+      else
+	rv = vnet_feature_enable_disable ("ip4-output",
+					  "esp4-encrypt-tun",
+					  itp->itp_sw_if_index, enable,
+					  &sai, sizeof (sai));
+    }
+  else
+    {
+      if (is_l2)
+	rv = vnet_feature_enable_disable ("ethernet-output",
+					  "esp6-encrypt-tun",
+					  itp->itp_sw_if_index, enable,
+					  &sai, sizeof (sai));
+      else
+	rv = vnet_feature_enable_disable ("ip6-output",
+					  "esp6-encrypt-tun",
+					  itp->itp_sw_if_index, enable,
+					  &sai, sizeof (sai));
+    }
+
+  ASSERT (!rv);
+  return (rv);
+}
+
+static void
+ipsec_tun_protect_db_add (ipsec_main_t * im, const ipsec_tun_protect_t * itp)
+{
+  const ipsec_sa_t *sa;
+  u32 sai;
+
+  /* *INDENT-OFF* */
+  FOR_EACH_IPSEC_PROTECT_INPUT_SAI(itp, sai,
+  ({
+      sa = ipsec_sa_get (sai);
+
+      ipsec_tun_lkup_result_t res = {
+        .tun_index = itp - ipsec_protect_pool,
+        .sa_index = sai,
+      };
+
+      /*
+       * The key is formed from the tunnel's destination
+       * as the packet lookup is done from the packet's source
+       */
+      if (ip46_address_is_ip4 (&itp->itp_crypto.dst))
+        {
+          ipsec4_tunnel_key_t key = {
+            .remote_ip = itp->itp_crypto.dst.ip4.as_u32,
+            .spi = clib_host_to_net_u32 (sa->spi),
+          };
+          hash_set (im->tun4_protect_by_key, key.as_u64, res.as_u64);
+        }
+      else
+        {
+          ipsec6_tunnel_key_t key = {
+            .remote_ip = itp->itp_crypto.dst.ip6,
+            .spi = clib_host_to_net_u32 (sa->spi),
+          };
+          hash_set_mem_alloc (&im->tun6_protect_by_key, &key, res.as_u64);
+        }
+  }))
+  /* *INDENT-ON* */
+}
+
+static void
+ipsec_tun_protect_db_remove (ipsec_main_t * im,
+			     const ipsec_tun_protect_t * itp)
+{
+  const ipsec_sa_t *sa;
+
+  /* *INDENT-OFF* */
+  FOR_EACH_IPSEC_PROTECT_INPUT_SA(itp, sa,
+  ({
+      if (ip46_address_is_ip4 (&itp->itp_crypto.dst))
+        {
+          ipsec4_tunnel_key_t key = {
+            .remote_ip = itp->itp_crypto.dst.ip4.as_u32,
+            .spi = clib_host_to_net_u32 (sa->spi),
+          };
+          hash_unset (im->tun4_protect_by_key, &key);
+        }
+      else
+        {
+          ipsec6_tunnel_key_t key = {
+            .remote_ip = itp->itp_crypto.dst.ip6,
+            .spi = clib_host_to_net_u32 (sa->spi),
+          };
+          hash_unset_mem_free (&im->tun6_protect_by_key, &key);
+        }
+  }))
+  /* *INDENT-ON* */
+}
+
+static void
+ipsec_tun_protect_config (ipsec_main_t * im,
+			  ipsec_tun_protect_t * itp, u32 sa_out, u32 * sas_in)
+{
+  ipsec_sa_t *sa;
+  u32 ii;
+
+  itp->itp_n_sa_in = vec_len (sas_in);
+  for (ii = 0; ii < itp->itp_n_sa_in; ii++)
+    itp->itp_in_sas[ii] = sas_in[ii];
+  itp->itp_out_sa = sa_out;
+
+  /* *INDENT-OFF* */
+  FOR_EACH_IPSEC_PROTECT_INPUT_SA(itp, sa,
+  ({
+    if (ipsec_sa_is_set_IS_TUNNEL (sa))
+      {
+        itp->itp_crypto.src = sa->tunnel_dst_addr;
+        itp->itp_crypto.dst = sa->tunnel_src_addr;
+        ipsec_sa_set_IS_PROTECT (sa);
+        itp->itp_flags |= IPSEC_PROTECT_ENCAPED;
+      }
+    else
+      {
+        itp->itp_crypto.src = itp->itp_tun.src;
+        itp->itp_crypto.dst = itp->itp_tun.dst;
+        itp->itp_flags &= ~IPSEC_PROTECT_ENCAPED;
+      }
+  }));
+  /* *INDENT-ON* */
+
+  /*
+   * add to the DB against each SA
+   */
+  ipsec_tun_protect_db_add (im, itp);
+
+  /*
+   * enable the encrypt feature for egress.
+   */
+  ipsec_tun_protect_feature_set (itp, 1);
+
+}
+
+static void
+ipsec_tun_protect_unconfig (ipsec_main_t * im, ipsec_tun_protect_t * itp)
+{
+  ipsec_sa_t *sa;
+
+  ipsec_tun_protect_feature_set (itp, 0);
+
+  /* *INDENT-OFF* */
+  FOR_EACH_IPSEC_PROTECT_INPUT_SA(itp, sa,
+  ({
+    ipsec_sa_unset_IS_PROTECT (sa);
+  }));
+  /* *INDENT-ON* */
+
+  ipsec_tun_protect_db_remove (im, itp);
+}
+
+index_t
+ipsec_tun_protect_find (u32 sw_if_index)
+{
+  if (vec_len (ipsec_protect_db.tunnels) < sw_if_index)
+    return (INDEX_INVALID);
+
+  return (ipsec_protect_db.tunnels[sw_if_index]);
+}
+
+int
+ipsec_tun_protect_update (u32 sw_if_index, u32 sa_out, u32 * sas_in)
+{
+  u32 itpi, ii;
+  ipsec_tun_protect_t *itp;
+  ipsec_main_t *im;
+  int rv;
+
+  rv = 0;
+  im = &ipsec_main;
+  vec_validate_init_empty (ipsec_protect_db.tunnels, sw_if_index,
+			   INDEX_INVALID);
+  itpi = ipsec_protect_db.tunnels[sw_if_index];
+
+  vec_foreach_index (ii, sas_in)
+  {
+    sas_in[ii] = ipsec_get_sa_index_by_sa_id (sas_in[ii]);
+    if (~0 == sas_in[ii])
+      {
+	rv = VNET_API_ERROR_INVALID_VALUE;
+	goto out;
+      }
+  }
+
+  sa_out = ipsec_get_sa_index_by_sa_id (sa_out);
+
+  if (~0 == sa_out)
+    {
+      rv = VNET_API_ERROR_INVALID_VALUE;
+      goto out;
+    }
+
+  if (INDEX_INVALID == itpi)
+    {
+      vnet_device_class_t *dev_class;
+      vnet_hw_interface_t *hi;
+      vnet_main_t *vnm;
+      u8 is_l2;
+
+      vnm = vnet_get_main ();
+      hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
+      dev_class = vnet_get_device_class (vnm, hi->dev_class_index);
+
+      if (NULL == dev_class->ip_tun_desc)
+	{
+	  rv = VNET_API_ERROR_INVALID_SW_IF_INDEX;
+	  goto out;
+	}
+
+      pool_get_zero (ipsec_protect_pool, itp);
+
+      itp->itp_sw_if_index = sw_if_index;
+      ipsec_protect_db.tunnels[sw_if_index] = itp - ipsec_protect_pool;
+      ipsec_protect_db.count++;
+
+      itp->itp_n_sa_in = vec_len (sas_in);
+      for (ii = 0; ii < itp->itp_n_sa_in; ii++)
+	itp->itp_in_sas[ii] = sas_in[ii];
+      itp->itp_out_sa = sa_out;
+
+      rv = dev_class->ip_tun_desc (sw_if_index,
+				   &itp->itp_tun.src,
+				   &itp->itp_tun.dst, &is_l2);
+
+      if (rv)
+	goto out;
+
+      if (is_l2)
+	itp->itp_flags |= IPSEC_PROTECT_L2;
+
+      /*
+       * add to the tunnel DB for ingress
+       *  - if the SA is in trasnport mode, then the packates will arrivw
+       *    with the IP src,dst of the protected tunnel, in which case we can
+       *    simply strip the IP header and hand the payload to the protocol
+       *    appropriate input handler
+       *  - if the SA is in tunnel mode then there are two IP headers present
+       *    one for the crytpo tunnel endpoints (described in the SA) and one
+       *    for the tunnel endpoints. The outer IP headers in the srriving
+       *    packets will have the crypto endpoints. So the DB needs to contain
+       *    the crpto endpoint. Once the crypto header is stripped, revealing,
+       *    the tunnel-IP we have 2 choices:
+       *     1) do a tunnel lookup based on the revealed header
+       *     2) skip the tunnel lookup and assume that the packet matches the
+       *        one that is protected here.
+       *    If we did 1) then we would allow our peer to use the SA for tunnel
+       *    X to inject traffic onto tunnel Y, this is not good. If we do 2)
+       *    then we don't verify that the peer is indeed using SA for tunnel
+       *    X and addressing tunnel X. So we take a compromise, once the SA
+       *    matches to tunnel X we veriy that the inner IP matches the value
+       *    of the tunnel we are protecting, else it's dropped.
+       */
+      ipsec_tun_protect_config (im, itp, sa_out, sas_in);
+
+      if (1 == hash_elts (im->tun4_protect_by_key))
+	ip4_register_protocol (IP_PROTOCOL_IPSEC_ESP,
+			       ipsec4_tun_input_node.index);
+      if (1 == hash_elts (im->tun6_protect_by_key))
+	ip6_register_protocol (IP_PROTOCOL_IPSEC_ESP,
+			       ipsec6_tun_input_node.index);
+    }
+  else
+    {
+      /* updating SAs only */
+      itp = pool_elt_at_index (ipsec_protect_pool, itpi);
+
+      ipsec_tun_protect_unconfig (im, itp);
+      ipsec_tun_protect_config (im, itp, sa_out, sas_in);
+    }
+
+  vec_free (sas_in);
+out:
+  return (rv);
+}
+
+int
+ipsec_tun_protect_del (u32 sw_if_index)
+{
+  ipsec_tun_protect_t *itp;
+  ipsec_main_t *im;
+  index_t itpi;
+
+  im = &ipsec_main;
+
+  vec_validate_init_empty (ipsec_protect_db.tunnels, sw_if_index,
+			   INDEX_INVALID);
+  itpi = ipsec_protect_db.tunnels[sw_if_index];
+
+  if (INDEX_INVALID == itpi)
+    return (VNET_API_ERROR_NO_SUCH_ENTRY);
+
+  itp = ipsec_tun_protect_get (itpi);
+  ipsec_tun_protect_unconfig (im, itp);
+
+  ipsec_protect_db.tunnels[itp->itp_sw_if_index] = INDEX_INVALID;
+
+  pool_put (ipsec_protect_pool, itp);
+
+  /* if (0 == hash_elts (im->tun4_protect_by_key)) */
+  /*   ip4_unregister_protocol (IP_PROTOCOL_IPSEC_ESP); */
+  /* if (0 == hash_elts (im->tun6_protect_by_key)) */
+  /*   ip6_unregister_protocol (IP_PROTOCOL_IPSEC_ESP); */
+
+  return (0);
+}
+
+void
+ipsec_tun_protect_walk (ipsec_tun_protect_walk_cb_t fn, void *ctx)
+{
+  index_t itpi;
+
+  /* *INDENT-OFF* */
+  pool_foreach_index(itpi, ipsec_protect_pool,
+  ({
+    fn (itpi, ctx);
+  }));
+  /* *INDENT-ON* */
+}
+
+clib_error_t *
+ipsec_tunnel_protect_init (vlib_main_t * vm)
+{
+  ipsec_main_t *im;
+
+  im = &ipsec_main;
+  im->tun6_protect_by_key = hash_create_mem (0,
+					     sizeof (ipsec6_tunnel_key_t),
+					     sizeof (u64));
+  im->tun4_protect_by_key = hash_create (0, sizeof (u64));
+
+  return 0;
+}
+
+VLIB_INIT_FUNCTION (ipsec_tunnel_protect_init);
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/ipsec/ipsec_tun.h b/src/vnet/ipsec/ipsec_tun.h
new file mode 100644
index 0000000..be5cef9
--- /dev/null
+++ b/src/vnet/ipsec/ipsec_tun.h
@@ -0,0 +1,114 @@
+/*
+ * ipsec_tun.h : IPSEC tunnel protection
+ *
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/ipsec/ipsec.h>
+
+typedef enum ipsec_protect_flags_t_
+{
+  IPSEC_PROTECT_L2 = (1 << 0),
+  IPSEC_PROTECT_ENCAPED = (1 << 1),
+} __clib_packed ipsec_protect_flags_t;
+
+typedef struct ipsec_ep_t_
+{
+  ip46_address_t src;
+  ip46_address_t dst;
+} ipsec_ep_t;
+
+typedef struct ipsec_tun_protect_t_
+{
+  CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
+  u32 itp_out_sa;
+
+  /* not using a vector since we want the memory inline
+   * with this struct */
+  u32 itp_n_sa_in;
+  u32 itp_in_sas[4];
+
+  u32 itp_sw_if_index;
+
+  ipsec_ep_t itp_crypto;
+
+  ipsec_protect_flags_t itp_flags;
+
+  ipsec_ep_t itp_tun;
+
+} ipsec_tun_protect_t;
+
+#define FOR_EACH_IPSEC_PROTECT_INPUT_SAI(_itp, _sai, body) \
+{                                                          \
+  u32 __ii;                                                \
+  for (__ii = 0; __ii < _itp->itp_n_sa_in; __ii++) {       \
+    _sai = itp->itp_in_sas[__ii];                          \
+    body;                                                  \
+  }                                                        \
+}
+#define FOR_EACH_IPSEC_PROTECT_INPUT_SA(_itp, _sa, body)   \
+{                                                          \
+  u32 __ii;                                                \
+  for (__ii = 0; __ii < _itp->itp_n_sa_in; __ii++) {       \
+    _sa = ipsec_sa_get(itp->itp_in_sas[__ii]);             \
+    body;                                                  \
+  }                                                        \
+}
+
+extern int ipsec_tun_protect_update (u32 sw_if_index, u32 sa_out,
+				     u32 sa_ins[2]);
+extern int ipsec_tun_protect_del (u32 sw_if_index);
+
+typedef walk_rc_t (*ipsec_tun_protect_walk_cb_t) (index_t itpi, void *arg);
+extern void ipsec_tun_protect_walk (ipsec_tun_protect_walk_cb_t fn,
+				    void *cttx);
+extern index_t ipsec_tun_protect_find (u32 sw_if_index);
+
+extern u8 *format_ipsec_tun_protect (u8 * s, va_list * args);
+
+// FIXME
+extern vlib_node_registration_t ipsec4_tun_input_node;
+extern vlib_node_registration_t ipsec6_tun_input_node;
+
+/*
+ * DP API
+ */
+extern ipsec_tun_protect_t *ipsec_protect_pool;
+
+typedef struct ipsec_tun_lkup_result_t_
+{
+  union
+  {
+    struct
+    {
+      u32 tun_index;
+      u32 sa_index;
+    };
+    u64 as_u64;
+  };
+} ipsec_tun_lkup_result_t;
+
+always_inline ipsec_tun_protect_t *
+ipsec_tun_protect_get (u32 index)
+{
+  return (pool_elt_at_index (ipsec_protect_pool, index));
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/ipsec/ipsec_tun_in.c b/src/vnet/ipsec/ipsec_tun_in.c
new file mode 100644
index 0000000..2ce1691
--- /dev/null
+++ b/src/vnet/ipsec/ipsec_tun_in.c
@@ -0,0 +1,436 @@
+/*
+ * ipsec_tun_protect_in.c : IPSec interface input node
+ *
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/api_errno.h>
+#include <vnet/ip/ip.h>
+
+#include <vnet/ipsec/ipsec.h>
+#include <vnet/ipsec/esp.h>
+#include <vnet/ipsec/ipsec_io.h>
+#include <vnet/ipsec/ipsec_punt.h>
+#include <vnet/ipsec/ipsec_tun.h>
+#include <vnet/ip/ip4_input.h>
+
+/* Statistics (not really errors) */
+#define foreach_ipsec_tun_protect_input_error                     \
+  _(RX, "good packets received")                                  \
+  _(DISABLED, "ipsec packets received on disabled interface")     \
+  _(NO_TUNNEL, "no matching tunnel")                              \
+  _(TUNNEL_MISMATCH, "SPI-tunnel mismatch")                       \
+  _(SPI_0, "SPI 0")
+
+static char *ipsec_tun_protect_input_error_strings[] = {
+#define _(sym,string) string,
+  foreach_ipsec_tun_protect_input_error
+#undef _
+};
+
+typedef enum
+{
+#define _(sym,str) IPSEC_TUN_PROTECT_INPUT_ERROR_##sym,
+  foreach_ipsec_tun_protect_input_error
+#undef _
+    IPSEC_TUN_PROTECT_INPUT_N_ERROR,
+} ipsec_tun_protect_input_error_t;
+
+typedef enum ipsec_tun_next_t_
+{
+#define _(v, s) IPSEC_TUN_PROTECT_NEXT_##v,
+  foreach_ipsec_input_next
+#undef _
+    IPSEC_TUN_PROTECT_NEXT_DECRYPT,
+  IPSEC_TUN_PROTECT_N_NEXT,
+} ipsec_tun_next_t;
+
+typedef struct
+{
+  u32 spi;
+  u32 seq;
+} ipsec_tun_protect_input_trace_t;
+
+static u8 *
+format_ipsec_tun_protect_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 *);
+  ipsec_tun_protect_input_trace_t *t =
+    va_arg (*args, ipsec_tun_protect_input_trace_t *);
+
+  s = format (s, "IPSec: spi %u seq %u", t->spi, t->seq);
+  return s;
+}
+
+always_inline u16
+ipsec_ip4_if_no_tunnel (vlib_node_runtime_t * node,
+			vlib_buffer_t * b,
+			const esp_header_t * esp, const ip4_header_t * ip4)
+{
+  if (PREDICT_FALSE (0 == esp->spi))
+    {
+      b->error = node->errors[IPSEC_TUN_PROTECT_INPUT_ERROR_SPI_0];
+      b->punt_reason = ipsec_punt_reason[(ip4->protocol == IP_PROTOCOL_UDP ?
+					  IPSEC_PUNT_IP4_SPI_UDP_0 :
+					  IPSEC_PUNT_IP4_SPI_0)];
+    }
+  else
+    {
+      b->error = node->errors[IPSEC_TUN_PROTECT_INPUT_ERROR_NO_TUNNEL];
+      b->punt_reason = ipsec_punt_reason[IPSEC_PUNT_IP4_NO_SUCH_TUNNEL];
+    }
+  return IPSEC_INPUT_NEXT_PUNT;
+}
+
+always_inline u16
+ipsec_ip6_if_no_tunnel (vlib_node_runtime_t * node,
+			vlib_buffer_t * b, const esp_header_t * esp)
+{
+  if (PREDICT_FALSE (0 == esp->spi))
+    {
+      b->error = node->errors[IPSEC_TUN_PROTECT_INPUT_ERROR_NO_TUNNEL];
+      b->punt_reason = ipsec_punt_reason[IPSEC_PUNT_IP6_SPI_0];
+    }
+  else
+    {
+      b->error = node->errors[IPSEC_TUN_PROTECT_INPUT_ERROR_NO_TUNNEL];
+      b->punt_reason = ipsec_punt_reason[IPSEC_PUNT_IP6_NO_SUCH_TUNNEL];
+    }
+  return (IPSEC_INPUT_NEXT_PUNT);
+}
+
+always_inline uword
+ipsec_tun_protect_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
+				vlib_frame_t * from_frame, int is_ip6)
+{
+  ipsec_main_t *im = &ipsec_main;
+  vnet_main_t *vnm = im->vnet_main;
+  vnet_interface_main_t *vim = &vnm->interface_main;
+
+  int is_trace = node->flags & VLIB_NODE_FLAG_TRACE;
+  u32 thread_index = vm->thread_index;
+
+  u32 n_left_from, *from;
+  u16 nexts[VLIB_FRAME_SIZE], *next;
+  vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
+
+  from = vlib_frame_vector_args (from_frame);
+  n_left_from = from_frame->n_vectors;
+
+  vlib_get_buffers (vm, from, bufs, n_left_from);
+  b = bufs;
+  next = nexts;
+
+  clib_memset_u16 (nexts, im->esp4_decrypt_next_index, n_left_from);
+
+  u64 n_bytes = 0, n_packets = 0;
+  u32 n_disabled = 0, n_no_tunnel = 0;
+
+  u32 last_sw_if_index = ~0;
+  ipsec_tun_lkup_result_t last_result = {
+    .tun_index = ~0
+  };
+  ipsec4_tunnel_key_t last_key4;
+  ipsec6_tunnel_key_t last_key6;
+
+  vlib_combined_counter_main_t *rx_counter;
+  vlib_combined_counter_main_t *drop_counter;
+  ipsec_tun_protect_t *itp0;
+
+  if (is_ip6)
+    clib_memset (&last_key6, 0xff, sizeof (last_key6));
+  else
+    last_key4.as_u64 = ~0;
+
+  rx_counter = vim->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX;
+  drop_counter = vim->combined_sw_if_counters + VNET_INTERFACE_COUNTER_DROP;
+
+  while (n_left_from > 0)
+    {
+      u32 sw_if_index0, len0, hdr_sz0;
+      ipsec_tun_lkup_result_t itr0;
+      ipsec4_tunnel_key_t key40;
+      ipsec6_tunnel_key_t key60;
+      ip4_header_t *ip40;
+      ip6_header_t *ip60;
+      esp_header_t *esp0;
+
+      ip40 = vlib_buffer_get_current (b[0]);
+
+      if (is_ip6)
+	{
+	  ip60 = (ip6_header_t *) ip40;
+	  esp0 = (esp_header_t *) (ip60 + 1);
+	  hdr_sz0 = sizeof (ip6_header_t);
+	}
+      else
+	{
+	  /* NAT UDP port 4500 case, don't advance any more */
+	  if (ip40->protocol == IP_PROTOCOL_UDP)
+	    {
+	      esp0 =
+		(esp_header_t *) ((u8 *) ip40 + ip4_header_bytes (ip40) +
+				  sizeof (udp_header_t));
+	      hdr_sz0 = 0;
+	    }
+	  else
+	    {
+	      esp0 = (esp_header_t *) ((u8 *) ip40 + ip4_header_bytes (ip40));
+	      hdr_sz0 = ip4_header_bytes (ip40);
+	    }
+	}
+
+      /* stats for the tunnel include all the data after the IP header
+         just like a norml IP-IP tunnel */
+      vlib_buffer_advance (b[0], hdr_sz0);
+      len0 = vlib_buffer_length_in_chain (vm, b[0]);
+
+      if (is_ip6)
+	{
+	  key60.remote_ip = ip60->src_address;
+	  key60.spi = esp0->spi;
+
+	  if (memcmp (&key60, &last_key6, sizeof (last_key6)) == 0)
+	    {
+	      itr0 = last_result;
+	    }
+	  else
+	    {
+	      uword *p = hash_get_mem (im->tun6_protect_by_key, &key60);
+	      if (p)
+		{
+		  itr0.as_u64 = p[0];
+		  last_result = itr0;
+		  clib_memcpy_fast (&last_key6, &key60, sizeof (key60));
+		}
+	      else
+		{
+		  next[0] = ipsec_ip6_if_no_tunnel (node, b[0], esp0);
+		  n_no_tunnel++;
+		  goto trace00;
+		}
+	    }
+	}
+      else
+	{
+	  key40.remote_ip = ip40->src_address.as_u32;
+	  key40.spi = esp0->spi;
+
+	  if (key40.as_u64 == last_key4.as_u64)
+	    {
+	      itr0 = last_result;
+	    }
+	  else
+	    {
+	      uword *p = hash_get (im->tun4_protect_by_key, key40.as_u64);
+	      if (p)
+		{
+		  itr0.as_u64 = p[0];
+		  last_result = itr0;
+		  last_key4.as_u64 = key40.as_u64;
+		}
+	      else
+		{
+		  next[0] = ipsec_ip4_if_no_tunnel (node, b[0], esp0, ip40);
+		  n_no_tunnel++;
+		  goto trace00;
+		}
+	    }
+	}
+
+      itp0 = pool_elt_at_index (ipsec_protect_pool, itr0.tun_index);
+      vnet_buffer (b[0])->ipsec.sad_index = itr0.sa_index;
+      vnet_buffer (b[0])->ipsec.protect_index = itr0.tun_index;
+
+      sw_if_index0 = itp0->itp_sw_if_index;
+      vnet_buffer (b[0])->sw_if_index[VLIB_RX] = sw_if_index0;
+
+      if (PREDICT_FALSE (!vnet_sw_interface_is_admin_up (vnm, sw_if_index0)))
+	{
+	  vlib_increment_combined_counter
+	    (drop_counter, thread_index, sw_if_index0, 1, len0);
+	  n_disabled++;
+	  b[0]->error = node->errors[IPSEC_TUN_PROTECT_INPUT_ERROR_DISABLED];
+	  next[0] = IPSEC_INPUT_NEXT_DROP;
+	  goto trace00;
+	}
+      else
+	{
+	  if (PREDICT_TRUE (sw_if_index0 == last_sw_if_index))
+	    {
+	      n_packets++;
+	      n_bytes += len0;
+	    }
+	  else
+	    {
+	      if (n_packets && !(itp0->itp_flags & IPSEC_PROTECT_ENCAPED))
+		{
+		  vlib_increment_combined_counter
+		    (rx_counter, thread_index, last_sw_if_index,
+		     n_packets, n_bytes);
+		}
+
+	      last_sw_if_index = sw_if_index0;
+	      n_packets = 1;
+	      n_bytes = len0;
+	    }
+
+	  /*
+	   * compare the packet's outer IP headers to that of the tunnels
+	   */
+	  if (is_ip6)
+	    {
+	      if (PREDICT_FALSE
+		  (!ip46_address_is_equal_v6
+		   (&itp0->itp_crypto.dst, &ip60->src_address)
+		   || !ip46_address_is_equal_v6 (&itp0->itp_crypto.src,
+						 &ip60->dst_address)))
+		{
+		  b[0]->error =
+		    node->errors
+		    [IPSEC_TUN_PROTECT_INPUT_ERROR_TUNNEL_MISMATCH];
+		  next[0] = IPSEC_INPUT_NEXT_DROP;
+		  goto trace00;
+		}
+	    }
+	  else
+	    {
+	      if (PREDICT_FALSE
+		  (!ip46_address_is_equal_v4
+		   (&itp0->itp_crypto.dst, &ip40->src_address)
+		   || !ip46_address_is_equal_v4 (&itp0->itp_crypto.src,
+						 &ip40->dst_address)))
+		{
+		  b[0]->error =
+		    node->errors
+		    [IPSEC_TUN_PROTECT_INPUT_ERROR_TUNNEL_MISMATCH];
+		  next[0] = IPSEC_INPUT_NEXT_DROP;
+		  goto trace00;
+		}
+	    }
+
+	  /*
+	   * There are two encap possibilities
+	   * 1) the tunnel and ths SA are prodiving encap, i.e. it's
+	   *   MAC | SA-IP | TUN-IP | ESP | PAYLOAD
+	   * implying the SA is in tunnel mode (on a tunnel interface)
+	   * 2) only the tunnel provides encap
+	   *   MAC | TUN-IP | ESP | PAYLOAD
+	   * implying the SA is in transport mode.
+	   *
+	   * For 2) we need only strip the tunnel encap and we're good.
+	   *  since the tunnel and crypto ecnap (int the tun=protect
+	   * object) are the same and we verified above that these match
+	   * for 1) we need to strip the SA-IP outer headers, to
+	   * reveal the tunnel IP and then check that this matches
+	   * the configured tunnel. this we can;t do here since it
+	   * involves a lookup in the per-tunnel-type DB - so ship
+	   * the packet to the tunnel-types provided node to do that
+	   */
+	  next[0] = IPSEC_TUN_PROTECT_NEXT_DECRYPT;
+	}
+    trace00:
+      if (PREDICT_FALSE (is_trace))
+	{
+	  if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
+	    {
+	      ipsec_tun_protect_input_trace_t *tr =
+		vlib_add_trace (vm, node, b[0], sizeof (*tr));
+	      tr->spi = clib_host_to_net_u32 (esp0->spi);
+	      tr->seq = clib_host_to_net_u32 (esp0->seq);
+	    }
+	}
+
+      /* next */
+      b += 1;
+      next += 1;
+      n_left_from -= 1;
+    }
+
+  if (n_packets && !(itp0->itp_flags & IPSEC_PROTECT_ENCAPED))
+    {
+      vlib_increment_combined_counter (rx_counter,
+				       thread_index,
+				       last_sw_if_index, n_packets, n_bytes);
+    }
+
+  vlib_node_increment_counter (vm, node->node_index,
+			       IPSEC_TUN_PROTECT_INPUT_ERROR_RX,
+			       from_frame->n_vectors - (n_disabled +
+							n_no_tunnel));
+
+  vlib_buffer_enqueue_to_next (vm, node, from, nexts, from_frame->n_vectors);
+
+  return from_frame->n_vectors;
+}
+
+VLIB_NODE_FN (ipsec4_tun_input_node) (vlib_main_t * vm,
+				      vlib_node_runtime_t * node,
+				      vlib_frame_t * from_frame)
+{
+  return ipsec_tun_protect_input_inline (vm, node, from_frame,
+					 0 /* is_ip6 */ );
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (ipsec4_tun_input_node) = {
+  .name = "ipsec4-tun-input",
+  .vector_size = sizeof (u32),
+  .format_trace = format_ipsec_tun_protect_input_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = ARRAY_LEN(ipsec_tun_protect_input_error_strings),
+  .error_strings = ipsec_tun_protect_input_error_strings,
+  .n_next_nodes = IPSEC_TUN_PROTECT_N_NEXT,
+  .next_nodes = {
+    [IPSEC_TUN_PROTECT_NEXT_DROP] = "ip4-drop",
+    [IPSEC_TUN_PROTECT_NEXT_PUNT] = "punt-dispatch",
+    [IPSEC_TUN_PROTECT_NEXT_DECRYPT] = "esp4-decrypt-tun",
+  }
+};
+/* *INDENT-ON* */
+
+VLIB_NODE_FN (ipsec6_tun_input_node) (vlib_main_t * vm,
+				      vlib_node_runtime_t * node,
+				      vlib_frame_t * from_frame)
+{
+  return ipsec_tun_protect_input_inline (vm, node, from_frame,
+					 1 /* is_ip6 */ );
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (ipsec6_tun_input_node) = {
+  .name = "ipsec6-tun-input",
+  .vector_size = sizeof (u32),
+  .format_trace = format_ipsec_tun_protect_input_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = ARRAY_LEN(ipsec_tun_protect_input_error_strings),
+  .error_strings = ipsec_tun_protect_input_error_strings,
+  .n_next_nodes = IPSEC_TUN_PROTECT_N_NEXT,
+  .next_nodes = {
+    [IPSEC_TUN_PROTECT_NEXT_DROP] = "ip6-drop",
+    [IPSEC_TUN_PROTECT_NEXT_PUNT] = "punt-dispatch",
+    [IPSEC_TUN_PROTECT_NEXT_DECRYPT] = "esp6-decrypt-tun",
+  }
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/vnet_all_api_h.h b/src/vnet/vnet_all_api_h.h
index 31d9c34..5dcdad4 100644
--- a/src/vnet/vnet_all_api_h.h
+++ b/src/vnet/vnet_all_api_h.h
@@ -48,7 +48,6 @@
 #include <vnet/vxlan-gpe/vxlan_gpe.api.h>
 #include <vnet/bfd/bfd.api.h>
 #include <vnet/ipsec/ipsec.api.h>
-#include <vnet/ipsec-gre/ipsec_gre.api.h>
 #include <vnet/lisp-cp/lisp.api.h>
 #include <vnet/lisp-gpe/lisp_gpe.api.h>
 #include <vnet/lisp-cp/one.api.h>
diff --git a/src/vpp/api/custom_dump.c b/src/vpp/api/custom_dump.c
index d754c3a..494e1ab 100644
--- a/src/vpp/api/custom_dump.c
+++ b/src/vpp/api/custom_dump.c
@@ -3245,40 +3245,6 @@
   FINISH;
 }
 
-static void *vl_api_ipsec_gre_tunnel_add_del_t_print
-  (vl_api_ipsec_gre_tunnel_add_del_t * mp, void *handle)
-{
-  u8 *s;
-
-  s = format (0, "SCRIPT: ipsec_gre_tunnel_add_del ");
-
-  s = format (s, "dst %U ", format_vl_api_ip4_address, mp->tunnel.dst);
-
-  s = format (s, "src %U ", format_vl_api_ip4_address, mp->tunnel.src);
-
-  s = format (s, "local_sa %d ", ntohl (mp->tunnel.local_sa_id));
-
-  s = format (s, "remote_sa %d ", ntohl (mp->tunnel.remote_sa_id));
-
-  if (mp->is_add == 0)
-    s = format (s, "del ");
-
-  FINISH;
-}
-
-static void *vl_api_ipsec_gre_tunnel_dump_t_print
-  (vl_api_ipsec_gre_tunnel_dump_t * mp, void *handle)
-{
-  u8 *s;
-
-  s = format (0, "SCRIPT: ipsec_gre_tunnel_dump ");
-
-  if (mp->sw_if_index != ~0)
-    s = format (s, "sw_if_index %d ", ntohl (mp->sw_if_index));
-
-  FINISH;
-}
-
 static void *vl_api_l2_interface_pbb_tag_rewrite_t_print
   (vl_api_l2_interface_pbb_tag_rewrite_t * mp, void *handle)
 {
@@ -3843,8 +3809,6 @@
 _(LISP_RLOC_PROBE_ENABLE_DISABLE, lisp_rloc_probe_enable_disable)       \
 _(LISP_MAP_REGISTER_ENABLE_DISABLE, lisp_map_register_enable_disable)   \
 _(IPSEC_TUNNEL_IF_ADD_DEL, ipsec_tunnel_if_add_del)                     \
-_(IPSEC_GRE_TUNNEL_ADD_DEL, ipsec_gre_tunnel_add_del)                   \
-_(IPSEC_GRE_TUNNEL_DUMP, ipsec_gre_tunnel_dump)                         \
 _(DELETE_SUBIF, delete_subif)                                           \
 _(L2_INTERFACE_PBB_TAG_REWRITE, l2_interface_pbb_tag_rewrite)           \
 _(SET_PUNT, set_punt)                                                   \
diff --git a/src/vpp/api/vpe.api b/src/vpp/api/vpe.api
index 94732bb..735b31d 100644
--- a/src/vpp/api/vpe.api
+++ b/src/vpp/api/vpe.api
@@ -38,7 +38,6 @@
  * L2TP APIs: see .../src/vnet/l2tp/{l2tp.api, l2tp_api.c}
  * BFD APIs: see .../src/vnet/bfd/{bfd.api, bfd_api.c}
  * IPSEC APIs: see .../src/vnet/ipsec/{ipsec.api, ipsec_api.c}
- * IPSEC-GRE APIs: see .../src/vnet/ipsec-gre/{ipsec_gre.api, ipsec_gre_api.c}
  * LISP APIs: see .../src/vnet/lisp/{lisp.api, lisp_api.c}
  * LISP-GPE APIs: see .../src/vnet/lisp-gpe/{lisp_gpe.api, lisp_gpe_api.c}
  * SESSION APIs: .../vnet/session/{session.api session_api.c}