VPP-470: Introduce VxLAN-GPE as transport for iOAM.
This is the second of the two commits. This commit introduces VxLAN-GPE
transit functionality.

Change-Id: I12f13e00f456df0047f2b7a0737addfeb683c420
Signed-off-by: Vengada Govindan <venggovi@cisco.com>
diff --git a/plugins/ioam-plugin/Makefile.am b/plugins/ioam-plugin/Makefile.am
index 130f87e..9a87767 100644
--- a/plugins/ioam-plugin/Makefile.am
+++ b/plugins/ioam-plugin/Makefile.am
@@ -126,6 +126,7 @@
 libioam_vxlan_gpe_plugin_la_SOURCES =			\
 	ioam/lib-vxlan-gpe/ioam_encap.c			\
 	ioam/lib-vxlan-gpe/ioam_decap.c			\
+	ioam/lib-vxlan-gpe/ioam_transit.c		\
 	ioam/lib-vxlan-gpe/ioam_pop.c			\
 	ioam/lib-vxlan-gpe/vxlan_gpe_api.c		\
 	ioam/lib-vxlan-gpe/vxlan_gpe_ioam_trace.c	\
diff --git a/plugins/ioam-plugin/ioam/lib-vxlan-gpe/ioam_decap.c b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/ioam_decap.c
index f938a33..fd30865 100644
--- a/plugins/ioam-plugin/ioam/lib-vxlan-gpe/ioam_decap.c
+++ b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/ioam_decap.c
@@ -52,10 +52,10 @@
   vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
 
   next0[0] = next1[0] = hm->decap_v4_next_override;
-  vxlan_gpe_encap_decap_ioam_v4_one_inline (vm, node, ngm, b0, &next0[0],
+  vxlan_gpe_encap_decap_ioam_v4_one_inline (vm, node, b0, &next0[0],
 					    VXLAN_GPE_DECAP_IOAM_V4_NEXT_DROP,
 					    0 /* use_adj */ );
-  vxlan_gpe_encap_decap_ioam_v4_one_inline (vm, node, ngm, b1, &next0[1],
+  vxlan_gpe_encap_decap_ioam_v4_one_inline (vm, node, b1, &next0[1],
 					    VXLAN_GPE_DECAP_IOAM_V4_NEXT_DROP,
 					    0 /* use_adj */ );
 }
@@ -163,7 +163,7 @@
 					sizeof (vxlan_gpe_header_t)));
 
 	  next0 = hm->decap_v4_next_override;
