GBP Endpoint Learning

Learning GBP endpoints over vxlan-gbp tunnels

Change-Id: I1db9fda5a16802d9ad8b4efd4e475614f3b21502
Signed-off-by: Neale Ranns <neale.ranns@cisco.com>
diff --git a/src/vnet/CMakeLists.txt b/src/vnet/CMakeLists.txt
index a45f921..6d26b5a 100644
--- a/src/vnet/CMakeLists.txt
+++ b/src/vnet/CMakeLists.txt
@@ -679,6 +679,7 @@
   vxlan-gbp/encap.c
   vxlan-gbp/vxlan_gbp_api.c
   vxlan-gbp/vxlan_gbp.c
+  vxlan-gbp/vxlan_gbp_packet.c
 )
 
 list(APPEND VNET_HEADERS
diff --git a/src/vnet/ethernet/mac_address.h b/src/vnet/ethernet/mac_address.h
index 7b4390d..a249cb5 100644
--- a/src/vnet/ethernet/mac_address.h
+++ b/src/vnet/ethernet/mac_address.h
@@ -37,6 +37,13 @@
   clib_memcpy (mac->bytes, bytes, 6);
 }
 
+static_always_inline void
+mac_address_to_bytes (const mac_address_t * mac, u8 * bytes)
+{
+  /* zero out the last 2 bytes, then copy over only 6 */
+  clib_memcpy (bytes, mac->bytes, 6);
+}
+
 static_always_inline int
 mac_address_is_zero (const mac_address_t * mac)
 {
@@ -57,6 +64,12 @@
   mac->bytes[5] = 0;
 }
 
+static_always_inline void
+mac_address_copy (mac_address_t * dst, const mac_address_t * src)
+{
+  mac_address_from_bytes (dst, src->bytes);
+}
+
 extern uword unformat_mac_address_t (unformat_input_t * input,
 				     va_list * args);
 extern u8 *format_mac_address_t (u8 * s, va_list * args);
diff --git a/src/vnet/interface_funcs.h b/src/vnet/interface_funcs.h
index a3bfdc9..b7d9007 100644
--- a/src/vnet/interface_funcs.h
+++ b/src/vnet/interface_funcs.h
@@ -271,6 +271,13 @@
     && vnet_sw_interface_is_api_visible (vnm, sw_if_index);
 }
 
+always_inline const u8 *
+vnet_sw_interface_get_hw_address (vnet_main_t * vnm, u32 sw_if_index)
+{
+  vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
+  return hw->hw_address;
+}
+
 always_inline uword
 vnet_hw_interface_get_flags (vnet_main_t * vnm, u32 hw_if_index)
 {
diff --git a/src/vnet/ip/ip_types_api.c b/src/vnet/ip/ip_types_api.c
index 11b5276..3d1f806 100644
--- a/src/vnet/ip/ip_types_api.c
+++ b/src/vnet/ip/ip_types_api.c
@@ -107,6 +107,7 @@
       break;
     }
   out->fp_len = in->address_length;
+  out->___fp___pad = 0;
   ip_address_decode (&in->address, &out->fp_addr);
 }
 
diff --git a/src/vnet/l2/l2.api b/src/vnet/l2/l2.api
index 8b65bc3..7c71ea6 100644
--- a/src/vnet/l2/l2.api
+++ b/src/vnet/l2/l2.api
@@ -483,7 +483,6 @@
   u32 context;
   u32 bd_id;
   u8 is_add;
-  u8 is_ipv6;
   vl_api_address_t ip;
   vl_api_mac_address_t mac;
 };
diff --git a/src/vnet/l2/l2_input.h b/src/vnet/l2/l2_input.h
index f55e703..57fca57 100644
--- a/src/vnet/l2/l2_input.h
+++ b/src/vnet/l2/l2_input.h
@@ -104,12 +104,13 @@
  _(FLOOD,         "l2-flood")                   \
  _(ARP_TERM,      "arp-term-l2bd")              \
  _(UU_FLOOD,      "l2-flood")                   \
- _(UU_FWD,        "l2-uu-fwd")                  \
  _(GBP_FWD,       "gbp-fwd")                    \
+ _(UU_FWD,        "l2-uu-fwd")                  \
  _(FWD,           "l2-fwd")                     \
  _(RW,            "l2-rw")                      \
  _(LEARN,         "l2-learn")                   \
  _(L2_EMULATION,  "l2-emulation")               \
+ _(GBP_LEARN,     "gbp-learn-l2")               \
  _(GBP_NULL_CLASSIFY, "gbp-null-classify")      \
  _(GBP_SRC_CLASSIFY,  "gbp-src-classify")       \
  _(VTR,           "l2-input-vtr")               \
