gbp: Ownership of dynamically created vxlan-gbp tunnels managed via gbp_itf

Type: fix

This solves the ownership of vxlan-gbp tunnels. When the last reference of these goes away they need to be deleted. Currently there are two owners; gbp_itf via gef_itf and the lock held by the gbp_endpoint_location_t. The problem is that the
loc removes its reference whilst the fwd still holds the gbp_itf, and things go wrong.
This change moves the lifecycle management of the vxlan-gbp tunnel to the gbp_itf. When the last lock of the gbp_itf goes, so does the tunnel. now both the EP's loc and fwd can hold a lock on the gbp_itf and it's only removed when required.
The other change is the management of the 'user' of the gbp_itf. Since each user can enable and disable different features, it's the job of the gbp_itf to apply the combined set. determining a unique 'uesr' from the caller was near impossible, so I moved that to the gbp_itf, and return the allocated user, hence the 'handle' that encodes both user and interface.

The hash table maps from sw_if_index to pool index.

Change-Id: I4c7bf4c0e5dcf33d1c545f262365e69151febcf4
Signed-off-by: Neale Ranns <nranns@cisco.com>
diff --git a/src/plugins/gbp/gbp_api.c b/src/plugins/gbp/gbp_api.c
index 7c7026a..0945547 100644
--- a/src/plugins/gbp/gbp_api.c
+++ b/src/plugins/gbp/gbp_api.c
@@ -240,7 +240,8 @@
     }
   else
     {
-      mp->endpoint.sw_if_index = ntohl (gef->gef_itf);
+      mp->endpoint.sw_if_index =
+	ntohl (gbp_itf_get_sw_if_index (gef->gef_itf));
     }
   mp->endpoint.sclass = ntohs (ge->ge_fwd.gef_sclass);
   mp->endpoint.n_ips = n_ips;
@@ -588,7 +589,8 @@
   mp->bd.rd_id = ntohl (gr->grd_id);
   mp->bd.bvi_sw_if_index = ntohl (gb->gb_bvi_sw_if_index);
   mp->bd.uu_fwd_sw_if_index = ntohl (gb->gb_uu_fwd_sw_if_index);
-  mp->bd.bm_flood_sw_if_index = ntohl (gb->gb_bm_flood_sw_if_index);
+  mp->bd.bm_flood_sw_if_index =
+    ntohl (gbp_itf_get_sw_if_index (gb->gb_bm_flood_itf));
 
   vl_api_send_msg (ctx->reg, (u8 *) mp);
 
@@ -763,7 +765,7 @@
   mp->ext_itf.flags = ntohl (gx->gx_flags);
   mp->ext_itf.bd_id = ntohl (gbp_bridge_domain_get_bd_id (gx->gx_bd));
   mp->ext_itf.rd_id = ntohl (gbp_route_domain_get_rd_id (gx->gx_rd));
-  mp->ext_itf.sw_if_index = ntohl (gx->gx_itf);
+  mp->ext_itf.sw_if_index = ntohl (gbp_itf_get_sw_if_index (gx->gx_itf));
 
   vl_api_send_msg (ctx->reg, (u8 *) mp);
 
diff --git a/src/plugins/gbp/gbp_bridge_domain.c b/src/plugins/gbp/gbp_bridge_domain.c
index 3e6d7ef..f2a56d7 100644
--- a/src/plugins/gbp/gbp_bridge_domain.c
+++ b/src/plugins/gbp/gbp_bridge_domain.c
@@ -17,6 +17,7 @@
 #include <plugins/gbp/gbp_route_domain.h>
 #include <plugins/gbp/gbp_endpoint.h>
 #include <plugins/gbp/gbp_learn.h>
+#include <plugins/gbp/gbp_itf.h>
 
 #include <vnet/dpo/dvr_dpo.h>
 #include <vnet/fib/fib_table.h>
@@ -149,13 +150,14 @@
   vnet_main_t *vnm = vnet_get_main ();
 
   if (NULL != gb)
-    s = format (s, "[%d] bd:[%d,%d], bvi:%U uu-flood:%U flags:%U locks:%d",
-		gb - gbp_bridge_domain_pool,
-		gb->gb_bd_id,
-		gb->gb_bd_index,
-		format_vnet_sw_if_index_name, vnm, gb->gb_bvi_sw_if_index,
-		format_vnet_sw_if_index_name, vnm, gb->gb_uu_fwd_sw_if_index,
-		format_gbp_bridge_domain_flags, gb->gb_flags, gb->gb_locks);
+    s =
+      format (s,
+	      "[%d] bd:[%d,%d], bvi:%U uu-flood:%U bm-flood:%U flags:%U locks:%d",
+	      gb - gbp_bridge_domain_pool, gb->gb_bd_id, gb->gb_bd_index,
+	      format_vnet_sw_if_index_name, vnm, gb->gb_bvi_sw_if_index,
+	      format_vnet_sw_if_index_name, vnm, gb->gb_uu_fwd_sw_if_index,
+	      format_gbp_itf_hdl, gb->gb_bm_flood_itf,
+	      format_gbp_bridge_domain_flags, gb->gb_flags, gb->gb_locks);
   else
     s = format (s, "NULL");
 
@@ -174,28 +176,6 @@
   return (s);
 }
 