-	  vxlan_gpe_encap_decap_ioam_v4_one_inline (vm, node, ngm, b0,
+	  vxlan_gpe_encap_decap_ioam_v4_one_inline (vm, node, b0,
 						    &next0,
 						    VXLAN_GPE_DECAP_IOAM_V4_NEXT_DROP,
 						    0 /* use_adj */ );
diff --git a/plugins/ioam-plugin/ioam/lib-vxlan-gpe/ioam_encap.c b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/ioam_encap.c
index 7add6d0..4b18bfe 100644
--- a/plugins/ioam-plugin/ioam/lib-vxlan-gpe/ioam_encap.c
+++ b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/ioam_encap.c
@@ -56,10 +56,10 @@
 				    u32 * next0, u32 * next1)
 {
   next0[0] = next1[0] = VXLAN_GPE_ENCAP_IOAM_V4_NEXT_IP4_LOOKUP;
-  vxlan_gpe_encap_decap_ioam_v4_one_inline (vm, node, ngm, b0, next0,
+  vxlan_gpe_encap_decap_ioam_v4_one_inline (vm, node, b0, next0,
 					    VXLAN_GPE_ENCAP_IOAM_V4_NEXT_DROP,
 					    0 /* use_adj */ );
-  vxlan_gpe_encap_decap_ioam_v4_one_inline (vm, node, ngm, b1, next1,
+  vxlan_gpe_encap_decap_ioam_v4_one_inline (vm, node, b1, next1,
 					    VXLAN_GPE_ENCAP_IOAM_V4_NEXT_DROP,
 					    0 /* use_adj */ );
 }
@@ -142,7 +142,7 @@
 
 	  b0 = vlib_get_buffer (vm, bi0);
 
-	  vxlan_gpe_encap_decap_ioam_v4_one_inline (vm, node, ngm, b0,
+	  vxlan_gpe_encap_decap_ioam_v4_one_inline (vm, node, b0,
 						    &next0,
 						    VXLAN_GPE_ENCAP_IOAM_V4_NEXT_DROP,
 						    0 /* use_adj */ );
diff --git a/plugins/ioam-plugin/ioam/lib-vxlan-gpe/ioam_transit.c b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/ioam_transit.c
new file mode 100644
index 0000000..b42c357
--- /dev/null
+++ b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/ioam_transit.c
@@ -0,0 +1,188 @@
+ /*
+  * 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 <vppinfra/error.h>
+#include <vppinfra/hash.h>
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/ip/udp.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/vxlan-gpe/vxlan_gpe.h>
+#include <ioam/lib-vxlan-gpe/vxlan_gpe_ioam_packet.h>
+#include <ioam/lib-vxlan-gpe/vxlan_gpe_ioam.h>
+#include <ioam/lib-vxlan-gpe/vxlan_gpe_ioam_util.h>
+#include <vnet/fib/ip6_fib.h>
+#include <vnet/fib/ip4_fib.h>
+#include <vnet/fib/fib_entry.h>
+
+/* Statistics (not really errors) */
+#define foreach_vxlan_gpe_transit_ioam_error    \
+_(ENCAPSULATED, "good packets encapsulated")
+
+static char *vxlan_gpe_transit_ioam_error_strings[] = {
+#define _(sym,string) string,
+  foreach_vxlan_gpe_transit_ioam_error
+#undef _
+};
+
+typedef enum
+{
+#define _(sym,str) VXLAN_GPE_TRANSIT_IOAM_ERROR_##sym,
+  foreach_vxlan_gpe_transit_ioam_error
+#undef _
+    VXLAN_GPE_TRANSIT_IOAM_N_ERROR,
+} vxlan_gpe_transit_ioam_error_t;
+
+typedef enum
+{
+  VXLAN_GPE_TRANSIT_IOAM_NEXT_OUTPUT,
+  VXLAN_GPE_TRANSIT_IOAM_NEXT_DROP,
+  VXLAN_GPE_TRANSIT_IOAM_N_NEXT
+} vxlan_gpe_transit_ioam_next_t;
+
+
+/* *INDENT-OFF* */
+VNET_FEATURE_INIT (vxlan_gpe_transit_ioam, static) =
+{
+  .arc_name = "ip4-output",
+  .node_name = "vxlan-gpe-transit-ioam",
+  .runs_before = VNET_FEATURES ("interface-output"),
+};
+/* *INDENT-ON* */
+
+static uword
+vxlan_gpe_transit_ioam (vlib_main_t * vm,
+			vlib_node_runtime_t * node, vlib_frame_t * from_frame)
+{
+  u32 n_left_from, next_index, *from, *to_next;
+
+  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 > 0 && n_left_to_next > 0)
+	{
+	  u32 bi0;
+	  vlib_buffer_t *b0;
+	  u32 next0 = VXLAN_GPE_TRANSIT_IOAM_NEXT_OUTPUT;
+
+	  bi0 = from[0];
+	  to_next[0] = bi0;
+	  from += 1;
+	  to_next += 1;
+	  n_left_from -= 1;
+	  n_left_to_next -= 1;
+	  ip4_header_t *ip0;
+	  u32 iph_offset = 0;
+
+	  b0 = vlib_get_buffer (vm, bi0);
+	  iph_offset = vnet_buffer (b0)->ip.save_rewrite_length;
+	  ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0)
+				  + iph_offset);
+
+	  /* just forward non ipv4 packets */
+	  if (PREDICT_FALSE
+	      ((ip0->ip_version_and_header_length & 0xF0) == 0x40))
+	    {
+	      /* ipv4 packets */
+	      udp_header_t *udp_hdr0 = (udp_header_t *) (ip0 + 1);
+	      if (PREDICT_FALSE
+		  ((ip0->protocol == IP_PROTOCOL_UDP) &&
+		   (clib_net_to_host_u16 (udp_hdr0->dst_port) ==
+		    UDP_DST_PORT_vxlan_gpe)))
+		{
+
+		  /* Check the iOAM header */
+		  vxlan_gpe_header_t *gpe_hdr0 =
+		    (vxlan_gpe_header_t *) (udp_hdr0 + 1);
+
+		  if (PREDICT_FALSE
+		      (gpe_hdr0->protocol == VXLAN_GPE_PROTOCOL_IOAM))
+		    {
+		      uword *t = NULL;
+		      vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
+		      fib_prefix_t key4;
+		      memset (&key4, 0, sizeof (key4));
+		      key4.fp_proto = FIB_PROTOCOL_IP4;
+		      key4.fp_addr.ip4.as_u32 = ip0->dst_address.as_u32;
+		      t = hash_get_mem (hm->dst_by_ip4, &key4);
+		      if (t)
+			{
+
+
+			  vlib_buffer_advance (b0,
+					       (word) (sizeof
+						       (ethernet_header_t)));
+			  vxlan_gpe_encap_decap_ioam_v4_one_inline (vm, node,
+								    b0,
+								    &next0,
+								    VXLAN_GPE_TRANSIT_IOAM_NEXT_DROP,
+								    1
+								    /* use_adj */
+			    );
+			  vlib_buffer_advance (b0,
+					       -(word) (sizeof
+							(ethernet_header_t)));
+			}
+		    }
+		}
+	    }
+
+	  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);
+    }
+
+  return from_frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (vxlan_gpe_transit_ioam_node) = {
+  .function = vxlan_gpe_transit_ioam,
+  .name = "vxlan-gpe-transit-ioam",
+  .vector_size = sizeof (u32),
+  .format_trace = format_vxlan_gpe_ioam_v4_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = ARRAY_LEN(vxlan_gpe_transit_ioam_error_strings),
+  .error_strings = vxlan_gpe_transit_ioam_error_strings,
+
+  .n_next_nodes = VXLAN_GPE_TRANSIT_IOAM_N_NEXT,
+
+  .next_nodes = {
+        [VXLAN_GPE_TRANSIT_IOAM_NEXT_OUTPUT] = "interface-output",
+        [VXLAN_GPE_TRANSIT_IOAM_NEXT_DROP] = "error-drop",
+  },
+
+};
+/* *INDENT-ON* */
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe.api b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe.api
index 2cf7a59..056529a 100644
--- a/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe.api
+++ b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe.api
@@ -124,3 +124,58 @@
 };
 
 
+/** \brief Enable iOAM for a VXLAN-GPE transit
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param dst_addr - IPv4/6 Address of the local VTEP
+    @param outer_fib_index- FIB index
+
+*/
+define vxlan_gpe_ioam_transit_enable {
+  u32 client_index;
+  u32 context;
+  u32 outer_fib_index;
+  u8  dst_addr[16];
+  u8  is_ipv6;
+};
+
+/** \brief Reply to enable iOAM for VXLAN-GPE transit
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param retval - return value for request
+
+*/
+define vxlan_gpe_ioam_transit_enable_reply {
+  u32 client_index;
+  u32 context;
+  i32 retval;
+};
+
+/** \brief Disable iOAM for VXLAN-GPE transit
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param dst_addr - IPv4/6 Address of the local VTEP
+    @param outer_fib_index- FIB index
+
+*/
+define vxlan_gpe_ioam_transit_disable {
+  u32 client_index;
+  u32 context;
+  u32 outer_fib_index;
+  u8  dst_addr[16];
+  u8  is_ipv6;
+};
+
+/** \brief Reply to disable iOAM for VXLAN-GPE transit
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param retval - return value for request
+
+*/
+define vxlan_gpe_ioam_transit_disable_reply {
+  u32 client_index;
+  u32 context;
+  i32 retval;
+};
+
+
diff --git a/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_api.c b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_api.c
index 3e14385..6875236 100644
--- a/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_api.c
+++ b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_api.c
@@ -94,11 +94,13 @@
 
 /* List of message types that this plugin understands */
 