diff --git a/src/vnet/l2/l2_output.h b/src/vnet/l2/l2_output.h
index a6db776..33eeb8e 100644
--- a/src/vnet/l2/l2_output.h
+++ b/src/vnet/l2/l2_output.h
@@ -81,7 +81,8 @@
 #define foreach_l2output_feat \
  _(OUTPUT,            "interface-output")           \
  _(SPAN,              "span-l2-output")             \
- _(GBP_POLICY,        "gbp-policy")                 \
+ _(GBP_POLICY_PORT,   "gbp-policy-port")            \
+ _(GBP_POLICY_MAC,    "gbp-policy-mac")             \
  _(CFM,               "feature-bitmap-drop")        \
  _(QOS,               "feature-bitmap-drop")        \
  _(ACL,               "l2-output-acl")              \
diff --git a/src/vnet/vxlan-gbp/decap.c b/src/vnet/vxlan-gbp/decap.c
index 1602e94..0d361a3 100644
--- a/src/vnet/vxlan-gbp/decap.c
+++ b/src/vnet/vxlan-gbp/decap.c
@@ -29,6 +29,7 @@
   u32 error;
   u32 vni;
   u16 sclass;
+  u8 flags;
 } vxlan_gbp_rx_trace_t;
 
 static u8 *
@@ -44,8 +45,10 @@
 		   t->vni);
   return format (s,
 		 "VXLAN_GBP decap from vxlan_gbp_tunnel%d vni %d sclass %d"
-		 " next %d error %d",
-		 t->tunnel_index, t->vni, t->sclass, t->next_index, t->error);
+		 " flags %U next %d error %d",
+		 t->tunnel_index, t->vni, t->sclass,
+		 format_vxlan_gbp_header_gpflags, t->flags,
+		 t->next_index, t->error);
 }
 
 always_inline u32
@@ -161,10 +164,34 @@
   return t0;
 }
 
+always_inline vxlan_gbp_input_next_t
+vxlan_gbp_tunnel_get_next (const vxlan_gbp_tunnel_t * t, vlib_buffer_t * b0)
+{
+  if (VXLAN_GBP_TUNNEL_MODE_L2 == t->mode)
+    return (VXLAN_GBP_INPUT_NEXT_L2_INPUT);
+  else
+    {
+      ethernet_header_t *e0;
+      u16 type0;
+
+      e0 = vlib_buffer_get_current (b0);
+      vlib_buffer_advance (b0, sizeof (*e0));
+      type0 = clib_net_to_host_u16 (e0->type);
+      switch (type0)
+	{
+	case ETHERNET_TYPE_IP4:
+	  return (VXLAN_GBP_INPUT_NEXT_IP4_INPUT);
+	case ETHERNET_TYPE_IP6:
+	  return (VXLAN_GBP_INPUT_NEXT_IP6_INPUT);
+	}
+    }
+  return (VXLAN_GBP_INPUT_NEXT_DROP);
+}
+
 always_inline uword
 vxlan_gbp_input (vlib_main_t * vm,
 		 vlib_node_runtime_t * node,
-		 vlib_frame_t * from_frame, u32 is_ip4)
+		 vlib_frame_t * from_frame, u8 is_ip4)
 {
   vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
   vnet_main_t *vnm = vxm->vnet_main;
@@ -239,10 +266,6 @@
 	      ip6_1 = cur1 - sizeof (udp_header_t) - sizeof (ip6_header_t);
 	    }
 
-	  /* pop vxlan_gbp */
-	  vlib_buffer_advance (b0, sizeof *vxlan_gbp0);
-	  vlib_buffer_advance (b1, sizeof *vxlan_gbp1);
-
 	  u32 fi0 = buf_fib_index (b0, is_ip4);
 	  u32 fi1 = buf_fib_index (b1, is_ip4);
 
@@ -270,16 +293,19 @@
 	  u32 len0 = vlib_buffer_length_in_chain (vm, b0);
 	  u32 len1 = vlib_buffer_length_in_chain (vm, b1);
 
-	  u32 next0, next1;
+	  vxlan_gbp_input_next_t next0, next1;
 	  u8 error0 = 0, error1 = 0;
 	  u8 flags0 = vxlan_gbp_get_flags (vxlan_gbp0);
 	  u8 flags1 = vxlan_gbp_get_flags (vxlan_gbp1);