-void
-gbp_bridge_domain_itf_add (u32 sw_if_index, u32 bd_index,
-			   l2_bd_port_type_t type)
-{
-  set_int_l2_mode (vlib_get_main (), vnet_get_main (), MODE_L2_BRIDGE,
-		   sw_if_index, bd_index, type, 0, 0);
-  /*
-   * adding an interface to the bridge enable learning on the
-   * interface. Disable learning on the interface by default for gbp
-   * interfaces
-   */
-  l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_LEARN, 0);
-}
-
-void
-gbp_bridge_domain_itf_del (u32 sw_if_index, u32 bd_index,
-			   l2_bd_port_type_t type)
-{
-  set_int_l2_mode (vlib_get_main (), vnet_get_main (), MODE_L3, sw_if_index,
-		   bd_index, type, 0, 0);
-}
-
 int
 gbp_bridge_domain_add_and_lock (u32 bd_id,
 				u32 rd_id,
@@ -228,11 +208,12 @@
       pool_get (gbp_bridge_domain_pool, gb);
       memset (gb, 0, sizeof (*gb));
 
+      gbi = gb - gbp_bridge_domain_pool;
       gb->gb_bd_id = bd_id;
       gb->gb_bd_index = bd_index;
       gb->gb_uu_fwd_sw_if_index = uu_fwd_sw_if_index;
       gb->gb_bvi_sw_if_index = bvi_sw_if_index;
-      gb->gb_bm_flood_sw_if_index = bm_flood_sw_if_index;
+      gbp_itf_hdl_reset (&gb->gb_bm_flood_itf);
       gb->gb_locks = 1;
       gb->gb_flags = flags;
       gb->gb_rdi = gbp_route_domain_find_and_lock (rd_id);
@@ -247,20 +228,21 @@
       /*
        * Set the BVI and uu-flood interfaces into the BD
        */
-      gbp_bridge_domain_itf_add (gb->gb_bvi_sw_if_index, bd_index,
+      gbp_bridge_domain_itf_add (gbi, gb->gb_bvi_sw_if_index,
 				 L2_BD_PORT_TYPE_BVI);
 
-      if ((!(flags & GBP_BD_FLAG_UU_FWD_DROP)
-	   || (flags & GBP_BD_FLAG_UCAST_ARP))
-	  && ~0 != gb->gb_uu_fwd_sw_if_index)
-	gbp_bridge_domain_itf_add (gb->gb_uu_fwd_sw_if_index, bd_index,
+      if ((!(flags & GBP_BD_FLAG_UU_FWD_DROP) ||
+	   (flags & GBP_BD_FLAG_UCAST_ARP)) &&
+	  ~0 != gb->gb_uu_fwd_sw_if_index)
+	gbp_bridge_domain_itf_add (gbi, gb->gb_uu_fwd_sw_if_index,
 				   L2_BD_PORT_TYPE_UU_FWD);
-      if (!(flags & GBP_BD_FLAG_MCAST_DROP)
-	  && ~0 != gb->gb_bm_flood_sw_if_index)
+
+      if (!(flags & GBP_BD_FLAG_MCAST_DROP) && ~0 != bm_flood_sw_if_index)
 	{
-	  gbp_bridge_domain_itf_add (gb->gb_bm_flood_sw_if_index, bd_index,
-				     L2_BD_PORT_TYPE_NORMAL);
-	  gbp_learn_enable (gb->gb_bm_flood_sw_if_index, GBP_LEARN_MODE_L2);
+	  gb->gb_bm_flood_itf =
+	    gbp_itf_l2_add_and_lock (bm_flood_sw_if_index, gbi);
+	  gbp_itf_l2_set_input_feature (gb->gb_bm_flood_itf,
+					L2INPUT_FEAT_GBP_LEARN);
 	}
 
       /*
@@ -297,11 +279,41 @@
 }
 
 void
-gbp_bridge_domain_unlock (index_t index)
+gbp_bridge_domain_itf_add (index_t gbdi,
+			   u32 sw_if_index, l2_bd_port_type_t type)
 {
   gbp_bridge_domain_t *gb;
 
-  gb = gbp_bridge_domain_get (index);
+  gb = gbp_bridge_domain_get (gbdi);
+
+  set_int_l2_mode (vlib_get_main (), vnet_get_main (), MODE_L2_BRIDGE,
+		   sw_if_index, gb->gb_bd_index, type, 0, 0);
+  /*
+   * adding an interface to the bridge enables learning on the
+   * interface. Disable learning on the interface by default for gbp
+   * interfaces
+   */
+  l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_LEARN, 0);
+}
+
+void
+gbp_bridge_domain_itf_del (index_t gbdi,
+			   u32 sw_if_index, l2_bd_port_type_t type)
+{
+  gbp_bridge_domain_t *gb;
+
+  gb = gbp_bridge_domain_get (gbdi);
+
+  set_int_l2_mode (vlib_get_main (), vnet_get_main (), MODE_L3, sw_if_index,
+		   gb->gb_bd_index, type, 0, 0);
+}
+
+void
+gbp_bridge_domain_unlock (index_t gbdi)
+{
+  gbp_bridge_domain_t *gb;
+
+  gb = gbp_bridge_domain_get (gbdi);
 
   gb->gb_locks--;
 
@@ -313,17 +325,12 @@
 		       (vnet_get_main (), gb->gb_bvi_sw_if_index),
 		       gb->gb_bd_index, gb->gb_bvi_sw_if_index);
 
-      gbp_bridge_domain_itf_del (gb->gb_bvi_sw_if_index, gb->gb_bd_index,
+      gbp_bridge_domain_itf_del (gbdi, gb->gb_bvi_sw_if_index,
 				 L2_BD_PORT_TYPE_BVI);
       if (~0 != gb->gb_uu_fwd_sw_if_index)
-	gbp_bridge_domain_itf_del (gb->gb_uu_fwd_sw_if_index, gb->gb_bd_index,
+	gbp_bridge_domain_itf_del (gbdi, gb->gb_uu_fwd_sw_if_index,
 				   L2_BD_PORT_TYPE_UU_FWD);
-      if (~0 != gb->gb_bm_flood_sw_if_index)
-	{
-	  gbp_bridge_domain_itf_del (gb->gb_bm_flood_sw_if_index,
-				     gb->gb_bd_index, L2_BD_PORT_TYPE_NORMAL);
-	  gbp_learn_enable (gb->gb_bm_flood_sw_if_index, GBP_LEARN_MODE_L2);
-	}
+      gbp_itf_unlock (&gb->gb_bm_flood_itf);
 
       gbp_bridge_domain_db_remove (gb);
       gbp_route_domain_unlock (gb->gb_rdi);
diff --git a/src/plugins/gbp/gbp_bridge_domain.h b/src/plugins/gbp/gbp_bridge_domain.h
index 6704368..0449240 100644
--- a/src/plugins/gbp/gbp_bridge_domain.h
+++ b/src/plugins/gbp/gbp_bridge_domain.h
@@ -17,6 +17,7 @@
 #define __GBP_BRIDGE_DOMAIN_H__
 
 #include <plugins/gbp/gbp_types.h>
+#include <plugins/gbp/gbp_itf.h>
 
 #include <vnet/fib/fib_types.h>
 #include <vnet/l2/l2_bd.h>
@@ -70,7 +71,7 @@
   /**
    * The BD's interface to sned Broadcast and multicast packets
    */
-  u32 gb_bm_flood_sw_if_index;
+  gbp_itf_hdl_t gb_bm_flood_itf;
 
   /**
    * The index of the BD's VNI interface on which packets from
@@ -85,9 +86,11 @@
   u32 gb_locks;
 } gbp_bridge_domain_t;
 
-extern void gbp_bridge_domain_itf_add (u32 sw_if_index, u32 bd_index,
+extern void gbp_bridge_domain_itf_add (index_t gbdi,
+				       u32 sw_if_index,
 				       l2_bd_port_type_t type);
-extern void gbp_bridge_domain_itf_del (u32 sw_if_index, u32 bd_index,
+extern void gbp_bridge_domain_itf_del (index_t gbdi,
+				       u32 sw_if_index,
 				       l2_bd_port_type_t type);
 
 extern int gbp_bridge_domain_add_and_lock (u32 bd_id,
diff --git a/src/plugins/gbp/gbp_contract.c b/src/plugins/gbp/gbp_contract.c
index c7c2378..452c5a5 100644
--- a/src/plugins/gbp/gbp_contract.c
+++ b/src/plugins/gbp/gbp_contract.c
@@ -275,7 +275,10 @@
   gnh->gnh_ai[fproto] =
     adj_nbr_add_or_lock_w_rewrite (fproto,
 				   fib_proto_to_link (fproto),
-				   &gnh->gnh_ip, ge->ge_fwd.gef_itf, rewrite);
+				   &gnh->gnh_ip,
+				   gbp_itf_get_sw_if_index (ge->
+							    ge_fwd.gef_itf),
+				   rewrite);
 
   adj_unlock (old_ai);
 }
diff --git a/src/plugins/gbp/gbp_endpoint.c b/src/plugins/gbp/gbp_endpoint.c
index d759da2..836930a 100644
--- a/src/plugins/gbp/gbp_endpoint.c
+++ b/src/plugins/gbp/gbp_endpoint.c
@@ -264,8 +264,8 @@
   fib_node_init (&ge->ge_node, gbp_endpoint_fib_type);
   gei = gbp_endpoint_index (ge);
   ge->ge_key.gek_gbd =
-    ge->ge_key.gek_grd =
-    ge->ge_fwd.gef_itf = ge->ge_fwd.gef_fib_index = INDEX_INVALID;
+    ge->ge_key.gek_grd = ge->ge_fwd.gef_fib_index = INDEX_INVALID;
+  gbp_itf_hdl_reset (&ge->ge_fwd.gef_itf);
   ge->ge_last_time = vlib_time_now (vlib_get_main ());
   ge->ge_key.gek_gbd = gbp_bridge_domain_index (gbd);
 
@@ -336,11 +336,7 @@
 gbp_endpoint_loc_destroy (gbp_endpoint_loc_t * gel)
 {
   gbp_endpoint_group_unlock (gel->gel_epg);
-
-  if (gel->gel_flags & GBP_ENDPOINT_FLAG_REMOTE)
-    {
-      vxlan_gbp_tunnel_unlock (gel->gel_sw_if_index);
-    }
+  gbp_itf_unlock (&gel->gel_itf);
 }
 
 static gbp_endpoint_loc_t *
@@ -349,7 +345,7 @@
   gbp_endpoint_loc_t gel = {
     .gel_src = src,
     .gel_epg = INDEX_INVALID,
-    .gel_sw_if_index = INDEX_INVALID,
+    .gel_itf = GBP_ITF_HDL_INVALID,
     .gel_locks = 0,
   };
   u32 pos;
@@ -484,7 +480,8 @@
 }
 
 static void
-gbp_endpoint_loc_update (gbp_endpoint_loc_t * gel,
+gbp_endpoint_loc_update (const gbp_endpoint_t * ge,
+			 gbp_endpoint_loc_t * gel,
 			 const gbp_bridge_domain_t * gb,
 			 u32 sw_if_index,
 			 index_t ggi,
@@ -544,29 +541,29 @@
        * create a child vlxan-gbp tunnel and use that as the endpoint's
        * interface.
        */
-      if (~0 != gel->gel_sw_if_index)
-	vxlan_gbp_tunnel_unlock (gel->gel_sw_if_index);
+      gbp_itf_hdl_t old = gel->gel_itf;
 
       switch (gbp_vxlan_tunnel_get_type (sw_if_index))
 	{
 	case GBP_VXLAN_TEMPLATE_TUNNEL:
 	  gel->tun.gel_parent_sw_if_index = sw_if_index;
-	  gel->gel_sw_if_index =
-	    gbp_vxlan_tunnel_clone_and_lock (sw_if_index,
-					     &gel->tun.gel_src,
-					     &gel->tun.gel_dst);
+	  gel->gel_itf = gbp_vxlan_tunnel_clone_and_lock (sw_if_index,
+							  &gel->tun.gel_src,
+							  &gel->tun.gel_dst);
 	  break;
 	case VXLAN_GBP_TUNNEL:
 	  gel->tun.gel_parent_sw_if_index =
 	    vxlan_gbp_tunnel_get_parent (sw_if_index);
-	  gel->gel_sw_if_index = sw_if_index;
-	  vxlan_gbp_tunnel_lock (gel->gel_sw_if_index);
+	  gel->gel_itf = vxlan_gbp_tunnel_lock_itf (sw_if_index);
 	  break;
 	}
+
+      gbp_itf_unlock (&old);
     }
   else
     {
-      gel->gel_sw_if_index = sw_if_index;
+      gel->gel_itf = gbp_itf_l2_add_and_lock (sw_if_index,
+					      ge->ge_key.gek_gbd);
     }
 }
 
@@ -578,9 +575,7 @@
   gbp_endpoint_fwd_t *gef;
   const fib_prefix_t *pfx;
   index_t *ai;
-  index_t gei;
 
-  gei = gbp_endpoint_index (ge);
   gbd = gbp_bridge_domain_get (ge->ge_key.gek_gbd);
   gef = &ge->ge_fwd;
 
@@ -609,26 +604,23 @@
     adj_unlock (*ai);
   }
 
-  if (INDEX_INVALID != gef->gef_itf)
+  if (gbp_itf_hdl_is_valid (gef->gef_itf))
     {
       l2fib_del_entry (ge->ge_key.gek_mac.bytes,
-		       gbd->gb_bd_index, gef->gef_itf);
-      gbp_itf_set_l2_input_feature (gef->gef_itf, gei, L2INPUT_FEAT_NONE);
-      gbp_itf_set_l2_output_feature (gef->gef_itf, gei, L2OUTPUT_FEAT_NONE);
-
-      gbp_itf_unlock (gef->gef_itf);
-      gef->gef_itf = INDEX_INVALID;
+		       gbd->gb_bd_index,
+		       gbp_itf_get_sw_if_index (gef->gef_itf));
     }
 
+  gbp_itf_unlock (&gef->gef_itf);
   vec_free (gef->gef_adjs);
 }
 
 static void
 gbb_endpoint_fwd_recalc (gbp_endpoint_t * ge)
 {
-  const gbp_route_domain_t *grd;
   const gbp_bridge_domain_t *gbd;
   const gbp_endpoint_group_t *gg;
+  const gbp_route_domain_t *grd;
   gbp_endpoint_loc_t *gel;
   gbp_endpoint_fwd_t *gef;
   const fib_prefix_t *pfx;
@@ -654,12 +646,11 @@
       gg = NULL;
     }
 