-#define foreach_vxlan_gpe_plugin_api_msg                        \
-_(VXLAN_GPE_IOAM_ENABLE, vxlan_gpe_ioam_enable)                 \
-_(VXLAN_GPE_IOAM_DISABLE, vxlan_gpe_ioam_disable)               \
-_(VXLAN_GPE_IOAM_VNI_ENABLE, vxlan_gpe_ioam_vni_enable)         \
-_(VXLAN_GPE_IOAM_VNI_DISABLE, vxlan_gpe_ioam_vni_disable)       \
+#define foreach_vxlan_gpe_plugin_api_msg                               \
+_(VXLAN_GPE_IOAM_ENABLE, vxlan_gpe_ioam_enable)                        \
+_(VXLAN_GPE_IOAM_DISABLE, vxlan_gpe_ioam_disable)                      \
+_(VXLAN_GPE_IOAM_VNI_ENABLE, vxlan_gpe_ioam_vni_enable)                \
+_(VXLAN_GPE_IOAM_VNI_DISABLE, vxlan_gpe_ioam_vni_disable)              \
+_(VXLAN_GPE_IOAM_TRANSIT_ENABLE, vxlan_gpe_ioam_transit_enable)        \
+_(VXLAN_GPE_IOAM_TRANSIT_DISABLE, vxlan_gpe_ioam_transit_disable)      \
 
 
 static void vl_api_vxlan_gpe_ioam_enable_t_handler
@@ -239,6 +241,50 @@
   VXLAN_GPE_REPLY_MACRO (VL_API_VXLAN_GPE_IOAM_VNI_DISABLE_REPLY);
 }
 
+static void vl_api_vxlan_gpe_ioam_transit_enable_t_handler
+  (vl_api_vxlan_gpe_ioam_transit_enable_t * mp)
+{
+  int rv = 0;
+  vl_api_vxlan_gpe_ioam_transit_enable_reply_t *rmp;
+  vxlan_gpe_ioam_main_t *sm = &vxlan_gpe_ioam_main;
+  ip46_address_t dst_addr;
+
+  memset (&dst_addr.ip4, 0, sizeof (dst_addr.ip4));
+  if (!mp->is_ipv6)
+    {
+      clib_memcpy (&dst_addr.ip4, &mp->dst_addr, sizeof (dst_addr.ip4));
+    }
+  rv = vxlan_gpe_enable_disable_ioam_for_dest (sm->vlib_main,
+					       dst_addr,
+					       ntohl (mp->outer_fib_index),
+					       mp->is_ipv6 ? 0 : 1,
+					       1 /* is_add */ );
+
+  VXLAN_GPE_REPLY_MACRO (VL_API_VXLAN_GPE_IOAM_TRANSIT_ENABLE_REPLY);
+}
+
+static void vl_api_vxlan_gpe_ioam_transit_disable_t_handler
+  (vl_api_vxlan_gpe_ioam_transit_disable_t * mp)
+{
+  int rv = 0;
+  vl_api_vxlan_gpe_ioam_transit_disable_reply_t *rmp;
+  vxlan_gpe_ioam_main_t *sm = &vxlan_gpe_ioam_main;
+  ip46_address_t dst_addr;
+
+  memset (&dst_addr.ip4, 0, sizeof (dst_addr.ip4));
+  if (!mp->is_ipv6)
+    {
+      clib_memcpy (&dst_addr.ip4, &mp->dst_addr, sizeof (dst_addr.ip4));
+    }
+
+  rv = vxlan_gpe_ioam_disable_for_dest (sm->vlib_main,
+					dst_addr,
+					ntohl (mp->outer_fib_index),
+					mp->is_ipv6 ? 0 : 1);
+  VXLAN_GPE_REPLY_MACRO (VL_API_VXLAN_GPE_IOAM_TRANSIT_DISABLE_REPLY);
+}
+
+
 /*
  * This routine exists to convince the vlib plugin framework that
  * we haven't accidentally copied a random .dll into the plugin directory.
@@ -255,6 +301,8 @@
 
   sm->vlib_main = vm;
   sm->vnet_main = h->vnet_main;
+  sm->unix_time_0 = (u32) time (0);	/* Store starting time */
+  sm->vlib_time_0 = vlib_time_now (vm);
   return error;
 }
 
@@ -308,6 +356,12 @@
     vlib_node_add_next (vm, vxlan_gpe_decap_node->index, decap_node_index);
   vxlan_gpe_register_decap_protocol (VXLAN_GPE_PROTOCOL_IOAM, next_node);
 
+  vec_new (vxlan_gpe_ioam_sw_interface_t, pool_elts (sm->sw_interfaces));
+  sm->dst_by_ip4 = hash_create_mem (0, sizeof (fib_prefix_t), sizeof (uword));
+
+  sm->dst_by_ip6 = hash_create_mem (0, sizeof (fib_prefix_t), sizeof (uword));
+
+  vxlan_gpe_ioam_interface_init ();
   vec_free (name);
 
   return error;
diff --git a/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_ioam.c b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_ioam.c
index 066f582..6c04d9a 100644
--- a/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_ioam.c
+++ b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_ioam.c
@@ -16,6 +16,9 @@
 #include <vnet/vxlan-gpe/vxlan_gpe_packet.h>
 #include <vnet/ip/format.h>
 #include <ioam/lib-vxlan-gpe/vxlan_gpe_ioam.h>
+#include <vnet/fib/ip6_fib.h>
+#include <vnet/fib/ip4_fib.h>
+#include <vnet/fib/fib_entry.h>
 
 vxlan_gpe_ioam_main_t vxlan_gpe_ioam_main;
 
@@ -163,11 +166,257 @@
 
 }
 