+	  /* Required to make the l2 tag push / pop code work on l2 subifs */
+	  /* pop vxlan_gbp */
+	  vlib_buffer_advance (b0, sizeof *vxlan_gbp0);
+	  vlib_buffer_advance (b1, sizeof *vxlan_gbp1);
+
 	  /* Validate VXLAN_GBP tunnel encap-fib index against packet */
 	  if (PREDICT_FALSE
 	      (t0 == 0 || flags0 != (VXLAN_GBP_FLAGS_I | VXLAN_GBP_FLAGS_G)))
 	    {
-	      next0 = VXLAN_GBP_INPUT_NEXT_DROP;
-
 	      if (t0 != 0
 		  && flags0 != (VXLAN_GBP_FLAGS_I | VXLAN_GBP_FLAGS_G))
 		{
@@ -287,22 +313,18 @@
 		  vlib_increment_combined_counter
 		    (drop_counter, thread_index, stats_t0->sw_if_index, 1,
 		     len0);
+		  next0 = VXLAN_GBP_INPUT_NEXT_DROP;
 		}
 	      else
-		error0 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL;
+		{
+		  error0 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL;
+		  next0 = VXLAN_GBP_INPUT_NEXT_NO_TUNNEL;
+		}
 	      b0->error = node->errors[error0];
 	    }
 	  else
 	    {
-	      next0 = t0->decap_next_index;
-	      vnet_buffer2 (b0)->gbp.flags =
-		vxlan_gbp_get_gpflags (vxlan_gbp0);
-	      vnet_buffer2 (b0)->gbp.src_epg =
-		vxlan_gbp_get_sclass (vxlan_gbp0);
-
-	      /* Required to make the l2 tag push / pop code work on l2 subifs */
-	      if (PREDICT_TRUE (next0 == VXLAN_GBP_INPUT_NEXT_L2_INPUT))
-		vnet_update_l2_len (b0);
+	      next0 = vxlan_gbp_tunnel_get_next (t0, b0);
 
 	      /* Set packet input sw_if_index to unicast VXLAN tunnel for learning */
 	      vnet_buffer (b0)->sw_if_index[VLIB_RX] = t0->sw_if_index;
@@ -311,12 +333,13 @@
 	      pkts_decapsulated++;
 	    }
 
-	  /* Validate VXLAN_GBP tunnel encap-fib index against packet */
+	  vnet_buffer2 (b0)->gbp.flags = vxlan_gbp_get_gpflags (vxlan_gbp0);
+	  vnet_buffer2 (b0)->gbp.src_epg = vxlan_gbp_get_sclass (vxlan_gbp0);
+
+
 	  if (PREDICT_FALSE
 	      (t1 == 0 || flags1 != (VXLAN_GBP_FLAGS_I | VXLAN_GBP_FLAGS_G)))
 	    {
-	      next1 = VXLAN_GBP_INPUT_NEXT_DROP;
-
 	      if (t1 != 0
 		  && flags1 != (VXLAN_GBP_FLAGS_I | VXLAN_GBP_FLAGS_G))
 		{
@@ -324,22 +347,18 @@
 		  vlib_increment_combined_counter
 		    (drop_counter, thread_index, stats_t1->sw_if_index, 1,
 		     len1);
+		  next1 = VXLAN_GBP_INPUT_NEXT_DROP;
 		}
 	      else
-		error1 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL;
+		{
+		  error1 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL;
+		  next1 = VXLAN_GBP_INPUT_NEXT_NO_TUNNEL;
+		}
 	      b1->error = node->errors[error1];
 	    }
 	  else
 	    {
-	      next1 = t1->decap_next_index;
-	      vnet_buffer2 (b1)->gbp.flags =
-		vxlan_gbp_get_gpflags (vxlan_gbp1);
-	      vnet_buffer2 (b1)->gbp.src_epg =
-		vxlan_gbp_get_sclass (vxlan_gbp1);
-
-	      /* Required to make the l2 tag push / pop code work on l2 subifs */
-	      if (PREDICT_TRUE (next1 == VXLAN_GBP_INPUT_NEXT_L2_INPUT))
-		vnet_update_l2_len (b1);
+	      next1 = vxlan_gbp_tunnel_get_next (t1, b1);
 
 	      /* Set packet input sw_if_index to unicast VXLAN_GBP tunnel for learning */
 	      vnet_buffer (b1)->sw_if_index[VLIB_RX] = t1->sw_if_index;
@@ -349,6 +368,12 @@
 		(rx_counter, thread_index, stats_t1->sw_if_index, 1, len1);
 	    }
 