-  gef->gef_itf = gbp_itf_add_and_lock (gel->gel_sw_if_index,
-				       gbd->gb_bd_index);
+  gef->gef_itf = gbp_itf_clone_and_lock (gel->gel_itf);
 
   if (!mac_address_is_zero (&ge->ge_key.gek_mac))
     {
-      gbp_itf_set_l2_input_feature (gef->gef_itf, gei, L2INPUT_FEAT_GBP_FWD);
+      gbp_itf_l2_set_input_feature (gef->gef_itf, L2INPUT_FEAT_GBP_FWD);
 
       if (gbp_endpoint_is_remote (ge) || gbp_endpoint_is_external (ge))
 	{
@@ -667,18 +658,19 @@
 	   * bridged packets to external endpoints should be classifed
 	   * based on the EP's/BD's EPG
 	   */
-	  gbp_itf_set_l2_output_feature (gef->gef_itf, gei,
+	  gbp_itf_l2_set_output_feature (gef->gef_itf,
 					 L2OUTPUT_FEAT_GBP_POLICY_MAC);
 	}
       else
 	{
-	  gbp_endpoint_add_itf (gef->gef_itf, gei);
-	  gbp_itf_set_l2_output_feature (gef->gef_itf, gei,
+	  gbp_endpoint_add_itf (gbp_itf_get_sw_if_index (gef->gef_itf), gei);
+	  gbp_itf_l2_set_output_feature (gef->gef_itf,
 					 L2OUTPUT_FEAT_GBP_POLICY_PORT);
 	}
       l2fib_add_entry (ge->ge_key.gek_mac.bytes,
 		       gbd->gb_bd_index,
-		       gef->gef_itf, L2FIB_ENTRY_RESULT_FLAG_STATIC);
+		       gbp_itf_get_sw_if_index (gef->gef_itf),
+		       L2FIB_ENTRY_RESULT_FLAG_STATIC);
     }
 
   vec_foreach (pfx, ge->ge_key.gek_ips)
@@ -716,7 +708,7 @@
 	 * tunnel since the BD will not contain the EP's MAC since it was
 	 * L3 learned. The dst MAC address used is the 'BD's MAC'.
 	 */
-	ip_sw_if_index = gef->gef_itf;
+	ip_sw_if_index = gbp_itf_get_sw_if_index (gef->gef_itf);
 
 	mac_address_to_bytes (gbp_route_domain_get_local_mac (),
 			      eth->src_address);
@@ -786,7 +778,8 @@
 	 */
 	if (gbp_endpoint_is_local (ge) && ~0 != gg->gg_uplink_sw_if_index)
 	  {
-	    gbp_endpoint_add_itf (gef->gef_itf, gei);
+	    gbp_endpoint_add_itf (gbp_itf_get_sw_if_index (gef->gef_itf),
+				  gei);
 	    if (FIB_PROTOCOL_IP4 == pfx->fp_proto)
 	      send_ip4_garp_w_addr (vlib_get_main (),
 				    &pfx->fp_addr.ip4,
@@ -801,7 +794,7 @@
 
   if (gbp_endpoint_is_external (ge))
     {
-      gbp_itf_set_l2_input_feature (gef->gef_itf, gei,
+      gbp_itf_l2_set_input_feature (gef->gef_itf,
 				    L2INPUT_FEAT_GBP_LPM_CLASSIFY);
     }
   else if (gbp_endpoint_is_local (ge))
@@ -817,7 +810,7 @@
 
       if (NULL != gg && ~0 != gg->gg_uplink_sw_if_index)
 	feats |= L2INPUT_FEAT_GBP_FWD;
-      gbp_itf_set_l2_input_feature (gef->gef_itf, gei, feats);
+      gbp_itf_l2_set_input_feature (gef->gef_itf, feats);
     }
 
   /*
@@ -901,8 +894,8 @@
   gei = gbp_endpoint_index (ge);
   gel = gbp_endpoint_loc_find_or_add (ge, src);
 
-  gbp_endpoint_loc_update (gel, gbd, sw_if_index, ggi, flags, tun_src,
-			   tun_dst);
+  gbp_endpoint_loc_update (ge, gel, gbd, sw_if_index, ggi, flags,
+			   tun_src, tun_dst);
 
   if (src <= best)
     {
@@ -1193,7 +1186,7 @@
   gbp_endpoint_fwd_t *gef = va_arg (*args, gbp_endpoint_fwd_t *);
 
   s = format (s, "fwd:");
-  s = format (s, "\n   itf:[%U]", format_gbp_itf, gef->gef_itf);
+  s = format (s, "\n   itf:[%U]", format_gbp_itf_hdl, gef->gef_itf);
   if (GBP_ENDPOINT_FLAG_NONE != gef->gef_flags)
     {
       s = format (s, " flags:%U", format_gbp_endpoint_flags, gef->gef_flags);
@@ -1227,10 +1220,8 @@
   gbp_endpoint_loc_t *gel = va_arg (*args, gbp_endpoint_loc_t *);
 
   s = format (s, "%U", format_gbp_endpoint_src, gel->gel_src);
-  s =
-    format (s, "\n    %U", format_vnet_sw_if_index_name, vnet_get_main (),
-	    gel->gel_sw_if_index);
-  s = format (s, " EPG:%d", gel->gel_epg);
+  s = format (s, "\n    EPG:%d [%U]", gel->gel_epg,
+	      format_gbp_itf_hdl, gel->gel_itf);
 
   if (GBP_ENDPOINT_FLAG_NONE != gel->gel_flags)
     {
diff --git a/src/plugins/gbp/gbp_endpoint.h b/src/plugins/gbp/gbp_endpoint.h
index 27df644..3155e7b 100644
--- a/src/plugins/gbp/gbp_endpoint.h
+++ b/src/plugins/gbp/gbp_endpoint.h
@@ -17,6 +17,7 @@
 #define __GBP_ENDPOINT_H__
 
 #include <plugins/gbp/gbp_types.h>
+#include <plugins/gbp/gbp_itf.h>
 #include <vnet/ip/ip.h>
 #include <vnet/ethernet/mac_address.h>
 
@@ -122,7 +123,7 @@
   /**
    * The interface on which the EP is connected
    */
-  u32 gel_sw_if_index;
+  gbp_itf_hdl_t gel_itf;
 
   /**
    * Endpoint flags
@@ -158,7 +159,7 @@
   /**
    * The interface on which the EP is connected
    */
-  index_t gef_itf;
+  gbp_itf_hdl_t gef_itf;
 
   /**
    * The L3 adj, if created
diff --git a/src/plugins/gbp/gbp_endpoint_group.c b/src/plugins/gbp/gbp_endpoint_group.c
index bacbb0c..19c05f9 100644
--- a/src/plugins/gbp/gbp_endpoint_group.c
+++ b/src/plugins/gbp/gbp_endpoint_group.c
@@ -19,6 +19,7 @@
 #include <plugins/gbp/gbp_endpoint.h>
 #include <plugins/gbp/gbp_bridge_domain.h>
 #include <plugins/gbp/gbp_route_domain.h>
+#include <plugins/gbp/gbp_itf.h>
 
 #include <vnet/dpo/dvr_dpo.h>
 #include <vnet/fib/fib_table.h>
@@ -87,7 +88,6 @@
 
   if (INDEX_INVALID == ggi)
     {
-      gbp_bridge_domain_t *gb;
       fib_protocol_t fproto;
       index_t gbi, grdi;
 
@@ -104,16 +104,14 @@
 	  return (VNET_API_ERROR_NO_SUCH_FIB);
 	}
 
-      gb = gbp_bridge_domain_get (gbi);
-
       pool_get_zero (gbp_endpoint_group_pool, gg);
 
       gg->gg_vnid = vnid;
       gg->gg_rd = grdi;
       gg->gg_gbd = gbi;
-      gg->gg_bd_index = gb->gb_bd_index;
 
       gg->gg_uplink_sw_if_index = uplink_sw_if_index;
+      gbp_itf_hdl_reset (&gg->gg_uplink_itf);
       gg->gg_locks = 1;
       gg->gg_sclass = sclass;
       gg->gg_retention = *retention;
@@ -138,10 +136,11 @@
 	   * Add the uplink to the BD
 	   * packets direct from the uplink have had policy applied
 	   */
-	  gbp_bridge_domain_itf_add (gg->gg_uplink_sw_if_index,
-				     gg->gg_bd_index, L2_BD_PORT_TYPE_NORMAL);
-	  l2input_intf_bitmap_enable (gg->gg_uplink_sw_if_index,
-				      L2INPUT_FEAT_GBP_NULL_CLASSIFY, 1);
+	  gg->gg_uplink_itf =
+	    gbp_itf_l2_add_and_lock (gg->gg_uplink_sw_if_index, gbi);
+
+	  gbp_itf_l2_set_input_feature (gg->gg_uplink_itf,
+					L2INPUT_FEAT_GBP_NULL_CLASSIFY);
 	}
 
       hash_set (gbp_endpoint_group_db.gg_hash_sclass,
@@ -176,13 +175,8 @@
 
       gg = pool_elt_at_index (gbp_endpoint_group_pool, ggi);
 
-      if (~0 != gg->gg_uplink_sw_if_index)
-	{
-	  gbp_bridge_domain_itf_del (gg->gg_uplink_sw_if_index,
-				     gg->gg_bd_index, L2_BD_PORT_TYPE_NORMAL);
-	  l2input_intf_bitmap_enable (gg->gg_uplink_sw_if_index,
-				      L2INPUT_FEAT_GBP_NULL_CLASSIFY, 0);
-	}
+      gbp_itf_unlock (&gg->gg_uplink_itf);
+
       FOR_EACH_FIB_IP_PROTOCOL (fproto)
       {
 	dpo_reset (&gg->gg_dpo[fproto]);
@@ -332,16 +326,15 @@
 format_gbp_endpoint_group (u8 * s, va_list * args)
 {
   gbp_endpoint_group_t *gg = va_arg (*args, gbp_endpoint_group_t*);
-  vnet_main_t *vnm = vnet_get_main ();
 
   if (NULL != gg)
-    s = format (s, "[%d] %d, sclass:%d bd:[%d,%d] rd:[%d] uplink:%U retention:%U locks:%d",
+    s = format (s, "[%d] %d, sclass:%d bd:%d rd:%d uplink:%U retention:%U locks:%d",
                 gg - gbp_endpoint_group_pool,
                 gg->gg_vnid,
                 gg->gg_sclass,
-                gbp_endpoint_group_get_bd_id(gg), gg->gg_bd_index,
+                gg->gg_gbd,
                 gg->gg_rd,
-                format_vnet_sw_if_index_name, vnm, gg->gg_uplink_sw_if_index,
+                format_gbp_itf_hdl, gg->gg_uplink_itf,
                 format_gbp_endpoint_retention, &gg->gg_retention,
                 gg->gg_locks);
   else
diff --git a/src/plugins/gbp/gbp_endpoint_group.h b/src/plugins/gbp/gbp_endpoint_group.h
index e0d54ea..c5fdff8 100644
--- a/src/plugins/gbp/gbp_endpoint_group.h
+++ b/src/plugins/gbp/gbp_endpoint_group.h
@@ -17,6 +17,7 @@
 #define __GBP_ENDPOINT_GROUP_H__
 
 #include <plugins/gbp/gbp_types.h>
+#include <plugins/gbp/gbp_itf.h>
 
 #include <vnet/fib/fib_types.h>
 
@@ -48,7 +49,6 @@
    * Bridge-domain ID the EPG is in
    */
   index_t gg_gbd;
-  index_t gg_bd_index;
 
   /**
    * route-domain/IP-table ID the EPG is in
@@ -64,6 +64,7 @@
    * the uplink interface dedicated to the EPG
    */
   u32 gg_uplink_sw_if_index;
+  gbp_itf_hdl_t gg_uplink_itf;
 
   /**
    * The DPO used in the L3 path for forwarding internal subnets
diff --git a/src/plugins/gbp/gbp_ext_itf.c b/src/plugins/gbp/gbp_ext_itf.c
index f3c54fc..e18bbe9 100644
--- a/src/plugins/gbp/gbp_ext_itf.c
+++ b/src/plugins/gbp/gbp_ext_itf.c
@@ -44,7 +44,7 @@
   gbp_ext_itf_t *gx = va_arg (*args, gbp_ext_itf_t *);
 
   return (format (s, "%U%s in %U",
-		  format_gbp_itf, gx->gx_itf,
+		  format_gbp_itf_hdl, gx->gx_itf,
 		  (gx->gx_flags & GBP_EXT_ITF_F_ANON) ? " [anon]" : "",
 		  format_gbp_bridge_domain, gx->gx_bd));
 }
@@ -85,7 +85,7 @@
 
       gx->gx_bd = gbi;
       gx->gx_rd = gri;
-      gx->gx_itf = sw_if_index;
+      gbp_itf_hdl_reset (&gx->gx_itf);
 
       FOR_EACH_FIB_IP_PROTOCOL (fproto)
       {
@@ -96,14 +96,13 @@
       if (flags & GBP_EXT_ITF_F_ANON)
 	{
 	  /* add interface to the BD */
-	  index_t itf = gbp_itf_add_and_lock (sw_if_index,
-					      gbp_bridge_domain_get
-					      (gbi)->gb_bd_index);
+	  gx->gx_itf = gbp_itf_l2_add_and_lock (sw_if_index, gbi);
+
 	  /* setup GBP L2 features on this interface */
-	  gbp_itf_set_l2_input_feature (itf, 0,
+	  gbp_itf_l2_set_input_feature (gx->gx_itf,
 					L2INPUT_FEAT_GBP_LPM_ANON_CLASSIFY |
 					L2INPUT_FEAT_LEARN);
-	  gbp_itf_set_l2_output_feature (itf, 0,
+	  gbp_itf_l2_set_output_feature (gx->gx_itf,
 					 L2OUTPUT_FEAT_GBP_POLICY_LPM);
 	}
 
@@ -136,9 +135,7 @@
 
       GBP_EXT_ITF_DBG ("del: %U", format_gbp_ext_itf, gx);
 
-      if (gx->gx_flags & GBP_EXT_ITF_F_ANON)
-	gbp_itf_unlock (gx->gx_itf);
-
+      gbp_itf_unlock (&gx->gx_itf);
       gbp_route_domain_unlock (gx->gx_rd);
       gbp_bridge_domain_unlock (gx->gx_bd);
 
diff --git a/src/plugins/gbp/gbp_ext_itf.h b/src/plugins/gbp/gbp_ext_itf.h
index f3829ca..03b1992 100644
--- a/src/plugins/gbp/gbp_ext_itf.h
+++ b/src/plugins/gbp/gbp_ext_itf.h
@@ -35,7 +35,7 @@
   /**
    * The interface
    */
-  u32 gx_itf;
+  gbp_itf_hdl_t gx_itf;
 
   /**
    * The BD this external interface is a member of
diff --git a/src/plugins/gbp/gbp_itf.c b/src/plugins/gbp/gbp_itf.c
index 59c9629..a9e9225 100644
--- a/src/plugins/gbp/gbp_itf.c
+++ b/src/plugins/gbp/gbp_itf.c
@@ -15,6 +15,18 @@
 
 #include <plugins/gbp/gbp_itf.h>
 #include <plugins/gbp/gbp_bridge_domain.h>
+#include <plugins/gbp/gbp_route_domain.h>
+
+#define foreach_gbp_itf_mode  \
+  _(L2, "l2")                 \
+  _(L3, "L3")
+
+typedef enum gbp_ift_mode_t_
+{
+#define _(s,v)  GBP_ITF_MODE_##s,
+  foreach_gbp_itf_mode
+#undef _
+} gbp_itf_mode_t;
 
 /**
  * Attributes and configurations attached to interfaces by GBP
@@ -26,100 +38,371 @@
    */
   u32 gi_locks;
 
+  /**
+   * The interface this wrapper is managing
+   */
   u32 gi_sw_if_index;
-  u32 gi_bd_index;
+
+  /**
+   * The mode of the interface
+   */
+  gbp_itf_mode_t gi_mode;
+
+  /**
+   * Users of this interface - this is encoded in the user's handle
+   */
+  u32 *gi_users;
 
   /**
    * L2/L3 Features configured by each user
    */
-  u32 *gi_l2_input_fbs;
-  u32 gi_l2_input_fb;
-  u32 *gi_l2_output_fbs;
-  u32 gi_l2_output_fb;
+  u32 *gi_input_fbs;
+  u32 gi_input_fb;
+  u32 *gi_output_fbs;
+  u32 gi_output_fb;
+
+  /**
+   * function to call when the interface is deleted.
+   */
+  gbp_itf_free_fn_t gi_free_fn;
+
+  union
+  {
+    /**
+     * GBP BD or RD index
+     */
+    u32 gi_gbi;
+    index_t gi_gri;
+  };
 } gbp_itf_t;
 
-static gbp_itf_t *gbp_itfs;
+static gbp_itf_t *gbp_itf_pool;
+static uword *gbp_itf_db;
+
+static const char *gbp_itf_feat_bit_pos_to_arc[] = {
+#define _(s,v,a) [GBP_ITF_L3_FEAT_POS_##s] = a,
+  foreach_gdb_l3_feature
+#undef _
+};
+
+static const char *gbp_itf_feat_bit_pos_to_feat[] = {
+#define _(s,v,a) [GBP_ITF_L3_FEAT_POS_##s] = v,
+  foreach_gdb_l3_feature
+#undef _
+};
+
+u8 *
+format_gbp_itf_l3_feat (u8 * s, va_list * args)
+{
+  gbp_itf_l3_feat_t flags = va_arg (*args, gbp_itf_l3_feat_t);
+
+#define _(a, b, c)                              \
+  if (flags & GBP_ITF_L3_FEAT_##a)              \
+    s = format (s, "%s ", b);
+  foreach_gdb_l3_feature
+#undef _
+    return (s);
+}
+
+void
+gbp_itf_hdl_reset (gbp_itf_hdl_t * gh)
+{
+  *gh = GBP_ITF_HDL_INVALID;
+}
+
+bool
+gbp_itf_hdl_is_valid (gbp_itf_hdl_t gh)
+{
+  return (gh.gh_which != GBP_ITF_HDL_INVALID.gh_which);
+}
 
 static gbp_itf_t *
 gbp_itf_get (index_t gii)
 {
-  vec_validate (gbp_itfs, gii);
+  if (pool_is_free_index (gbp_itf_pool, gii))
+    return (NULL);
 
-  return (&gbp_itfs[gii]);
+  return (pool_elt_at_index (gbp_itf_pool, gii));
 }
 
-static index_t
-gbp_itf_get_itf (u32 sw_if_index)
+static gbp_itf_t *
+gbp_itf_find (u32 sw_if_index)
 {
-  return (sw_if_index);
+  uword *p;
+
+  p = hash_get (gbp_itf_db, sw_if_index);
+
+  if (NULL != p)
+    return (gbp_itf_get (p[0]));
+
+  return (NULL);
 }
 
-index_t
-gbp_itf_add_and_lock (u32 sw_if_index, u32 bd_index)
+static gbp_itf_t *
+gbp_itf_find_hdl (gbp_itf_hdl_t gh)
+{
+  return (gbp_itf_find (gh.gh_which));
+}
+
+u32
+gbp_itf_get_sw_if_index (gbp_itf_hdl_t hdl)
+{
+  return (hdl.gh_which);
+}
+
+static gbp_itf_hdl_t
+gbp_itf_mk_hdl (gbp_itf_t * gi)
+{
+  gbp_itf_hdl_t gh;
+  u32 *useri;
+
+  pool_get (gi->gi_users, useri);
+  *useri = 0;
+
+  gh.gh_who = useri - gi->gi_users;
+  gh.gh_which = gi->gi_sw_if_index;
+
+  return (gh);
+}
+
+static gbp_itf_hdl_t
+gbp_itf_l2_add_and_lock_i (u32 sw_if_index, index_t gbi, gbp_itf_free_fn_t ff)
 {
   gbp_itf_t *gi;
 
-  gi = gbp_itf_get (gbp_itf_get_itf (sw_if_index));
+  gi = gbp_itf_find (sw_if_index);
 
-  if (0 == gi->gi_locks)
+  if (NULL == gi)
     {
-      gi->gi_sw_if_index = sw_if_index;
-      gi->gi_bd_index = bd_index;
+      pool_get_zero (gbp_itf_pool, gi);
 
-      if (~0 != gi->gi_bd_index)
-	gbp_bridge_domain_itf_add (sw_if_index, bd_index,
-				   L2_BD_PORT_TYPE_NORMAL);
+      gi->gi_sw_if_index = sw_if_index;
+      gi->gi_gbi = gbi;
+      gi->gi_mode = GBP_ITF_MODE_L2;
+      gi->gi_free_fn = ff;
+
+      gbp_bridge_domain_itf_add (gi->gi_gbi, gi->gi_sw_if_index,
+				 L2_BD_PORT_TYPE_NORMAL);
+
+      hash_set (gbp_itf_db, gi->gi_sw_if_index, gi - gbp_itf_pool);
     }
 
   gi->gi_locks++;
 
-  return (sw_if_index);
+  return (gbp_itf_mk_hdl (gi));
 }
 
-void
-gbp_itf_unlock (index_t gii)
+gbp_itf_hdl_t
+gbp_itf_l2_add_and_lock (u32 sw_if_index, index_t gbi)
+{
+  return (gbp_itf_l2_add_and_lock_i (sw_if_index, gbi, NULL));
+}
+
+gbp_itf_hdl_t
+gbp_itf_l2_add_and_lock_w_free (u32 sw_if_index,
+				index_t gbi, gbp_itf_free_fn_t ff)
+{
+  return (gbp_itf_l2_add_and_lock_i (sw_if_index, gbi, ff));
+}
+
+gbp_itf_hdl_t
+gbp_itf_l3_add_and_lock_i (u32 sw_if_index, index_t gri, gbp_itf_free_fn_t ff)
 {
   gbp_itf_t *gi;
 
-  gi = gbp_itf_get (gii);
+  gi = gbp_itf_find (sw_if_index);
+
+  if (NULL == gi)
+    {
+      const gbp_route_domain_t *grd;
+      fib_protocol_t fproto;
+
+      pool_get_zero (gbp_itf_pool, gi);
+
+      gi->gi_sw_if_index = sw_if_index;
+      gi->gi_mode = GBP_ITF_MODE_L3;
+      gi->gi_gri = gri;
+      gi->gi_free_fn = ff;
+
+      grd = gbp_route_domain_get (gi->gi_gri);
+
+      ip4_sw_interface_enable_disable (gi->gi_sw_if_index, 1);
+      ip6_sw_interface_enable_disable (gi->gi_sw_if_index, 1);
+
+      FOR_EACH_FIB_IP_PROTOCOL (fproto)
+	ip_table_bind (fproto, gi->gi_sw_if_index,
+		       grd->grd_table_id[fproto], 1);
+
+      hash_set (gbp_itf_db, gi->gi_sw_if_index, gi - gbp_itf_pool);
+    }
+
+  gi->gi_locks++;
+
+  return (gbp_itf_mk_hdl (gi));
+}
+
+gbp_itf_hdl_t
+gbp_itf_l3_add_and_lock (u32 sw_if_index, index_t gri)
+{
+  return (gbp_itf_l3_add_and_lock_i (sw_if_index, gri, NULL));
+}
+
+gbp_itf_hdl_t
+gbp_itf_l3_add_and_lock_w_free (u32 sw_if_index,
+				index_t gri, gbp_itf_free_fn_t ff)
+{
+  return (gbp_itf_l3_add_and_lock_i (sw_if_index, gri, ff));
+}
+
+void
+gbp_itf_lock (gbp_itf_hdl_t gh)
+{
+  gbp_itf_t *gi;
+
+  if (!gbp_itf_hdl_is_valid (gh))
+    return;
+
+  gi = gbp_itf_find_hdl (gh);
+
+  gi->gi_locks++;
+}
+
+gbp_itf_hdl_t
+gbp_itf_clone_and_lock (gbp_itf_hdl_t gh)
+{
+  gbp_itf_t *gi;
+
+  if (!gbp_itf_hdl_is_valid (gh))
+    return (GBP_ITF_HDL_INVALID);
+
+  gi = gbp_itf_find_hdl (gh);
+
+  gi->gi_locks++;
+
+  return (gbp_itf_mk_hdl (gi));
+}
+
+void
+gbp_itf_unlock (gbp_itf_hdl_t * gh)
+{
+  gbp_itf_t *gi;
+
+  if (!gbp_itf_hdl_is_valid (*gh))
+    return;
+
+  gi = gbp_itf_find_hdl (*gh);
   ASSERT (gi->gi_locks > 0);
   gi->gi_locks--;
 
   if (0 == gi->gi_locks)
     {
-      if (~0 != gi->gi_bd_index)
-	gbp_bridge_domain_itf_del (gi->gi_sw_if_index, gi->gi_bd_index,
-				   L2_BD_PORT_TYPE_NORMAL);
-      vec_free (gi->gi_l2_input_fbs);
-      vec_free (gi->gi_l2_output_fbs);
+      if (GBP_ITF_MODE_L2 == gi->gi_mode)
+	{
+	  gbp_itf_l2_set_input_feature (*gh, L2INPUT_FEAT_NONE);
+	  gbp_itf_l2_set_output_feature (*gh, L2OUTPUT_FEAT_NONE);
+	  gbp_bridge_domain_itf_del (gi->gi_gbi,
+				     gi->gi_sw_if_index,
+				     L2_BD_PORT_TYPE_NORMAL);
+	}
+      else
+	{
+	  fib_protocol_t fproto;
+
+	  gbp_itf_l3_set_input_feature (*gh, GBP_ITF_L3_FEAT_NONE);
+	  FOR_EACH_FIB_IP_PROTOCOL (fproto)
+	    ip_table_bind (fproto, gi->gi_sw_if_index, 0, 0);
+
+	  ip4_sw_interface_enable_disable (gi->gi_sw_if_index, 0);
+	  ip6_sw_interface_enable_disable (gi->gi_sw_if_index, 0);
+	}
+
+      hash_unset (gbp_itf_db, gi->gi_sw_if_index);
+
+      if (gi->gi_free_fn)
+	gi->gi_free_fn (gi->gi_sw_if_index);
+
+      pool_free (gi->gi_users);
+      vec_free (gi->gi_input_fbs);
+      vec_free (gi->gi_output_fbs);
 
       memset (gi, 0, sizeof (*gi));
     }
+
+  gbp_itf_hdl_reset (gh);
 }
 
 void
-gbp_itf_set_l2_input_feature (index_t gii,
-			      index_t useri, l2input_feat_masks_t feats)
+gbp_itf_l3_set_input_feature (gbp_itf_hdl_t gh, gbp_itf_l3_feat_t feats)
 {
   u32 diff_fb, new_fb, *fb, feat;
   gbp_itf_t *gi;
 
-  gi = gbp_itf_get (gii);
+  gi = gbp_itf_find_hdl (gh);
 
-  if (gi->gi_bd_index == ~0)
+  if (NULL == gi || GBP_ITF_MODE_L3 != gi->gi_mode)
     return;
 
-  vec_validate (gi->gi_l2_input_fbs, useri);
-  gi->gi_l2_input_fbs[useri] = feats;
+  vec_validate (gi->gi_input_fbs, gh.gh_who);
+  gi->gi_input_fbs[gh.gh_who] = feats;
 
   new_fb = 0;
-  vec_foreach (fb, gi->gi_l2_input_fbs)
+  vec_foreach (fb, gi->gi_input_fbs)
   {
     new_fb |= *fb;
   }
 
   /* add new features */
-  diff_fb = (gi->gi_l2_input_fb ^ new_fb) & new_fb;
+  diff_fb = (gi->gi_input_fb ^ new_fb) & new_fb;
+
+  /* *INDENT-OFF* */
+  foreach_set_bit (feat, diff_fb,
+  ({
+    vnet_feature_enable_disable (gbp_itf_feat_bit_pos_to_arc[feat],
+                                 gbp_itf_feat_bit_pos_to_feat[feat],
+                                 gi->gi_sw_if_index, 1, 0, 0);
+  }));
+  /* *INDENT-ON* */
+
+  /* remove unneeded features */
+  diff_fb = (gi->gi_input_fb ^ new_fb) & gi->gi_input_fb;
+
+  /* *INDENT-OFF* */
+  foreach_set_bit (feat, diff_fb,
+  ({
+    vnet_feature_enable_disable (gbp_itf_feat_bit_pos_to_arc[feat],
+                                 gbp_itf_feat_bit_pos_to_feat[feat],
+                                 gi->gi_sw_if_index, 0, 0, 0);
+  }));
+  /* *INDENT-ON* */
+
+  gi->gi_input_fb = new_fb;
+}
+
+void
+gbp_itf_l2_set_input_feature (gbp_itf_hdl_t gh, l2input_feat_masks_t feats)
+{
+  u32 diff_fb, new_fb, *fb, feat;
+  gbp_itf_t *gi;
+
+  gi = gbp_itf_find_hdl (gh);
+
+  if (NULL == gi || GBP_ITF_MODE_L2 != gi->gi_mode)
+    {
+      ASSERT (0);
+      return;
+    }
+
+  vec_validate (gi->gi_input_fbs, gh.gh_who);
+  gi->gi_input_fbs[gh.gh_who] = feats;
+
+  new_fb = 0;
+  vec_foreach (fb, gi->gi_input_fbs)
+  {
+    new_fb |= *fb;
+  }
+
+  /* add new features */
+  diff_fb = (gi->gi_input_fb ^ new_fb) & new_fb;
 
   /* *INDENT-OFF* */
   foreach_set_bit (feat, diff_fb,
@@ -129,7 +412,7 @@
   /* *INDENT-ON* */
 
   /* remove unneeded features */
-  diff_fb = (gi->gi_l2_input_fb ^ new_fb) & gi->gi_l2_input_fb;
+  diff_fb = (gi->gi_input_fb ^ new_fb) & gi->gi_input_fb;
 
   /* *INDENT-OFF* */
   foreach_set_bit (feat, diff_fb,
@@ -138,32 +421,34 @@
   }));
   /* *INDENT-ON* */
 
-  gi->gi_l2_input_fb = new_fb;
+  gi->gi_input_fb = new_fb;
 }
 
 void
-gbp_itf_set_l2_output_feature (index_t gii,
-			       index_t useri, l2output_feat_masks_t feats)
+gbp_itf_l2_set_output_feature (gbp_itf_hdl_t gh, l2output_feat_masks_t feats)
 {
   u32 diff_fb, new_fb, *fb, feat;
   gbp_itf_t *gi;
 
-  gi = gbp_itf_get (gii);
+  gi = gbp_itf_find_hdl (gh);
 
-  if (gi->gi_bd_index == ~0)
-    return;
+  if (NULL == gi || GBP_ITF_MODE_L2 != gi->gi_mode)
+    {
+      ASSERT (0);
+      return;
+    }
 
-  vec_validate (gi->gi_l2_output_fbs, useri);
-  gi->gi_l2_output_fbs[useri] = feats;
+  vec_validate (gi->gi_output_fbs, gh.gh_who);
+  gi->gi_output_fbs[gh.gh_who] = feats;
 
   new_fb = 0;
-  vec_foreach (fb, gi->gi_l2_output_fbs)
+  vec_foreach (fb, gi->gi_output_fbs)
   {
     new_fb |= *fb;
   }
 
   /* add new features */
-  diff_fb = (gi->gi_l2_output_fb ^ new_fb) & new_fb;
+  diff_fb = (gi->gi_output_fb ^ new_fb) & new_fb;
 
   /* *INDENT-OFF* */
   foreach_set_bit (feat, diff_fb,
@@ -173,7 +458,7 @@
   /* *INDENT-ON* */
 
   /* remove unneeded features */
-  diff_fb = (gi->gi_l2_output_fb ^ new_fb) & gi->gi_l2_output_fb;
+  diff_fb = (gi->gi_output_fb ^ new_fb) & gi->gi_output_fb;
 
   /* *INDENT-OFF* */
   foreach_set_bit (feat, diff_fb,
@@ -182,27 +467,69 @@
   }));
   /* *INDENT-ON* */
 
-  gi->gi_l2_output_fb = new_fb;
+  gi->gi_output_fb = new_fb;
 }
 
-u8 *
+static u8 *
+format_gbp_itf_mode (u8 * s, va_list * args)
+{
+  gbp_itf_mode_t mode = va_arg (*args, gbp_itf_mode_t);
+
+  switch (mode)
+    {
+#define _(a,v)                                  \
+    case GBP_ITF_MODE_##a:                      \
+      return format(s, "%s", v);
+      foreach_gbp_itf_mode
+#undef _
+    }
+  return (s);
+}
+
+static u8 *
 format_gbp_itf (u8 * s, va_list * args)
 {
   index_t gii = va_arg (*args, index_t);
   gbp_itf_t *gi;
 
+  if (INDEX_INVALID == gii)
+    return (format (s, "unset"));
+
   gi = gbp_itf_get (gii);
 
-  s = format (s, "%U locks:%d bd-index:%d input-feats:%U output-feats:%U",
+  s = format (s, "%U locks:%d mode:%U ",
 	      format_vnet_sw_if_index_name, vnet_get_main (),
 	      gi->gi_sw_if_index, gi->gi_locks,
-	      gi->gi_bd_index,
-	      format_l2_input_features, gi->gi_l2_input_fb, 0,
-	      format_l2_output_features, gi->gi_l2_output_fb, 0);
+	      format_gbp_itf_mode, gi->gi_mode);
+
+  if (GBP_ITF_MODE_L2 == gi->gi_mode)
+    s = format (s, "gbp-bd:%d input-feats:[%U] output-feats:[%U]",
+		gi->gi_gbi,
+		format_l2_input_features, gi->gi_input_fb, 0,
+		format_l2_output_features, gi->gi_output_fb, 0);
+  else
+    s = format (s, "gbp-rd:%d input-feats:[%U] output-feats:[%U]",
+		gi->gi_gbi,
+		format_gbp_itf_l3_feat, gi->gi_input_fb,
+		format_gbp_itf_l3_feat, gi->gi_output_fb);
 
   return (s);
 }
 
+u8 *
+format_gbp_itf_hdl (u8 * s, va_list * args)
+{
+  gbp_itf_hdl_t gh = va_arg (*args, gbp_itf_hdl_t);
+  gbp_itf_t *gi;
+
+  gi = gbp_itf_find_hdl (gh);
+
+  if (NULL == gi)
+    return format (s, "INVALID");
+
+  return (format (s, "%U", format_gbp_itf, gi - gbp_itf_pool));
+}
+
 static clib_error_t *
 gbp_itf_show (vlib_main_t * vm,
 	      unformat_input_t * input, vlib_cli_command_t * cmd)
@@ -211,10 +538,12 @@
 
   vlib_cli_output (vm, "Interfaces:");
 
-  vec_foreach_index (gii, gbp_itfs)
-  {
+  /* *INDENT-OFF* */
+  pool_foreach_index (gii, gbp_itf_pool,
+  ({
     vlib_cli_output (vm, "  [%d] %U", gii, format_gbp_itf, gii);
-  }
+  }));
+  /* *INDENT-ON* */
 
   return (NULL);
 }
diff --git a/src/plugins/gbp/gbp_itf.h b/src/plugins/gbp/gbp_itf.h
index 6ece7b1..b0c7ed9 100644
--- a/src/plugins/gbp/gbp_itf.h
+++ b/src/plugins/gbp/gbp_itf.h
@@ -19,17 +19,71 @@
 #include <vnet/l2/l2_input.h>
 #include <vnet/l2/l2_output.h>
 
-extern index_t gbp_itf_add_and_lock (u32 sw_if_index, u32 bd_index);
-extern void gbp_itf_unlock (index_t index);
 
-extern void gbp_itf_set_l2_input_feature (index_t gii,
-					  index_t useri,
+#define foreach_gdb_l3_feature                  \
+  _(LEARN_IP4, "gbp-learn-ip4", "ip4-unicast") \
+  _(LEARN_IP6, "gbp-learn-ip6", "ip6-unicast")
+
+typedef enum gbp_itf_l3_feat_pos_t_
+{
+#define _(s,v,a) GBP_ITF_L3_FEAT_POS_##s,
+  foreach_gdb_l3_feature
+#undef _
+} gbp_itf_l3_feat_pos_t;
+
+typedef enum gbp_itf_l3_feat_t_
+{
+  GBP_ITF_L3_FEAT_NONE,
+#define _(s,v,a) GBP_ITF_L3_FEAT_##s = (1 << GBP_ITF_L3_FEAT_POS_##s),
+  foreach_gdb_l3_feature
+#undef _
+} gbp_itf_l3_feat_t;
+
+#define GBP_ITF_L3_FEAT_LEARN (GBP_ITF_L3_FEAT_LEARN_IP4|GBP_ITF_L3_FEAT_LEARN_IP6)
+
+typedef struct gbp_itf_hdl_t_
+{
+  union
+  {
+    struct
+    {
+      u32 gh_who;
+      u32 gh_which;
+    };
+  };
+} gbp_itf_hdl_t;
+
+#define GBP_ITF_HDL_INIT {.gh_which = ~0}
+const static gbp_itf_hdl_t GBP_ITF_HDL_INVALID = GBP_ITF_HDL_INIT;
+
+extern void gbp_itf_hdl_reset (gbp_itf_hdl_t * gh);
+extern bool gbp_itf_hdl_is_valid (gbp_itf_hdl_t gh);
+
+typedef void (*gbp_itf_free_fn_t) (u32 sw_if_index);
+
+extern gbp_itf_hdl_t gbp_itf_l2_add_and_lock (u32 sw_if_index, u32 bd_index);
+extern gbp_itf_hdl_t gbp_itf_l3_add_and_lock (u32 sw_if_index, index_t gri);
+extern gbp_itf_hdl_t gbp_itf_l2_add_and_lock_w_free (u32 sw_if_index,
+						     u32 bd_index,
+						     gbp_itf_free_fn_t ff);
+extern gbp_itf_hdl_t gbp_itf_l3_add_and_lock_w_free (u32 sw_if_index,
+						     index_t gri,
+						     gbp_itf_free_fn_t ff);
+
+extern void gbp_itf_unlock (gbp_itf_hdl_t * hdl);
+extern void gbp_itf_lock (gbp_itf_hdl_t hdl);
+extern gbp_itf_hdl_t gbp_itf_clone_and_lock (gbp_itf_hdl_t hdl);
+extern u32 gbp_itf_get_sw_if_index (gbp_itf_hdl_t hdl);
+
+extern void gbp_itf_l2_set_input_feature (gbp_itf_hdl_t hdl,
 					  l2input_feat_masks_t feats);
-extern void gbp_itf_set_l2_output_feature (index_t gii,
-					   index_t useri,
+extern void gbp_itf_l2_set_output_feature (gbp_itf_hdl_t hdl,
 					   l2output_feat_masks_t feats);
 
-extern u8 *format_gbp_itf (u8 * s, va_list * args);
+extern void gbp_itf_l3_set_input_feature (gbp_itf_hdl_t hdl,
+					  gbp_itf_l3_feat_t feats);
+
+extern u8 *format_gbp_itf_hdl (u8 * s, va_list * args);
 
 #endif
 
diff --git a/src/plugins/gbp/gbp_learn.c b/src/plugins/gbp/gbp_learn.c
index 612af98..af3a6fb 100644
--- a/src/plugins/gbp/gbp_learn.c
+++ b/src/plugins/gbp/gbp_learn.c
@@ -22,35 +22,21 @@
 gbp_learn_main_t gbp_learn_main;
 
 void
-gbp_learn_enable (u32 sw_if_index, gbb_learn_mode_t mode)
+gbp_learn_enable (u32 sw_if_index)
 {
-  if (GBP_LEARN_MODE_L2 == mode)
-    {
-      l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_LEARN, 1);
-    }
-  else
-    {
-      vnet_feature_enable_disable ("ip4-unicast",
-				   "gbp-learn-ip4", sw_if_index, 1, 0, 0);
-      vnet_feature_enable_disable ("ip6-unicast",
-				   "gbp-learn-ip6", sw_if_index, 1, 0, 0);
-    }
+  vnet_feature_enable_disable ("ip4-unicast",
+			       "gbp-learn-ip4", sw_if_index, 1, 0, 0);
+  vnet_feature_enable_disable ("ip6-unicast",
+			       "gbp-learn-ip6", sw_if_index, 1, 0, 0);
 }
 
 void
-gbp_learn_disable (u32 sw_if_index, gbb_learn_mode_t mode)
+gbp_learn_disable (u32 sw_if_index)
 {
-  if (GBP_LEARN_MODE_L2 == mode)
-    {
-      l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_LEARN, 0);
-    }
-  else
-    {
-      vnet_feature_enable_disable ("ip4-unicast",
-				   "gbp-learn-ip4", sw_if_index, 0, 0, 0);
-      vnet_feature_enable_disable ("ip6-unicast",
-				   "gbp-learn-ip6", sw_if_index, 0, 0, 0);
-    }
+  vnet_feature_enable_disable ("ip4-unicast",
+			       "gbp-learn-ip4", sw_if_index, 0, 0, 0);
+  vnet_feature_enable_disable ("ip6-unicast",
+			       "gbp-learn-ip6", sw_if_index, 0, 0, 0);
 }
 
 static clib_error_t *
diff --git a/src/plugins/gbp/gbp_learn.h b/src/plugins/gbp/gbp_learn.h
index 15a9fec..a000905 100644
--- a/src/plugins/gbp/gbp_learn.h
+++ b/src/plugins/gbp/gbp_learn.h
@@ -23,12 +23,6 @@
  */
 #define GBP_ENDPOINT_HASH_LEARN_RATE (1e-2)
 
-typedef enum gbp_learn_mode_t_
-{
-  GBP_LEARN_MODE_L2,
-  GBP_LEARN_MODE_L3,
-} gbb_learn_mode_t;
-
 /**
  * Grouping of global data for the GBP source EPG classification feature
  */
@@ -53,8 +47,8 @@
 
 extern gbp_learn_main_t gbp_learn_main;
 
-extern void gbp_learn_enable (u32 sw_if_index, gbb_learn_mode_t mode);
-extern void gbp_learn_disable (u32 sw_if_index, gbb_learn_mode_t mode);
+extern void gbp_learn_enable (u32 sw_if_index);
+extern void gbp_learn_disable (u32 sw_if_index);
 
 #endif
 
diff --git a/src/plugins/gbp/gbp_learn_node.c b/src/plugins/gbp/gbp_learn_node.c
index 42d1ceb..113969a 100644
--- a/src/plugins/gbp/gbp_learn_node.c
+++ b/src/plugins/gbp/gbp_learn_node.c
@@ -245,8 +245,8 @@
 	  /*
 	   * check for new EP or a moved EP
 	   */
-	  if (NULL == ge0 || ge0->ge_fwd.gef_itf != sw_if_index0)
-
+	  if (NULL == ge0 ||
+	      gbp_itf_get_sw_if_index (ge0->ge_fwd.gef_itf) != sw_if_index0)
 	    {
 	      /*
 	       * use the last 4 bytes of the mac address as the hash for the EP
diff --git a/src/plugins/gbp/gbp_recirc.c b/src/plugins/gbp/gbp_recirc.c
index 87d0d05..2b45d48 100644
--- a/src/plugins/gbp/gbp_recirc.c
+++ b/src/plugins/gbp/gbp_recirc.c
@@ -108,7 +108,7 @@
       /*
        * bind to the bridge-domain of the EPG
        */
-      gr->gr_itf = gbp_itf_add_and_lock (gr->gr_sw_if_index, gg->gg_bd_index);
+      gr->gr_itf = gbp_itf_l2_add_and_lock (gr->gr_sw_if_index, gg->gg_gbd);
 
       /*
        * set the interface into L2 emulation mode
@@ -211,7 +211,7 @@
       ip6_sw_interface_enable_disable (gr->gr_sw_if_index, 0);
       l2e_disable (gr->gr_sw_if_index);
 
-      gbp_itf_unlock (gr->gr_itf);
+      gbp_itf_unlock (&gr->gr_itf);
 
       gbp_endpoint_group_unlock (gr->gr_epgi);
       gbp_recirc_db[sw_if_index] = INDEX_INVALID;
diff --git a/src/plugins/gbp/gbp_recirc.h b/src/plugins/gbp/gbp_recirc.h
index 3af77b4..2f3354b 100644
--- a/src/plugins/gbp/gbp_recirc.h
+++ b/src/plugins/gbp/gbp_recirc.h
@@ -17,6 +17,7 @@
 #define __GBP_RECIRC_H__
 
 #include <plugins/gbp/gbp_types.h>
+#include <plugins/gbp/gbp_itf.h>
 #include <vnet/fib/fib_types.h>
 
 /**
@@ -51,7 +52,7 @@
   /**
    */
   u32 gr_sw_if_index;
-  u32 gr_itf;
+  gbp_itf_hdl_t gr_itf;
 
   /**
    * The endpoint created to represent the reric interface
diff --git a/src/plugins/gbp/gbp_route_domain.h b/src/plugins/gbp/gbp_route_domain.h
index dd7adf0..897c1bd 100644
--- a/src/plugins/gbp/gbp_route_domain.h
+++ b/src/plugins/gbp/gbp_route_domain.h
@@ -37,12 +37,6 @@
   u32 grd_table_id[FIB_PROTOCOL_IP_MAX];
 
   /**
-   * The RD's VNI interface on which packets from unkown endpoints
-   * arrive
-   */
-  u32 grd_vni_sw_if_index;
-
-  /**
    * The interfaces on which to send packets to unnknown EPs
    */
   u32 grd_uu_sw_if_index[FIB_PROTOCOL_IP_MAX];
diff --git a/src/plugins/gbp/gbp_vxlan.c b/src/plugins/gbp/gbp_vxlan.c
index de635d0..643089e 100644
--- a/src/plugins/gbp/gbp_vxlan.c
+++ b/src/plugins/gbp/gbp_vxlan.c
@@ -14,7 +14,6 @@
  */
 
 #include <plugins/gbp/gbp_vxlan.h>
-#include <plugins/gbp/gbp_itf.h>
 #include <plugins/gbp/gbp_learn.h>
 #include <plugins/gbp/gbp_bridge_domain.h>
 #include <plugins/gbp/gbp_route_domain.h>
@@ -30,9 +29,8 @@
  */
 typedef struct vxlan_tunnel_ref_t_
 {
+  gbp_itf_hdl_t vxr_itf;
   u32 vxr_sw_if_index;
-  index_t vxr_itf;
-  u32 vxr_locks;
   index_t vxr_parent;
   gbp_vxlan_tunnel_layer_t vxr_layer;
 } vxlan_tunnel_ref_t;
@@ -45,27 +43,27 @@
 /**
  * Logger
  */
-vlib_log_class_t gt_logger;
+static vlib_log_class_t gt_logger;
 
 /**
  * Pool of template tunnels
  */
-gbp_vxlan_tunnel_t *gbp_vxlan_tunnel_pool;
+static gbp_vxlan_tunnel_t *gbp_vxlan_tunnel_pool;
 
 /**
  * Pool of child tunnels
  */
-vxlan_tunnel_ref_t *vxlan_tunnel_ref_pool;
+static vxlan_tunnel_ref_t *vxlan_tunnel_ref_pool;
 
 /**
  * DB of template interfaces by SW interface index
  */
-index_t *gbp_vxlan_tunnel_db;
+static index_t *gbp_vxlan_tunnel_db;
 
 /**
  * DB of child interfaces by SW interface index
  */
-index_t *vxlan_tunnel_ref_db;
+static index_t *vxlan_tunnel_ref_db;
 
 /**
  * handle registered with the ;unt infra
@@ -82,7 +80,6 @@
     vlib_log_debug (gt_logger, __VA_ARGS__);
 
 
-
 gbp_vxlan_tunnel_t *
 gbp_vxlan_tunnel_get (index_t gti)
 {
@@ -103,13 +100,37 @@
 
   vxr = vxlan_tunnel_ref_get (vxri);
 
-  s = format (s, "[%U locks:%d]", format_vnet_sw_if_index_name,
-	      vnet_get_main (), vxr->vxr_sw_if_index, vxr->vxr_locks);
+  s = format (s, "[%U]", format_gbp_itf_hdl, vxr->vxr_itf);
 
   return (s);
 }
 
-static u32
+static void
+gdb_vxlan_dep_del (u32 sw_if_index)
+{
+  vxlan_tunnel_ref_t *vxr;
+  gbp_vxlan_tunnel_t *gt;
+  index_t vxri;
+  u32 pos;
+
+  vxr = vxlan_tunnel_ref_get (vxlan_tunnel_ref_db[sw_if_index]);
+  vxri = vxr - vxlan_tunnel_ref_pool;
+  gt = gbp_vxlan_tunnel_get (vxr->vxr_parent);
+
+  GBP_VXLAN_TUN_DBG ("del-dep:%U", format_vxlan_tunnel_ref, vxri);
+
+  vxlan_tunnel_ref_db[vxr->vxr_sw_if_index] = INDEX_INVALID;
+  pos = vec_search (gt->gt_tuns, vxri);
+
+  ASSERT (~0 != pos);
+  vec_del1 (gt->gt_tuns, pos);
+
+  vnet_vxlan_gbp_tunnel_del (vxr->vxr_sw_if_index);
+
+  pool_put (vxlan_tunnel_ref_pool, vxr);
+}
+
+static gbp_itf_hdl_t
 gdb_vxlan_dep_add (gbp_vxlan_tunnel_t * gt,
 		   const ip46_address_t * src, const ip46_address_t * dst)
 {
@@ -136,7 +157,7 @@
       vxri = vxlan_tunnel_ref_db[sw_if_index];
 
       vxr = vxlan_tunnel_ref_get (vxri);
-      vxr->vxr_locks++;
+      gbp_itf_lock (vxr->vxr_itf);
     }
   else if (0 == rv)
     {
@@ -151,7 +172,6 @@
       vxri = (vxr - vxlan_tunnel_ref_pool);
       vxr->vxr_parent = gt - gbp_vxlan_tunnel_pool;
       vxr->vxr_sw_if_index = sw_if_index;
-      vxr->vxr_locks = 1;
       vxr->vxr_layer = gt->gt_layer;
 
       /*
@@ -170,8 +190,8 @@
 	  gbp_bridge_domain_t *gbd;
 
 	  gbd = gbp_bridge_domain_get (gt->gt_gbd);
-	  vxr->vxr_itf = gbp_itf_add_and_lock (vxr->vxr_sw_if_index,
-					       gt->gt_bd_index);
+	  vxr->vxr_itf = gbp_itf_l2_add_and_lock_w_free
+	    (vxr->vxr_sw_if_index, gt->gt_gbd, gdb_vxlan_dep_del);
 
 	  ofeat = L2OUTPUT_FEAT_GBP_POLICY_MAC;
 	  ifeat = L2INPUT_FEAT_NONE;
@@ -179,27 +199,23 @@
 	  if (!(gbd->gb_flags & GBP_BD_FLAG_DO_NOT_LEARN))
 	    ifeat |= L2INPUT_FEAT_GBP_LEARN;
 
-	  gbp_itf_set_l2_output_feature (vxr->vxr_itf,
-					 vxr->vxr_sw_if_index, ofeat);
-	  gbp_itf_set_l2_input_feature (vxr->vxr_itf,
-					vxr->vxr_sw_if_index, ifeat);
+	  gbp_itf_l2_set_output_feature (vxr->vxr_itf, ofeat);
+	  gbp_itf_l2_set_input_feature (vxr->vxr_itf, ifeat);
 	}
       else
 	{
-	  const gbp_route_domain_t *grd;
-	  fib_protocol_t fproto;
+	  vxr->vxr_itf = gbp_itf_l3_add_and_lock_w_free
+	    (vxr->vxr_sw_if_index, gt->gt_grd, gdb_vxlan_dep_del);
 
-	  grd = gbp_route_domain_get (gt->gt_grd);
-
-	  FOR_EACH_FIB_IP_PROTOCOL (fproto)
-	    ip_table_bind (fproto, vxr->vxr_sw_if_index,
-			   grd->grd_table_id[fproto], 1);
-
-	  gbp_learn_enable (vxr->vxr_sw_if_index, GBP_LEARN_MODE_L3);
+	  gbp_itf_l3_set_input_feature (vxr->vxr_itf, GBP_ITF_L3_FEAT_LEARN);
 	}
     }
+  else
+    {
+      return (GBP_ITF_HDL_INVALID);
+    }
 
-  return (sw_if_index);
+  return (vxr->vxr_itf);
 }
 
 u32
@@ -217,6 +233,22 @@
   return (gt->gt_sw_if_index);
 }
 
+gbp_itf_hdl_t
+vxlan_gbp_tunnel_lock_itf (u32 sw_if_index)
+{
+  ASSERT ((sw_if_index < vec_len (vxlan_tunnel_ref_db)) &&
+	  (INDEX_INVALID != vxlan_tunnel_ref_db[sw_if_index]));
+
+  vxlan_tunnel_ref_t *vxr;
+
+  vxr = vxlan_tunnel_ref_get (vxlan_tunnel_ref_db[sw_if_index]);
+
+  gbp_itf_lock (vxr->vxr_itf);
+
+  return (vxr->vxr_itf);
+}
+
+
 gbp_vxlan_tunnel_type_t
 gbp_vxlan_tunnel_get_type (u32 sw_if_index)
 {
@@ -235,7 +267,7 @@
   return (GBP_VXLAN_TEMPLATE_TUNNEL);
 }
 
-u32
+gbp_itf_hdl_t
 gbp_vxlan_tunnel_clone_and_lock (u32 sw_if_index,
 				 const ip46_address_t * src,
 				 const ip46_address_t * dst)
@@ -246,84 +278,26 @@
   gti = gbp_vxlan_tunnel_db[sw_if_index];
 
   if (INDEX_INVALID == gti)
-    return (~0);
+    return (GBP_ITF_HDL_INVALID);
 
   gt = pool_elt_at_index (gbp_vxlan_tunnel_pool, gti);
 
   return (gdb_vxlan_dep_add (gt, src, dst));
 }
 
-static void
-gdb_vxlan_dep_del (index_t vxri)
-{
-  vxlan_tunnel_ref_t *vxr;
-  gbp_vxlan_tunnel_t *gt;
-  u32 pos;
-
-  vxr = vxlan_tunnel_ref_get (vxri);
-  gt = gbp_vxlan_tunnel_get (vxr->vxr_parent);
-
-  GBP_VXLAN_TUN_DBG ("del-dep:%U", format_vxlan_tunnel_ref, vxri);
-
-  vxlan_tunnel_ref_db[vxr->vxr_sw_if_index] = INDEX_INVALID;
-  pos = vec_search (gt->gt_tuns, vxri);
-
-  ASSERT (~0 != pos);
-  vec_del1 (gt->gt_tuns, pos);
-
-  if (GBP_VXLAN_TUN_L2 == vxr->vxr_layer)
-    {
-      gbp_itf_set_l2_output_feature (vxr->vxr_itf, vxr->vxr_sw_if_index,
-				     L2OUTPUT_FEAT_NONE);
-      gbp_itf_set_l2_input_feature (vxr->vxr_itf, vxr->vxr_sw_if_index,
-				    L2INPUT_FEAT_NONE);
-      gbp_itf_unlock (vxr->vxr_itf);
-    }
-  else
-    {
-      fib_protocol_t fproto;
-
-      FOR_EACH_FIB_IP_PROTOCOL (fproto)
-	ip_table_bind (fproto, vxr->vxr_sw_if_index, 0, 0);
-      gbp_learn_disable (vxr->vxr_sw_if_index, GBP_LEARN_MODE_L3);
-    }
-
-  vnet_vxlan_gbp_tunnel_del (vxr->vxr_sw_if_index);
-
-  pool_put (vxlan_tunnel_ref_pool, vxr);
-}
-
 void
 vxlan_gbp_tunnel_unlock (u32 sw_if_index)
 {
-  vxlan_tunnel_ref_t *vxr;
-  index_t vxri;
+  /* vxlan_tunnel_ref_t *vxr; */
+  /* index_t vxri; */
 
-  vxri = vxlan_tunnel_ref_db[sw_if_index];
+  /* vxri = vxlan_tunnel_ref_db[sw_if_index]; */
 
-  ASSERT (vxri != INDEX_INVALID);
+  /* ASSERT (vxri != INDEX_INVALID); */
 
-  vxr = vxlan_tunnel_ref_get (vxri);
-  vxr->vxr_locks--;
+  /* vxr = vxlan_tunnel_ref_get (vxri); */
 
-  if (0 == vxr->vxr_locks)
-    {
-      gdb_vxlan_dep_del (vxri);
-    }
-}
-
-void
-vxlan_gbp_tunnel_lock (u32 sw_if_index)
-{
-  vxlan_tunnel_ref_t *vxr;
-  index_t vxri;
-
-  vxri = vxlan_tunnel_ref_db[sw_if_index];
-
-  ASSERT (vxri != INDEX_INVALID);
-
-  vxr = vxlan_tunnel_ref_get (vxri);
-  vxr->vxr_locks++;
+  /* gdb_vxlan_dep_del (vxri); */
 }
 
 void
@@ -374,23 +348,20 @@
   gbp_vxlan_tunnel_t *gt = gbp_vxlan_tunnel_get (dev_instance);
   index_t *vxri;
 
-  s = format (s, "GBP VXLAN tunnel: hw:%d sw:%d vni:%d %U",
-	      gt->gt_hw_if_index, gt->gt_sw_if_index, gt->gt_vni,
+  s = format (s, " [%d] gbp-vxlan-tunnel: hw:%d sw:%d vni:%d %U",
+	      dev_instance, gt->gt_hw_if_index,
+	      gt->gt_sw_if_index, gt->gt_vni,
 	      format_gbp_vxlan_tunnel_layer, gt->gt_layer);
   if (GBP_VXLAN_TUN_L2 == gt->gt_layer)
-    s = format (s, " BD:%d bd-index:%d", gt->gt_bd_rd_id, gt->gt_bd_index);
+    s = format (s, " BD:%d gbd-index:%d", gt->gt_bd_rd_id, gt->gt_gbd);
   else
-    s = format (s, " RD:%d fib-index:[%d,%d]",
-		gt->gt_bd_rd_id,
-		gt->gt_fib_index[FIB_PROTOCOL_IP4],
-		gt->gt_fib_index[FIB_PROTOCOL_IP6]);
+    s = format (s, " RD:%d grd-index:%d", gt->gt_bd_rd_id, gt->gt_grd);
 
-  s = format (s, " children:[");
+  s = format (s, "   dependents:");
   vec_foreach (vxri, gt->gt_tuns)
   {
-    s = format (s, "%U, ", format_vxlan_tunnel_ref, *vxri);
+    s = format (s, "\n    %U, ", format_vxlan_tunnel_ref, *vxri);
   }
-  s = format (s, "]");
 
   return s;
 }
@@ -536,35 +507,18 @@
 	  gb = gbp_bridge_domain_get (gbi);
 
 	  gt->gt_gbd = gbi;
-	  gt->gt_bd_index = gb->gb_bd_index;
 	  gb->gb_vni = gti;
 	  /* set it up as a GBP interface */
-	  gt->gt_itf = gbp_itf_add_and_lock (gt->gt_sw_if_index,
-					     gt->gt_bd_index);
-	  gbp_learn_enable (gt->gt_sw_if_index, GBP_LEARN_MODE_L2);
+	  gt->gt_itf = gbp_itf_l2_add_and_lock (gt->gt_sw_if_index,
+						gt->gt_gbd);
+	  gbp_itf_l2_set_input_feature (gt->gt_itf, L2INPUT_FEAT_GBP_LEARN);
 	}
       else
 	{
-	  gbp_route_domain_t *grd;
-	  fib_protocol_t fproto;
-
-	  grd = gbp_route_domain_get (grdi);
-
 	  gt->gt_grd = grdi;
-	  grd->grd_vni_sw_if_index = gt->gt_sw_if_index;
-
-	  gbp_learn_enable (gt->gt_sw_if_index, GBP_LEARN_MODE_L3);
-
-	  ip4_sw_interface_enable_disable (gt->gt_sw_if_index, 1);
-	  ip6_sw_interface_enable_disable (gt->gt_sw_if_index, 1);
-
-	  FOR_EACH_FIB_IP_PROTOCOL (fproto)
-	  {
-	    gt->gt_fib_index[fproto] = grd->grd_fib_index[fproto];
-
-	    ip_table_bind (fproto, gt->gt_sw_if_index,
-			   grd->grd_table_id[fproto], 1);
-	  }
+	  gt->gt_itf = gbp_itf_l3_add_and_lock (gt->gt_sw_if_index,
+						gt->gt_grd);
+	  gbp_itf_l3_set_input_feature (gt->gt_itf, GBP_ITF_L3_FEAT_LEARN);
 	}
 
       /*
@@ -616,23 +570,14 @@
       ASSERT (0 == vec_len (gt->gt_tuns));
       vec_free (gt->gt_tuns);
 
+      gbp_itf_unlock (&gt->gt_itf);
+
       if (GBP_VXLAN_TUN_L2 == gt->gt_layer)
 	{
-	  gbp_learn_disable (gt->gt_sw_if_index, GBP_LEARN_MODE_L2);
-	  gbp_itf_unlock (gt->gt_itf);
 	  gbp_bridge_domain_unlock (gt->gt_gbd);
 	}
       else
 	{
-	  fib_protocol_t fproto;
-
-	  FOR_EACH_FIB_IP_PROTOCOL (fproto)
-	    ip_table_bind (fproto, gt->gt_sw_if_index, 0, 0);
-
-	  ip4_sw_interface_enable_disable (gt->gt_sw_if_index, 0);
-	  ip6_sw_interface_enable_disable (gt->gt_sw_if_index, 0);
-
-	  gbp_learn_disable (gt->gt_sw_if_index, GBP_LEARN_MODE_L3);
 	  gbp_route_domain_unlock (gt->gt_grd);
 	}
 
@@ -654,6 +599,9 @@
 gbp_vxlan_show (vlib_main_t * vm,
 		unformat_input_t * input, vlib_cli_command_t * cmd)
 {
+
+  vlib_cli_output (vm, "GBP-VXLAN Interfaces:");
+
   gbp_vxlan_walk (gbp_vxlan_tunnel_show_one, vm);
 
   return (NULL);
diff --git a/src/plugins/gbp/gbp_vxlan.h b/src/plugins/gbp/gbp_vxlan.h
index 908abc2..6e01dc1 100644
--- a/src/plugins/gbp/gbp_vxlan.h
+++ b/src/plugins/gbp/gbp_vxlan.h
@@ -17,6 +17,7 @@
 #define __GBP_VXLAN_H__
 
 #include <vnet/fib/fib_types.h>
+#include <plugins/gbp/gbp_itf.h>
 
 #define forecah_gbp_vxlan_tunnel_layer          \
   _(L2, "l2")                                   \
@@ -52,10 +53,6 @@
     struct
     {
       /**
-       * BD index (if L2)
-       */
-      u32 gt_bd_index;
-      /**
        * Reference to the GPB-BD
        */
       index_t gt_gbd;
@@ -63,10 +60,6 @@
     struct
     {
       /**
-       * FIB inidices (if L3)
-       */
-      u32 gt_fib_index[FIB_PROTOCOL_IP_MAX];
-      /**
        * References to the GBP-RD
        */
       index_t gt_grd;
@@ -76,7 +69,7 @@
   /**
    * gbp-itf config for this interface
    */
-  index_t gt_itf;
+  gbp_itf_hdl_t gt_itf;
 
   /**
    * list of child vxlan-gbp tunnels built from this template
@@ -115,13 +108,14 @@
 
 extern gbp_vxlan_tunnel_type_t gbp_vxlan_tunnel_get_type (u32 sw_if_index);
 
-extern u32 gbp_vxlan_tunnel_clone_and_lock (u32 parent_tunnel,
-					    const ip46_address_t * src,
-					    const ip46_address_t * dst);
+extern gbp_itf_hdl_t gbp_vxlan_tunnel_clone_and_lock (u32 parent_tunnel,
+						      const ip46_address_t *
+						      src,
+						      const ip46_address_t *
+						      dst);
 
-extern void vxlan_gbp_tunnel_lock (u32 sw_if_index);
-extern void vxlan_gbp_tunnel_unlock (u32 sw_if_index);
 extern u32 vxlan_gbp_tunnel_get_parent (u32 sw_if_index);
+extern gbp_itf_hdl_t vxlan_gbp_tunnel_lock_itf (u32 sw_if_index);
 
 typedef walk_rc_t (*gbp_vxlan_cb_t) (gbp_vxlan_tunnel_t * gt, void *ctx);
 extern void gbp_vxlan_walk (gbp_vxlan_cb_t cb, void *ctx);
diff --git a/test/test_gbp.py b/test/test_gbp.py
index 8b62540..9cf1817 100644
--- a/test/test_gbp.py
+++ b/test/test_gbp.py
@@ -1969,10 +1969,10 @@
                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
                 []),
-                VppGbpContractRule(
-                    VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
-                    VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
-                    [])],
+             VppGbpContractRule(
+                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+                 [])],
             [ETH_P_IP, ETH_P_IPV6])
         c1.add_vpp_config()
 
@@ -1987,8 +1987,8 @@
         #
         # send UU packets from the local EP
         #
-        self.logger.info(self.vapi.cli("sh bridge 1 detail"))
         self.logger.info(self.vapi.cli("sh gbp bridge"))
+        self.logger.info(self.vapi.cli("sh bridge-domain 1 detail"))
         p_uu = (Ether(src=ep.mac, dst="00:11:11:11:11:11") /
                 IP(dst="10.0.0.133", src=ep.ip4.address) /
                 UDP(sport=1234, dport=1234) /
@@ -2015,6 +2015,26 @@
             self.assertFalse(rx[VXLAN].gpflags.A)
             self.assertFalse(rx[VXLAN].gpflags.D)
 
+        acl = VppGbpAcl(self)
+        rule = acl.create_rule(permit_deny=1, proto=17)
+        rule2 = acl.create_rule(is_ipv6=1, permit_deny=1, proto=17)
+        acl_index = acl.add_vpp_config([rule, rule2])
+        c2 = VppGbpContract(
+            self, 401, epg_330.sclass, epg_220.sclass, acl_index,
+            [VppGbpContractRule(
+                VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+                VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+                []),
+                VppGbpContractRule(
+                    VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+                    VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+                    [])],
+            [ETH_P_IP, ETH_P_IPV6])
+        c2.add_vpp_config()
+
+        for l in learnt:
+            self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index,
+                                     mac=l['mac'])
         #
         # Check v6 Endpoints learning
         #
@@ -2025,17 +2045,53 @@
                  IP(src=self.pg2.remote_hosts[1].ip4,
                     dst=self.pg2.local_ip4) /
                  UDP(sport=1234, dport=48879) /
-                 VXLAN(vni=99, gpid=113, flags=0x88, gpflags='A') /
+                 VXLAN(vni=99, gpid=113, flags=0x88) /
                  Ether(src=l['mac'], dst=ep.mac) /
                  IPv6(src=l['ip6'], dst=ep.ip6.address) /
                  UDP(sport=1234, dport=1234) /
                  Raw('\xa5' * 100))
 
             rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
+            rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
 
-            self.assertTrue(find_gbp_endpoint(self,
-                                              vx_tun_l2_1.sw_if_index,
-                                              mac=l['mac']))
+            self.assertTrue(find_gbp_endpoint(
+                self,
+                vx_tun_l2_1.sw_if_index,
+                ip=l['ip6'],
+                tep=[self.pg2.local_ip4,
+                     self.pg2.remote_hosts[1].ip4]))
+
+        self.logger.info(self.vapi.cli("sh int"))
+        self.logger.info(self.vapi.cli("sh vxlan-gbp tunnel"))
+        self.logger.info(self.vapi.cli("sh gbp vxlan"))
+        self.logger.info(self.vapi.cli("sh gbp endpoint"))
+        self.logger.info(self.vapi.cli("sh gbp interface"))
+
+        #
+        # EP moves to a different TEP
+        #
+        for l in learnt:
+            # a packet with an sclass from a known EPG
+            p = (Ether(src=self.pg2.remote_mac,
+                       dst=self.pg2.local_mac) /
+                 IP(src=self.pg2.remote_hosts[2].ip4,
+                    dst=self.pg2.local_ip4) /
+                 UDP(sport=1234, dport=48879) /
+                 VXLAN(vni=99, gpid=113, flags=0x88) /
+                 Ether(src=l['mac'], dst=ep.mac) /
+                 IPv6(src=l['ip6'], dst=ep.ip6.address) /
+                 UDP(sport=1234, dport=1234) /
+                 Raw('\xa5' * 100))
+
+            rx = self.send_and_expect(self.pg2, p * 1, self.pg0)
+            rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
+
+            self.assertTrue(find_gbp_endpoint(
+                self,
+                vx_tun_l2_1.sw_if_index,
+                mac=l['mac'],
+                tep=[self.pg2.local_ip4,
+                     self.pg2.remote_hosts[2].ip4]))
 
         #
         # v6 remote EP reachability
@@ -2050,7 +2106,7 @@
 
             for rx in rxs:
                 self.assertEqual(rx[IP].src, self.pg2.local_ip4)
-                self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
+                self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[2].ip4)
                 self.assertEqual(rx[UDP].dport, 48879)
                 # the UDP source port is a random value for hashing
                 self.assertEqual(rx[VXLAN].gpid, 112)
@@ -2071,9 +2127,6 @@
         self.pg3.unconfig_ip4()
         self.pg4.unconfig_ip4()
 
-        self.logger.info(self.vapi.cli("sh int"))
-        self.logger.info(self.vapi.cli("sh gbp vxlan"))
-
     def test_gbp_contract(self):
         """ GBP Contracts """
 
@@ -3819,9 +3872,6 @@
         vlan_144.admin_up()
         # vlan_102 is not poped
 
-        ext_itf = VppGbpExtItf(self, self.loop0, bd1, rd1)
-        ext_itf.add_vpp_config()
-
         #
         # an unicast vxlan-gbp for inter-RD traffic
         #
@@ -4592,9 +4642,6 @@
         VppL2Vtr(self, self.vlan_100, L2_VTR_OP.L2_POP_1).add_vpp_config()
         VppL2Vtr(self, self.vlan_101, L2_VTR_OP.L2_POP_1).add_vpp_config()
 
-        ext_itf = VppGbpExtItf(self, self.loop0, bd1, rd1)
-        ext_itf.add_vpp_config()
-
         #
         # vlan_100 and vlan_101 are anonymous l3-out interfaces
         #