+static void
+vxlan_gpe_set_clear_output_feature_on_intf (vlib_main_t * vm,
+					    u32 sw_if_index0, u8 is_add)
+{
+
+
+
+  vnet_feature_enable_disable ("ip4-output", "vxlan-gpe-transit-ioam",
+			       sw_if_index0, is_add,
+			       0 /* void *feature_config */ ,
+			       0 /* u32 n_feature_config_bytes */ );
+  return;
+}
+
+void
+vxlan_gpe_clear_output_feature_on_all_intfs (vlib_main_t * vm)
+{
+  vnet_sw_interface_t *si = 0;
+  vnet_main_t *vnm = vnet_get_main ();
+  vnet_interface_main_t *im = &vnm->interface_main;
+
+  pool_foreach (si, im->sw_interfaces, (
+					 {
+					 vxlan_gpe_set_clear_output_feature_on_intf
+					 (vm, si->sw_if_index, 0);
+					 }));
+  return;
+}
+
+
+extern fib_forward_chain_type_t
+fib_entry_get_default_chain_type (const fib_entry_t * fib_entry);
+
+int
+vxlan_gpe_enable_disable_ioam_for_dest (vlib_main_t * vm,
+					ip46_address_t dst_addr,
+					u32 outer_fib_index,
+					u8 is_ipv4, u8 is_add)
+{
+  vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
+  u32 fib_index0 = 0;
+  u32 sw_if_index0 = ~0;
+
+  fib_node_index_t fei = ~0;
+  fib_entry_t *fib_entry;
+  u32 adj_index0;
+  ip_adjacency_t *adj0;
+  fib_prefix_t fib_prefix;
+  //fib_forward_chain_type_t fct;
+  load_balance_t *lb_m, *lb_b;
+  const dpo_id_t *dpo0, *dpo1;
+  u32 i, j;
+  //vnet_hw_interface_t *hw;
+
+  if (is_ipv4)
+    {
+      memset (&fib_prefix, 0, sizeof (fib_prefix_t));
+      fib_prefix.fp_len = 32;
+      fib_prefix.fp_proto = FIB_PROTOCOL_IP4;
+      fib_prefix.fp_addr = dst_addr;
+    }
+  else
+    {
+      return 0;
+    }
+
+  fei = fib_table_lookup (fib_index0, &fib_prefix);
+  fib_entry = fib_entry_get (fei);
+
+  //fct = fib_entry_get_default_chain_type (fib_entry);
+
+  if (!dpo_id_is_valid (&fib_entry->fe_lb /*[fct] */ ))
+    {
+      return (-1);
+    }
+
+  lb_m = load_balance_get (fib_entry->fe_lb /*[fct] */ .dpoi_index);
+
+  for (i = 0; i < lb_m->lb_n_buckets; i++)
+    {
+      dpo0 = load_balance_get_bucket_i (lb_m, i);
+
+      if (dpo0->dpoi_type == DPO_LOAD_BALANCE)
+	{
+	  lb_b = load_balance_get (dpo0->dpoi_index);
+
+	  for (j = 0; j < lb_b->lb_n_buckets; j++)
+	    {
+	      dpo1 = load_balance_get_bucket_i (lb_b, j);
+
+	      if (dpo1->dpoi_type == DPO_ADJACENCY)
+		{
+		  adj_index0 = dpo1->dpoi_index;
+
+		  if (ADJ_INDEX_INVALID == adj_index0)
+		    {
+		      continue;
+		    }
+
+		  adj0 =
+		    ip_get_adjacency (&(ip4_main.lookup_main), adj_index0);
+		  sw_if_index0 = adj0->rewrite_header.sw_if_index;
+
+		  if (~0 == sw_if_index0)
+		    {
+		      continue;
+		    }
+
+
+		  if (is_add)
+		    {
+		      vnet_feature_enable_disable ("ip4-output",
+						   "vxlan-gpe-transit-ioam",
+						   sw_if_index0, is_add, 0
+						   /* void *feature_config */
+						   , 0	/* u32 n_feature_config_bytes */
+			);
+
+		      vec_validate_init_empty (hm->bool_ref_by_sw_if_index,
+					       sw_if_index0, ~0);
+		      hm->bool_ref_by_sw_if_index[sw_if_index0] = 1;
+		    }
+		  else
+		    {
+		      hm->bool_ref_by_sw_if_index[sw_if_index0] = ~0;
+		    }
+		}
+	    }
+	}
+    }
+
+  if (is_ipv4)
+    {
+
+      uword *t = NULL;
+      vxlan_gpe_ioam_dest_tunnels_t *t1;
+      fib_prefix_t key4, *key4_copy;
+      hash_pair_t *hp;
+      memset (&key4, 0, sizeof (key4));
+      key4.fp_proto = FIB_PROTOCOL_IP4;
+      key4.fp_addr.ip4.as_u32 = fib_prefix.fp_addr.ip4.as_u32;
+      t = hash_get_mem (hm->dst_by_ip4, &key4);
+      if (is_add)
+	{
+	  if (t)
+	    {
+	      return 0;
+	    }
+	  pool_get_aligned (hm->dst_tunnels, t1, CLIB_CACHE_LINE_BYTES);
+	  memset (t1, 0, sizeof (*t1));
+	  t1->fp_proto = FIB_PROTOCOL_IP4;
+	  t1->dst_addr.ip4.as_u32 = fib_prefix.fp_addr.ip4.as_u32;
+	  key4_copy = clib_mem_alloc (sizeof (*key4_copy));
+	  clib_memcpy (key4_copy, &key4, sizeof (*key4_copy));
+	  hash_set_mem (hm->dst_by_ip4, key4_copy, t1 - hm->dst_tunnels);
+	  /*
+	   * Attach to the FIB entry for the VxLAN-GPE destination
+	   * and become its child. The dest route will invoke a callback
+	   * when the fib entry changes, it can be used to
+	   * re-program the output feature on the egress interface.
+	   */
+
+	  const fib_prefix_t tun_dst_pfx = {
+	    .fp_len = 32,
+	    .fp_proto = FIB_PROTOCOL_IP4,
+	    .fp_addr = {.ip4 = t1->dst_addr.ip4,}
+	  };
+
+	  t1->fib_entry_index =
+	    fib_table_entry_special_add (outer_fib_index,
+					 &tun_dst_pfx,
+					 FIB_SOURCE_RR,
+					 FIB_ENTRY_FLAG_NONE,
+					 ADJ_INDEX_INVALID);
+	  t1->sibling_index =
+	    fib_entry_child_add (t1->fib_entry_index,
+				 hm->fib_entry_type, t1 - hm->dst_tunnels);
+	  t1->outer_fib_index = outer_fib_index;
+
+	}
+      else
+	{
+	  if (!t)
+	    {
+	      return 0;
+	    }
+	  t1 = pool_elt_at_index (hm->dst_tunnels, t[0]);
+	  hp = hash_get_pair (hm->dst_by_ip4, &key4);
+	  key4_copy = (void *) (hp->key);
+	  hash_unset_mem (hm->dst_by_ip4, &key4);
+	  clib_mem_free (key4_copy);
+	  pool_put (hm->dst_tunnels, t1);
+	}
+    }
+  else
+    {
+      // TBD for IPv6
+    }
+
+  return 0;
+}
+
+void
+vxlan_gpe_refresh_output_feature_on_all_dest (void)
+{
+  vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
+  vxlan_gpe_ioam_dest_tunnels_t *t;
+  u32 i;
+  if (pool_elts (hm->dst_tunnels) == 0)
+    return;
+  vxlan_gpe_clear_output_feature_on_all_intfs (hm->vlib_main);
+  i = vec_len (hm->bool_ref_by_sw_if_index);
+  vec_free (hm->bool_ref_by_sw_if_index);
+  vec_validate_init_empty (hm->bool_ref_by_sw_if_index, i, ~0);
+  pool_foreach (t, hm->dst_tunnels, (
+				      {
+				      vxlan_gpe_enable_disable_ioam_for_dest
+				      (hm->vlib_main,
+				       t->dst_addr,
+				       t->outer_fib_index,
+				       (t->fp_proto == FIB_PROTOCOL_IP4), 1
+				       /* is_add */
+				      );
+				      }
+		));
+  return;
+}
+
+void
+vxlan_gpe_clear_output_feature_on_select_intfs (void)
+{
+  vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
+  u32 sw_if_index0 = 0;
+  for (sw_if_index0 = 0;
+       sw_if_index0 < vec_len (hm->bool_ref_by_sw_if_index); sw_if_index0++)
+    {
+      if (hm->bool_ref_by_sw_if_index[sw_if_index0] == 0xFF)
+	{
+	  vxlan_gpe_set_clear_output_feature_on_intf
+	    (hm->vlib_main, sw_if_index0, 0);
+	}
+    }
+
+  return;
+}
 
 static clib_error_t *