+	  vnet_buffer2 (b1)->gbp.flags = vxlan_gbp_get_gpflags (vxlan_gbp1);
+	  vnet_buffer2 (b1)->gbp.src_epg = vxlan_gbp_get_sclass (vxlan_gbp1);
+
+	  vnet_update_l2_len (b0);
+	  vnet_update_l2_len (b1);
+
 	  if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
 	    {
 	      vxlan_gbp_rx_trace_t *tr =
@@ -358,6 +383,7 @@
 	      tr->tunnel_index = t0 == 0 ? ~0 : t0 - vxm->tunnels;
 	      tr->vni = vxlan_gbp_get_vni (vxlan_gbp0);
 	      tr->sclass = vxlan_gbp_get_sclass (vxlan_gbp0);
+	      tr->flags = vxlan_gbp_get_gpflags (vxlan_gbp0);
 	    }
 	  if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
 	    {
@@ -368,6 +394,7 @@
 	      tr->tunnel_index = t1 == 0 ? ~0 : t1 - vxm->tunnels;
 	      tr->vni = vxlan_gbp_get_vni (vxlan_gbp1);
 	      tr->sclass = vxlan_gbp_get_sclass (vxlan_gbp1);
+	      tr->flags = vxlan_gbp_get_gpflags (vxlan_gbp0);
 	    }
 
 	  vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
@@ -395,9 +422,6 @@
 	  else
 	    ip6_0 = cur0 - sizeof (udp_header_t) - sizeof (ip6_header_t);
 
-	  /* pop (ip, udp, vxlan_gbp) */
-	  vlib_buffer_advance (b0, sizeof (*vxlan_gbp0));
-
 	  u32 fi0 = buf_fib_index (b0, is_ip4);
 
 	  vxlan_gbp_tunnel_t *t0, *stats_t0 = 0;
@@ -412,15 +436,16 @@
 
 	  uword len0 = vlib_buffer_length_in_chain (vm, b0);
 
-	  u32 next0;
+	  vxlan_gbp_input_next_t next0;
 	  u8 error0 = 0;
 	  u8 flags0 = vxlan_gbp_get_flags (vxlan_gbp0);
+
+	  /* pop (ip, udp, vxlan_gbp) */
+	  vlib_buffer_advance (b0, sizeof (*vxlan_gbp0));
 	  /* Validate VXLAN_GBP tunnel encap-fib index against packet */
 	  if (PREDICT_FALSE
 	      (t0 == 0 || flags0 != (VXLAN_GBP_FLAGS_I | VXLAN_GBP_FLAGS_G)))
 	    {
-	      next0 = VXLAN_GBP_INPUT_NEXT_DROP;
-
 	      if (t0 != 0
 		  && flags0 != (VXLAN_GBP_FLAGS_I | VXLAN_GBP_FLAGS_G))
 		{
@@ -428,24 +453,18 @@
 		  vlib_increment_combined_counter
 		    (drop_counter, thread_index, stats_t0->sw_if_index, 1,
 		     len0);
+		  next0 = VXLAN_GBP_INPUT_NEXT_DROP;
 		}
 	      else
-		error0 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL;
+		{
+		  error0 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL;
+		  next0 = VXLAN_GBP_INPUT_NEXT_NO_TUNNEL;
+		}
 	      b0->error = node->errors[error0];
 	    }
 	  else
 	    {
-	      next0 = t0->decap_next_index;
-	      vnet_buffer2 (b0)->gbp.flags =
-		vxlan_gbp_get_gpflags (vxlan_gbp0);
-	      vnet_buffer2 (b0)->gbp.src_epg =
-		vxlan_gbp_get_sclass (vxlan_gbp0);
-
-
-	      /* Required to make the l2 tag push / pop code work on l2 subifs */
-	      if (PREDICT_TRUE (next0 == VXLAN_GBP_INPUT_NEXT_L2_INPUT))
-		vnet_update_l2_len (b0);
-
+	      next0 = vxlan_gbp_tunnel_get_next (t0, b0);
 	      /* Set packet input sw_if_index to unicast VXLAN_GBP tunnel for learning */
 	      vnet_buffer (b0)->sw_if_index[VLIB_RX] = t0->sw_if_index;
 	      pkts_decapsulated++;
@@ -453,6 +472,11 @@
 	      vlib_increment_combined_counter
 		(rx_counter, thread_index, stats_t0->sw_if_index, 1, len0);
 	    }