-vxlan_gpe_set_ioam_rewrite_command_fn (vlib_main_t * vm,
-				       unformat_input_t * input,
-				       vlib_cli_command_t * cmd)
+vxlan_gpe_set_ioam_rewrite_command_fn (vlib_main_t *
+				       vm,
+				       unformat_input_t
+				       * input, vlib_cli_command_t * cmd)
 {
   vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
   ip46_address_t local, remote;
@@ -184,7 +433,6 @@
   uword *p;
   vxlan_gpe_main_t *gm = &vxlan_gpe_main;
   vxlan_gpe_tunnel_t *t = 0;
-
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     {
       if (unformat (input, "local %U", unformat_ip4_address, &local.ip4))
@@ -192,8 +440,8 @@
 	  local_set = 1;
 	  ipv4_set = 1;
 	}
-      else if (unformat (input, "remote %U",
-			 unformat_ip4_address, &remote.ip4))
+      else
+	if (unformat (input, "remote %U", unformat_ip4_address, &remote.ip4))
 	{
 	  remote_set = 1;
 	  ipv4_set = 1;
@@ -203,8 +451,8 @@
 	  local_set = 1;
 	  ipv6_set = 1;
 	}
-      else if (unformat (input, "remote %U",
-			 unformat_ip6_address, &remote.ip6))
+      else
+	if (unformat (input, "remote %U", unformat_ip6_address, &remote.ip6))
 	{
 	  remote_set = 1;
 	  ipv6_set = 1;
@@ -219,29 +467,27 @@
 
   if (local_set == 0)
     return clib_error_return (0, "tunnel local address not specified");
-
   if (remote_set == 0)
     return clib_error_return (0, "tunnel remote address not specified");
-
   if (ipv4_set && ipv6_set)
     return clib_error_return (0, "both IPv4 and IPv6 addresses specified");
-
-  if ((ipv4_set && memcmp (&local.ip4, &remote.ip4, sizeof (local.ip4)) == 0)
-      || (ipv6_set
-	  && memcmp (&local.ip6, &remote.ip6, sizeof (local.ip6)) == 0))
+  if ((ipv4_set
+       && memcmp (&local.ip4, &remote.ip4,
+		  sizeof (local.ip4)) == 0) || (ipv6_set
+						&&
+						memcmp
+						(&local.ip6,
+						 &remote.ip6,
+						 sizeof (local.ip6)) == 0))
     return clib_error_return (0, "src and dst addresses are identical");
-
   if (vni_set == 0)
     return clib_error_return (0, "vni not specified");
-
-
   if (!ipv6_set)
     {
       key4.local = local.ip4.as_u32;
       key4.remote = remote.ip4.as_u32;
       key4.vni = clib_host_to_net_u32 (vni << 8);
       key4.pad = 0;
-
       p = hash_get_mem (gm->vxlan4_gpe_tunnel_by_key, &key4);
     }
   else
@@ -251,20 +497,17 @@
       key6.remote.as_u64[0] = remote.ip6.as_u64[0];
       key6.remote.as_u64[1] = remote.ip6.as_u64[1];
       key6.vni = clib_host_to_net_u32 (vni << 8);
-
       p = hash_get_mem (gm->vxlan6_gpe_tunnel_by_key, &key6);
     }
 
   if (!p)
     return clib_error_return (0, "VxLAN Tunnel not found");
-
   t = pool_elt_at_index (gm->tunnels, p[0]);
-
   if (!disable)
     {
-      rv = vxlan_gpe_ioam_set (t, hm->has_trace_option,
-			       hm->has_pot_option,
-			       hm->has_ppc_option, ipv6_set);
+      rv =
+	vxlan_gpe_ioam_set (t, hm->has_trace_option,
+			    hm->has_pot_option, hm->has_ppc_option, ipv6_set);
     }
   else
     {
@@ -285,11 +528,10 @@
 
 
 clib_error_t *
-vxlan_gpe_ioam_enable (int has_trace_option, int has_pot_option,
-		       int has_ppc_option)
+vxlan_gpe_ioam_enable (int has_trace_option,
+		       int has_pot_option, int has_ppc_option)
 {
   vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
-
   hm->has_trace_option = has_trace_option;
   hm->has_pot_option = has_pot_option;
   hm->has_ppc_option = has_ppc_option;
@@ -302,11 +544,11 @@
 }
 
 clib_error_t *
-vxlan_gpe_ioam_disable (int has_trace_option, int has_pot_option,
-			int has_ppc_option)
+vxlan_gpe_ioam_disable (int
+			has_trace_option,
+			int has_pot_option, int has_ppc_option)
 {
   vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
-
   hm->has_trace_option = has_trace_option;
   hm->has_pot_option = has_pot_option;
   hm->has_ppc_option = has_ppc_option;
@@ -328,14 +570,13 @@
 
 static clib_error_t *
 vxlan_gpe_set_ioam_flags_command_fn (vlib_main_t * vm,
-				     unformat_input_t * input,
-				     vlib_cli_command_t * cmd)
+				     unformat_input_t
+				     * input, vlib_cli_command_t * cmd)
 {
   int has_trace_option = 0;
   int has_pot_option = 0;
   int has_ppc_option = 0;
   clib_error_t *rv = 0;
-
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     {
       if (unformat (input, "trace"))
@@ -355,7 +596,6 @@
 
   rv =
     vxlan_gpe_ioam_enable (has_trace_option, has_pot_option, has_ppc_option);
-
   return rv;
 }
 
@@ -368,12 +608,100 @@
 /* *INDENT-ON* */
 
 
+int vxlan_gpe_ioam_disable_for_dest
+  (vlib_main_t * vm, ip46_address_t dst_addr, u32 outer_fib_index,
+   u8 ipv4_set)
+{
+  vxlan_gpe_ioam_dest_tunnels_t *t;
+  vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
 
+  vxlan_gpe_enable_disable_ioam_for_dest (hm->vlib_main,
+					  dst_addr, outer_fib_index, ipv4_set,
+					  0);
+  if (pool_elts (hm->dst_tunnels) == 0)
+    {
+      vxlan_gpe_clear_output_feature_on_select_intfs ();
+      return 0;
+    }
 
-clib_error_t *
-clear_vxlan_gpe_ioam_rewrite_command_fn (vlib_main_t * vm,
-					 unformat_input_t * input,
-					 vlib_cli_command_t * cmd)
+  pool_foreach (t, hm->dst_tunnels, (
+				      {
+				      vxlan_gpe_enable_disable_ioam_for_dest
+				      (hm->vlib_main,
+				       t->dst_addr,
+				       t->outer_fib_index,
+				       (t->fp_proto ==
+					FIB_PROTOCOL_IP4), 1 /* is_add */ );
+				      }
+		));
+  vxlan_gpe_clear_output_feature_on_select_intfs ();
+  return (0);
+
+}
+
+static clib_error_t *vxlan_gpe_set_ioam_transit_rewrite_command_fn
+  (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
+  ip46_address_t dst_addr;
+  u8 dst_addr_set = 0;
+  u8 ipv4_set = 0;
+  u8 ipv6_set = 0;
+  u8 disable = 0;
+  clib_error_t *rv = 0;
+  u32 outer_fib_index = 0;
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "dst-ip %U", unformat_ip4_address, &dst_addr.ip4))
+	{
+	  dst_addr_set = 1;
+	  ipv4_set = 1;
+	}
+      else
+	if (unformat
+	    (input, "dst-ip %U", unformat_ip6_address, &dst_addr.ip6))
+	{
+	  dst_addr_set = 1;
+	  ipv6_set = 1;
+	}
+      else if (unformat (input, "outer-fib-index %d", &outer_fib_index))
+	{
+	}
+
+      else if (unformat (input, "disable"))
+	disable = 1;
+      else
+	break;
+    }
+
+  if (dst_addr_set == 0)
+    return clib_error_return (0, "tunnel destination address not specified");
+  if (ipv4_set && ipv6_set)
+    return clib_error_return (0, "both IPv4 and IPv6 addresses specified");
+  if (!disable)
+    {
+      vxlan_gpe_enable_disable_ioam_for_dest (hm->vlib_main,
+					      dst_addr, outer_fib_index,
+					      ipv4_set, 1);
+    }
+  else
+    {
+      vxlan_gpe_ioam_disable_for_dest
+	(vm, dst_addr, outer_fib_index, ipv4_set);
+    }
+  return rv;
+}
+
+       /* *INDENT-OFF* */
+VLIB_CLI_COMMAND (vxlan_gpe_set_ioam_transit_rewrite_cmd, static) = {
+  .path = "set vxlan-gpe-ioam-transit",
+  .short_help = "set vxlan-gpe-ioam-transit dst-ip <dst_ip> [outer-fib-index <outer_fib_index>] [disable]",
+  .function = vxlan_gpe_set_ioam_transit_rewrite_command_fn,
+};
+/* *INDENT-ON* */
+
+clib_error_t *clear_vxlan_gpe_ioam_rewrite_command_fn
+  (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd)
 {
   return (vxlan_gpe_ioam_disable (0, 0, 0));
 }