+	  vnet_buffer2 (b0)->gbp.flags = vxlan_gbp_get_gpflags (vxlan_gbp0);
+	  vnet_buffer2 (b0)->gbp.src_epg = vxlan_gbp_get_sclass (vxlan_gbp0);
+
+	  /* Required to make the l2 tag push / pop code work on l2 subifs */
+	  vnet_update_l2_len (b0);
 
 	  if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
 	    {
@@ -463,6 +487,7 @@
 	      tr->tunnel_index = t0 == 0 ? ~0 : t0 - vxm->tunnels;
 	      tr->vni = vxlan_gbp_get_vni (vxlan_gbp0);
 	      tr->sclass = vxlan_gbp_get_sclass (vxlan_gbp0);
+	      tr->flags = vxlan_gbp_get_gpflags (vxlan_gbp0);
 	    }
 	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
 					   to_next, n_left_to_next,
diff --git a/src/vnet/vxlan-gbp/vxlan_gbp.api b/src/vnet/vxlan-gbp/vxlan_gbp.api
index 6e41ec8..3e213dd 100644
--- a/src/vnet/vxlan-gbp/vxlan_gbp.api
+++ b/src/vnet/vxlan-gbp/vxlan_gbp.api
@@ -23,7 +23,6 @@
     @param dst - Destination IP address, can be multicast
     @param mcast_sw_if_index - Interface for multicast destination
     @param encap_table_id - Encap route table 
-    @param decap_next_index - Name of decap next graph node
     @param vni - The VXLAN Network Identifier, uint24
     @param sw_ifindex - Ignored in add message, set in details
 */
@@ -34,7 +33,6 @@
   vl_api_address_t dst;
   u32 mcast_sw_if_index;
   u32 encap_table_id;
-  u32 decap_next_index;
   u32 vni;
   u32 sw_if_index;
 };
diff --git a/src/vnet/vxlan-gbp/vxlan_gbp.c b/src/vnet/vxlan-gbp/vxlan_gbp.c
index ec4f923..691cc76 100644
--- a/src/vnet/vxlan-gbp/vxlan_gbp.c
+++ b/src/vnet/vxlan-gbp/vxlan_gbp.c
@@ -32,16 +32,21 @@
 
 vxlan_gbp_main_t vxlan_gbp_main;
 
-static u8 *
-format_decap_next (u8 * s, va_list * args)
+u8 *
+format_vxlan_gbp_tunnel_mode (u8 * s, va_list * args)
 {
-  u32 next_index = va_arg (*args, u32);
+  vxlan_gbp_tunnel_mode_t mode = va_arg (*args, vxlan_gbp_tunnel_mode_t);
 
-  if (next_index == VXLAN_GBP_INPUT_NEXT_DROP)
-    return format (s, "drop");
-  else
-    return format (s, "index %d", next_index);
-  return s;
+  switch (mode)
+    {
+    case VXLAN_GBP_TUNNEL_MODE_L2:
+      s = format (s, "L2");
+      break;
+    case VXLAN_GBP_TUNNEL_MODE_L3:
+      s = format (s, "L3");
+      break;
+    }
+  return (s);
 }
 
 u8 *
@@ -51,17 +56,15 @@
 
   s = format (s,
 	      "[%d] instance %d src %U dst %U vni %d fib-idx %d"
-	      " sw-if-idx %d ",
+	      " sw-if-idx %d mode %U ",
 	      t->dev_instance, t->user_instance,
 	      format_ip46_address, &t->src, IP46_TYPE_ANY,
 	      format_ip46_address, &t->dst, IP46_TYPE_ANY,
-	      t->vni, t->encap_fib_index, t->sw_if_index);
+	      t->vni, t->encap_fib_index, t->sw_if_index,
+	      format_vxlan_gbp_tunnel_mode, t->mode);
 
   s = format (s, "encap-dpo-idx %d ", t->next_dpo.dpoi_index);
 
-  if (PREDICT_FALSE (t->decap_next_index != VXLAN_GBP_INPUT_NEXT_L2_INPUT))
-    s = format (s, "decap-next-%U ", format_decap_next, t->decap_next_index);
-
   if (PREDICT_FALSE (ip46_address_is_multicast (&t->dst)))
     s = format (s, "mcast-sw-if-idx %d ", t->mcast_sw_if_index);
 
@@ -210,9 +213,9 @@
 
 #define foreach_copy_field                      \
 _(vni)                                          \
+_(mode)                                         \
 _(mcast_sw_if_index)                            \
 _(encap_fib_index)                              \
-_(decap_next_index)                             \
 _(src)                                          \
 _(dst)
 
@@ -267,18 +270,6 @@
   vnet_rewrite_set_data (*t, &h, len);
 }
 