@@ -387,6 +715,55 @@
 };
 /* *INDENT-ON* */
 
+
+/**
+ * Function definition to backwalk a FIB node
+ */
+static fib_node_back_walk_rc_t
+vxlan_gpe_ioam_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
+{
+  vxlan_gpe_refresh_output_feature_on_all_dest ();
+  return (FIB_NODE_BACK_WALK_CONTINUE);
+}
+
+/**
+ * Function definition to get a FIB node from its index
+ */
+static fib_node_t *
+vxlan_gpe_ioam_fib_node_get (fib_node_index_t index)
+{
+  vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
+  return (&hm->node);
+}
+
+/**
+ * Function definition to inform the FIB node that its last lock has gone.
+ */
+static void
+vxlan_gpe_ioam_last_lock_gone (fib_node_t * node)
+{
+  ASSERT (0);
+}
+
+
+/*
+ * Virtual function table registered by MPLS GRE tunnels
+ * for participation in the FIB object graph.
+ */
+const static fib_node_vft_t vxlan_gpe_ioam_vft = {
+  .fnv_get = vxlan_gpe_ioam_fib_node_get,
+  .fnv_last_lock = vxlan_gpe_ioam_last_lock_gone,
+  .fnv_back_walk = vxlan_gpe_ioam_back_walk,
+};
+
+void
+vxlan_gpe_ioam_interface_init (void)
+{
+  vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main;
+  hm->fib_entry_type = fib_node_register_new_type (&vxlan_gpe_ioam_vft);
+  return;
+}
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
diff --git a/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_ioam.h b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_ioam.h
index ac82480..3b7d72c 100644
--- a/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_ioam.h
+++ b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_ioam.h
@@ -21,8 +21,27 @@
 #include <vnet/ip/ip.h>
 
 