-static bool
-vxlan_gbp_decap_next_is_valid (vxlan_gbp_main_t * vxm, u32 is_ip6,
-			       u32 decap_next_index)
-{
-  vlib_main_t *vm = vxm->vlib_main;
-  u32 input_idx = (!is_ip6) ?
-    vxlan4_gbp_input_node.index : vxlan6_gbp_input_node.index;
-  vlib_node_runtime_t *r = vlib_node_get_runtime (vm, input_idx);
-
-  return decap_next_index < r->n_next_nodes;
-}
-
 static uword
 vtep_addr_ref (ip46_address_t * ip)
 {
@@ -434,14 +425,11 @@
 
       /* adding a tunnel: tunnel must not already exist */
       if (p)
-	return VNET_API_ERROR_TUNNEL_EXIST;
-
-      /* if not set explicitly, default to l2 */
-      if (a->decap_next_index == ~0)
-	a->decap_next_index = VXLAN_GBP_INPUT_NEXT_L2_INPUT;
-      if (!vxlan_gbp_decap_next_is_valid (vxm, is_ip6, a->decap_next_index))
-	return VNET_API_ERROR_INVALID_DECAP_NEXT;
-
+	{
+	  t = pool_elt_at_index (vxm->tunnels, *p);
+	  *sw_if_indexp = t->sw_if_index;
+	  return VNET_API_ERROR_TUNNEL_EXIST;
+	}
       pool_get_aligned (vxm->tunnels, t, CLIB_CACHE_LINE_BYTES);
       clib_memset (t, 0, sizeof (*t));
       dev_instance = t - vxm->tunnels;
@@ -505,6 +493,12 @@
 
       t->sw_if_index = sw_if_index = hi->sw_if_index;
 
+      if (VXLAN_GBP_TUNNEL_MODE_L3 == t->mode)
+	{
+	  ip4_sw_interface_enable_disable (t->sw_if_index, 1);
+	  ip6_sw_interface_enable_disable (t->sw_if_index, 1);
+	}
+
       vec_validate_init_empty (vxm->tunnel_index_by_sw_if_index, sw_if_index,
 			       ~0);
       vxm->tunnel_index_by_sw_if_index[sw_if_index] = dev_instance;
@@ -626,6 +620,12 @@
       sw_if_index = t->sw_if_index;
       vnet_sw_interface_set_flags (vnm, sw_if_index, 0 /* down */ );
 
+      if (VXLAN_GBP_TUNNEL_MODE_L3 == t->mode)
+	{
+	  ip4_sw_interface_enable_disable (t->sw_if_index, 0);
+	  ip6_sw_interface_enable_disable (t->sw_if_index, 0);
+	}
+
       vxm->tunnel_index_by_sw_if_index[sw_if_index] = ~0;
 
       if (!is_ip6)
@@ -660,6 +660,36 @@
   return 0;
 }
 
+int
+vnet_vxlan_gbp_tunnel_del (u32 sw_if_index)
+{
+  vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
+  vxlan_gbp_tunnel_t *t = 0;
+  u32 ti;
+
+  if (sw_if_index >= vec_len (vxm->tunnel_index_by_sw_if_index))
+    return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+  ti = vxm->tunnel_index_by_sw_if_index[sw_if_index];
+  if (~0 != ti)
+    {
+      t = pool_elt_at_index (vxm->tunnels, ti);
+
+      vnet_vxlan_gbp_tunnel_add_del_args_t args = {
+	.is_add = 0,
+	.is_ip6 = !ip46_address_is_ip4 (&t->src),
+	.vni = t->vni,
+	.src = t->src,
+	.dst = t->dst,
+	.instance = ~0,
+      };
+
+      return (vnet_vxlan_gbp_tunnel_add_del (&args, NULL));
+    }
+
+  return VNET_API_ERROR_NO_SUCH_ENTRY;
+}
+
 static uword
 get_decap_next_for_node (u32 node_index, u32 ipv4_set)
 {
@@ -700,6 +730,7 @@
   unformat_input_t _line_input, *line_input = &_line_input;
   ip46_address_t src = ip46_address_initializer, dst =
     ip46_address_initializer;
+  vxlan_gbp_tunnel_mode_t mode = VXLAN_GBP_TUNNEL_MODE_L2;
   u8 is_add = 1;
   u8 src_set = 0;
   u8 dst_set = 0;
diff --git a/src/vnet/vxlan-gbp/vxlan_gbp.h b/src/vnet/vxlan-gbp/vxlan_gbp.h
index f9edcdc..66f0cff 100644
--- a/src/vnet/vxlan-gbp/vxlan_gbp.h
+++ b/src/vnet/vxlan-gbp/vxlan_gbp.h
@@ -59,6 +59,14 @@
 */
 typedef clib_bihash_kv_24_8_t vxlan6_gbp_tunnel_key_t;
 
+typedef enum vxlan_gbp_tunnel_mode_t_
+{
+  VXLAN_GBP_TUNNEL_MODE_L2,
+  VXLAN_GBP_TUNNEL_MODE_L3,
+} vxlan_gbp_tunnel_mode_t;
+
+extern u8 *format_vxlan_gbp_tunnel_mode (u8 * s, va_list * args);
+
 typedef struct
 {
   /* Required for pool_get_aligned */
@@ -67,9 +75,6 @@
   /* FIB DPO for IP forwarding of VXLAN encap packet */
   dpo_id_t next_dpo;
 
-  /*  Group Policy ID */
-  u16 sclass;
-
   /* flags */
   u16 flags;
 
@@ -83,9 +88,6 @@
   /* mcast packet output intfc index (used only if dst is mcast) */
   u32 mcast_sw_if_index;
 
-  /* decap next index */
-  u32 decap_next_index;
-
   /* The FIB index for src/dst addresses */
   u32 encap_fib_index;
 
@@ -97,6 +99,12 @@
   uword encap_next_node;
 
   /**
+   * Tunnel mode.
+   * L2 tunnels decap to L2 path, L3 tunnels to the L3 path
+   */
+  vxlan_gbp_tunnel_mode_t mode;
+
+  /**
    * Linkage into the FIB object graph
    */
   fib_node_t node;
@@ -122,9 +130,12 @@
     vnet_declare_rewrite (VLIB_BUFFER_PRE_DATA_SIZE);
 } vxlan_gbp_tunnel_t;
 
-#define foreach_vxlan_gbp_input_next        \
-_(DROP, "error-drop")                   \
-_(L2_INPUT, "l2-input")
+#define foreach_vxlan_gbp_input_next         \
+  _(DROP, "error-drop")                      \
+  _(NO_TUNNEL, "error-punt")                 \
+  _(L2_INPUT, "l2-input")                    \
+  _(IP4_INPUT, "ip4-input")                  \
+  _(IP6_INPUT, "ip6-input")
 
 typedef enum
 {
@@ -142,6 +153,13 @@
   VXLAN_GBP_N_ERROR,
 } vxlan_gbp_input_error_t;
 
+/**
+ * Call back function packets that do not match a configured tunnel
+ */
+typedef vxlan_gbp_input_next_t (*vxlan_bgp_no_tunnel_t) (vlib_buffer_t * b,
+							 u32 thread_index,
+							 u8 is_ip6);
+
 typedef struct
 {
   /* vector of encap tunnel instances */
@@ -189,20 +207,22 @@
   u8 is_add;
   u8 is_ip6;
   u32 instance;
+  vxlan_gbp_tunnel_mode_t mode;
   ip46_address_t src, dst;
   u32 mcast_sw_if_index;
   u32 encap_fib_index;
-  u32 decap_next_index;
   u32 vni;
 } vnet_vxlan_gbp_tunnel_add_del_args_t;
 
 int vnet_vxlan_gbp_tunnel_add_del
   (vnet_vxlan_gbp_tunnel_add_del_args_t * a, u32 * sw_if_indexp);
+int vnet_vxlan_gbp_tunnel_del (u32 sw_if_indexp);
 
 void vnet_int_vxlan_gbp_bypass_mode (u32 sw_if_index, u8 is_ip6,
 				     u8 is_enable);
 
 u32 vnet_vxlan_gbp_get_tunnel_index (u32 sw_if_index);
+
 #endif /* included_vnet_vxlan_gbp_h */
 
 /*
diff --git a/src/vnet/vxlan-gbp/vxlan_gbp_api.c b/src/vnet/vxlan-gbp/vxlan_gbp_api.c
index b7e6935..f5e97e5 100644
--- a/src/vnet/vxlan-gbp/vxlan_gbp_api.c
+++ b/src/vnet/vxlan-gbp/vxlan_gbp_api.c
@@ -92,10 +92,10 @@
     .instance = ntohl (mp->tunnel.instance),
     .mcast_sw_if_index = ntohl (mp->tunnel.mcast_sw_if_index),
     .encap_fib_index = fib_index,
-    .decap_next_index = ntohl (mp->tunnel.decap_next_index),
     .vni = ntohl (mp->tunnel.vni),
     .dst = dst,
     .src = src,
+    .mode = VXLAN_GBP_TUNNEL_MODE_L2,
   };
 
   /* Check src & dst are different */
@@ -142,7 +142,6 @@
   rmp->tunnel.instance = htonl (t->user_instance);
   rmp->tunnel.mcast_sw_if_index = htonl (t->mcast_sw_if_index);
   rmp->tunnel.vni = htonl (t->vni);
-  rmp->tunnel.decap_next_index = htonl (t->decap_next_index);
   rmp->tunnel.sw_if_index = htonl (t->sw_if_index);
   rmp->context = context;
 
diff --git a/src/vnet/vxlan-gbp/vxlan_gbp_packet.c b/src/vnet/vxlan-gbp/vxlan_gbp_packet.c
new file mode 100644
index 0000000..01c7a19
--- /dev/null
+++ b/src/vnet/vxlan-gbp/vxlan_gbp_packet.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2018 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/vxlan-gbp/vxlan_gbp_packet.h>
+
+u8 *
+format_vxlan_gbp_header_flags (u8 * s, va_list * args)
+{
+  vxlan_gbp_flags_t flags = va_arg (*args, int);
+
+  if (VXLAN_GBP_FLAGS_NONE == flags)
+    {
+      s = format (s, "None");
+    }
+#define _(n,f) {                          \
+    if (VXLAN_GBP_FLAGS_##f & flags)      \
+      s = format (s, #f);                 \
+  }
+  foreach_vxlan_gbp_flags
+#undef _
+    return (s);
+}
+
+u8 *
+format_vxlan_gbp_header_gpflags (u8 * s, va_list * args)
+{
+  vxlan_gbp_gpflags_t flags = va_arg (*args, int);
+
+  if (VXLAN_GBP_GPFLAGS_NONE == flags)
+    {
+      s = format (s, "None");
+    }
+#define _(n,f) {                          \
+    if (VXLAN_GBP_GPFLAGS_##f & flags)    \
+      s = format (s, #f);                 \
+  }
+  foreach_vxlan_gbp_gpflags
+#undef _
+    return (s);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/vxlan-gbp/vxlan_gbp_packet.h b/src/vnet/vxlan-gbp/vxlan_gbp_packet.h
index e1674a0..33bccd6 100644
--- a/src/vnet/vxlan-gbp/vxlan_gbp_packet.h
+++ b/src/vnet/vxlan-gbp/vxlan_gbp_packet.h
@@ -15,6 +15,8 @@
 #ifndef __included_vxlan_gbp_packet_h__
 #define __included_vxlan_gbp_packet_h__ 1
 
+#include <vlib/vlib.h>
+
 /*
  * From draft-smith-vxlan-group-policy-04.txt
  *
@@ -85,8 +87,17 @@
   u32 vni_reserved;
 } vxlan_gbp_header_t;
 
-#define VXLAN_GBP_FLAGS_G 0x80
-#define VXLAN_GBP_FLAGS_I 0x08
+#define foreach_vxlan_gbp_flags    \
+  _ (0x80, G)                      \
+  _ (0x08, I)
+
+typedef enum
+{
+  VXLAN_GBP_FLAGS_NONE = 0,
+#define _(n,f) VXLAN_GBP_FLAGS_##f = n,
+  foreach_vxlan_gbp_flags
+#undef _
+} __attribute__ ((packed)) vxlan_gbp_flags_t;
 
 #define foreach_vxlan_gbp_gpflags \
 _ (0x40, D)                       \
@@ -96,10 +107,11 @@
 
 typedef enum
 {
+  VXLAN_GBP_GPFLAGS_NONE = 0,
 #define _(n,f) VXLAN_GBP_GPFLAGS_##f = n,
   foreach_vxlan_gbp_gpflags
 #undef _
-} vxlan_gbp_gpflag_t;
+} __attribute__ ((packed)) vxlan_gbp_gpflags_t;
 
 static inline u32
 vxlan_gbp_get_vni (vxlan_gbp_header_t * h)
@@ -119,13 +131,13 @@
   return sclass_host_byte_order;
 }
 
-static inline u8
+static inline vxlan_gbp_gpflags_t
 vxlan_gbp_get_gpflags (vxlan_gbp_header_t * h)
 {
   return h->gpflags;
 }
 
-static inline u8
+static inline vxlan_gbp_flags_t
 vxlan_gbp_get_flags (vxlan_gbp_header_t * h)
 {
   return h->flag_g_i;
@@ -139,6 +151,9 @@
   h->flag_g_i = VXLAN_GBP_FLAGS_I | VXLAN_GBP_FLAGS_G;
 }
 
+extern u8 *format_vxlan_gbp_header_flags (u8 * s, va_list * args);
+extern u8 *format_vxlan_gbp_header_gpflags (u8 * s, va_list * args);
+
 #endif /* __included_vxlan_gbp_packet_h__ */
 
 /*