+typedef struct vxlan_gpe_sw_interface_
+{
+  u32 sw_if_index;
+} vxlan_gpe_ioam_sw_interface_t;
+
+typedef struct vxlan_gpe_dest_tunnels_
+{
+  ip46_address_t dst_addr;
+  u32 fp_proto;
+  u32 sibling_index;
+  fib_node_index_t fib_entry_index;
+  u32 outer_fib_index;
+} vxlan_gpe_ioam_dest_tunnels_t;
+
 typedef struct vxlan_gpe_ioam_main_
 {
+  /**
+   * Linkage into the FIB object graph
+   */
+  fib_node_t node;
+
   /* time scale transform. Joy. */
   u32 unix_time_0;
   f64 vlib_time_0;
@@ -65,6 +84,18 @@
   uword encap_v4_next_node;
   uword encap_v6_next_node;
 
+  /* Software interfaces. */
+  vxlan_gpe_ioam_sw_interface_t *sw_interfaces;
+
+  /* hash ip4/ip6 -> list of destinations for doing transit iOAM operation */
+  vxlan_gpe_ioam_dest_tunnels_t *dst_tunnels;
+  uword *dst_by_ip4;
+  uword *dst_by_ip6;
+
+  /** per sw_if_index, to maintain bitmap */
+  u8 *bool_ref_by_sw_if_index;
+  fib_node_type_t fib_entry_type;
+
   /** State convenience vlib_main_t */
   vlib_main_t *vlib_main;
   /** State convenience vnet_main_t */
@@ -87,6 +118,7 @@
 
 vlib_node_registration_t vxlan_gpe_encap_ioam_v4_node;
 vlib_node_registration_t vxlan_gpe_decap_ioam_v4_node;
+vlib_node_registration_t vxlan_gpe_transit_ioam_v4_node;
 
 clib_error_t *vxlan_gpe_ioam_enable (int has_trace_option, int has_pot_option,
 				     int has_ppc_option);
@@ -123,6 +155,15 @@
 int vxlan_gpe_trace_profile_setup (void);
 
 int vxlan_gpe_trace_profile_cleanup (void);
+extern void vxlan_gpe_ioam_interface_init (void);
+int
+vxlan_gpe_enable_disable_ioam_for_dest (vlib_main_t * vm,
+					ip46_address_t dst_addr,
+					u32 outer_fib_index,
+					u8 is_ipv4, u8 is_add);
+int vxlan_gpe_ioam_disable_for_dest
+  (vlib_main_t * vm, ip46_address_t dst_addr, u32 outer_fib_index,
+   u8 ipv4_set);
 
 typedef enum
 {
diff --git a/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_ioam_trace.c b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_ioam_trace.c
index a10e85a..e37b164 100644
--- a/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_ioam_trace.c
+++ b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_ioam_trace.c
@@ -288,7 +288,11 @@
 	  if (trace->ioam_trace_type & BIT_TTL_NODEID)
 	    {
 	      ip4_header_t *ip0 = vlib_buffer_get_current (b);
-	      *elt = clib_host_to_net_u32 (((ip0->ttl - 1) << 24) |
+	      /* The transit case is the only case where the TTL decrement happens
+	       * before iOAM processing. For now, use the use_adj flag as an overload.
+	       * We can probably use a separate flag instead of overloading the use_adj flag.
+	       */
+	      *elt = clib_host_to_net_u32 (((ip0->ttl - 1 + use_adj) << 24) |
 					   profile->node_id);
 	      elt++;
 	    }
diff --git a/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_ioam_util.h b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_ioam_util.h
index 138eba1..c0ad8d9 100644
--- a/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_ioam_util.h
+++ b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_ioam_util.h
@@ -83,7 +83,6 @@
 always_inline void
 vxlan_gpe_encap_decap_ioam_v4_one_inline (vlib_main_t * vm,
 					  vlib_node_runtime_t * node,
-					  vxlan_gpe_main_t * ngm,
 					  vlib_buffer_t * b0,
 					  u32 * next0, u32 drop_node_val,
 					  u8 use_adj)
diff --git a/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_test.c b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_test.c
index 500e056..47253eb 100644
--- a/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_test.c
+++ b/plugins/ioam-plugin/ioam/lib-vxlan-gpe/vxlan_gpe_test.c
@@ -64,7 +64,9 @@
 _(vxlan_gpe_ioam_enable_reply)                    \
 _(vxlan_gpe_ioam_disable_reply)                   \
 _(vxlan_gpe_ioam_vni_enable_reply)                \
-_(vxlan_gpe_ioam_vni_disable_reply)
+_(vxlan_gpe_ioam_vni_disable_reply)               \
+_(vxlan_gpe_ioam_transit_enable_reply)            \
+_(vxlan_gpe_ioam_transit_disable_reply)
 
 #define _(n)                                            \
     static void vl_api_##n##_t_handler                  \
@@ -86,11 +88,13 @@
  * Table of message reply handlers, must include boilerplate handlers
  * we just generated
  */
-#define foreach_vpe_api_reply_msg                                       \
-_(VXLAN_GPE_IOAM_ENABLE_REPLY, vxlan_gpe_ioam_enable_reply)             \
-_(VXLAN_GPE_IOAM_DISABLE_REPLY, vxlan_gpe_ioam_disable_reply)           \
-_(VXLAN_GPE_IOAM_VNI_ENABLE_REPLY, vxlan_gpe_ioam_vni_enable_reply)     \
-_(VXLAN_GPE_IOAM_VNI_DISABLE_REPLY, vxlan_gpe_ioam_vni_disable_reply)   \
+#define foreach_vpe_api_reply_msg                                              \
+_(VXLAN_GPE_IOAM_ENABLE_REPLY, vxlan_gpe_ioam_enable_reply)                    \
+_(VXLAN_GPE_IOAM_DISABLE_REPLY, vxlan_gpe_ioam_disable_reply)                  \
+_(VXLAN_GPE_IOAM_VNI_ENABLE_REPLY, vxlan_gpe_ioam_vni_enable_reply)            \
+_(VXLAN_GPE_IOAM_VNI_DISABLE_REPLY, vxlan_gpe_ioam_vni_disable_reply)          \
+_(VXLAN_GPE_IOAM_TRANSIT_ENABLE_REPLY, vxlan_gpe_ioam_transit_enable_reply)    \
+_(VXLAN_GPE_IOAM_TRANSIT_DISABLE_REPLY, vxlan_gpe_ioam_transit_disable_reply)  \
 
 
 /* M: construct, but don't yet send a message */
@@ -379,7 +383,149 @@
   return 0;
 }
 
+static int
+api_vxlan_gpe_ioam_transit_enable (vat_main_t * vam)
+{
+  vxlan_gpe_test_main_t *sm = &vxlan_gpe_test_main;
 
+  unformat_input_t *line_input = vam->input;
+  vl_api_vxlan_gpe_ioam_transit_enable_t *mp;
+  ip4_address_t local4;
+  ip6_address_t local6;
+  u8 ipv4_set = 0, ipv6_set = 0;
+  u8 local_set = 0;
+  u32 outer_fib_index = 0;
+  f64 timeout;
+
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (line_input, "dst-ip %U", unformat_ip4_address, &local4))
+	{
+	  local_set = 1;
+	  ipv4_set = 1;
+	}
+      else if (unformat (line_input, "dst-ip %U",
+			 unformat_ip6_address, &local6))
+	{
+	  local_set = 1;
+	  ipv6_set = 1;
+	}
+
+      else if (unformat (line_input, "outer-fib-index %d", &outer_fib_index))
+	;
+      else
+	{
+	  errmsg ("parse error '%U'\n", format_unformat_error, line_input);
+	  return -99;
+	}
+    }
+
+  if (local_set == 0)
+    {
+      errmsg ("destination address not specified\n");
+      return -99;
+    }
+  if (ipv4_set && ipv6_set)
+    {
+      errmsg ("both IPv4 and IPv6 addresses specified");
+      return -99;
+    }
+
+
+  M (VXLAN_GPE_IOAM_TRANSIT_ENABLE, vxlan_gpe_ioam_transit_enable);
+
+
+  if (ipv6_set)
+    {
+      errmsg ("IPv6 currently unsupported");
+      return -1;
+    }
+  else
+    {
+      clib_memcpy (&mp->dst_addr, &local4, sizeof (local4));
+    }
+
+  mp->outer_fib_index = htonl (outer_fib_index);
+  mp->is_ipv6 = ipv6_set;
+
+  S;
+  W;
+
+  return (0);
+}
+
+static int
+api_vxlan_gpe_ioam_transit_disable (vat_main_t * vam)
+{
+  vxlan_gpe_test_main_t *sm = &vxlan_gpe_test_main;
+
+  unformat_input_t *line_input = vam->input;
+  vl_api_vxlan_gpe_ioam_transit_disable_t *mp;
+  ip4_address_t local4;
+  ip6_address_t local6;
+  u8 ipv4_set = 0, ipv6_set = 0;
+  u8 local_set = 0;
+  u32 outer_fib_index;
+  f64 timeout;
+
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (line_input, "dst-ip %U", unformat_ip4_address, &local4))
+	{
+	  local_set = 1;
+	  ipv4_set = 1;
+	}
+      else if (unformat (line_input, "dst-ip %U",
+			 unformat_ip6_address, &local6))
+	{
+	  local_set = 1;
+	  ipv6_set = 1;
+	}
+
+      else if (unformat (line_input, "outer-fib-index %d", &outer_fib_index))
+	;
+      else
+	{
+	  errmsg ("parse error '%U'\n", format_unformat_error, line_input);
+	  return -99;
+	}
+    }
+
+  if (local_set == 0)
+    {
+      errmsg ("destination address not specified\n");
+      return -99;
+    }
+  if (ipv4_set && ipv6_set)
+    {
+      errmsg ("both IPv4 and IPv6 addresses specified");
+      return -99;
+    }
+
+
+  M (VXLAN_GPE_IOAM_TRANSIT_DISABLE, vxlan_gpe_ioam_transit_disable);
+
+
+  if (ipv6_set)
+    {
+      return -1;
+    }
+  else
+    {
+      clib_memcpy (&mp->dst_addr, &local4, sizeof (local4));
+    }
+
+  mp->outer_fib_index = htonl (outer_fib_index);
+  mp->is_ipv6 = ipv6_set;
+
+  S;
+  W;
+
+
+  return (0);
+}
 
 /*
  * List of messages that the api test plugin sends,
@@ -393,6 +539,10 @@
   "local <local_vtep_ip> remote <remote_vtep_ip> vni <vnid>") \
 _(vxlan_gpe_ioam_vni_disable, ""\
   "local <local_vtep_ip> remote <remote_vtep_ip> vni <vnid>") \
+_(vxlan_gpe_ioam_transit_enable, ""\
+  "dst-ip <dst_ip> [outer-fib-index <outer_fib_index>]") \
+_(vxlan_gpe_ioam_transit_disable, ""\
+  "dst-ip <dst_ip> [outer-fib-index <outer_fib_index>]") \
 
 
 void