vxlan-gpe: add udp-port configuration support

similar behavior as here: 839dcc0fb7313638d9b8f52a9db81350dddfe461

Type: improvement

Signed-off-by: Artem Glazychev <artem.glazychev@xored.com>
Change-Id: I1b0a8f8f3dab48839e27df7065cf5f786cf0b5e9
diff --git a/src/plugins/ioam/lib-vxlan-gpe/vxlan_gpe_api.c b/src/plugins/ioam/lib-vxlan-gpe/vxlan_gpe_api.c
index 9f6d181..d61832d 100644
--- a/src/plugins/ioam/lib-vxlan-gpe/vxlan_gpe_api.c
+++ b/src/plugins/ioam/lib-vxlan-gpe/vxlan_gpe_api.c
@@ -27,6 +27,7 @@
 #include <vlibmemory/api.h>
 #include <vnet/format_fns.h>
 #include <vnet/ip/ip_types_api.h>
+#include <vnet/udp/udp_local.h>
 
 /* define message IDs */
 #include <ioam/lib-vxlan-gpe/ioam_vxlan_gpe.api_enum.h>
@@ -92,7 +93,7 @@
       clib_memcpy (&key4.remote, &mp->remote.un.ip4, sizeof (key4.remote));
       vni = clib_net_to_host_u32 (mp->vni);
       key4.vni = clib_host_to_net_u32 (vni << 8);
-      key4.pad = 0;
+      key4.port = clib_host_to_net_u16 (UDP_DST_PORT_VXLAN_GPE);
 
       p = hash_get_mem (gm->vxlan4_gpe_tunnel_by_key, &key4);
     }
@@ -141,7 +142,7 @@
       clib_memcpy (&key4.remote, &mp->remote, sizeof (key4.remote));
       vni = clib_net_to_host_u32 (mp->vni);
       key4.vni = clib_host_to_net_u32 (vni << 8);
-      key4.pad = 0;
+      key4.port = clib_host_to_net_u16 (UDP_DST_PORT_VXLAN_GPE);
 
       p = hash_get_mem (gm->vxlan4_gpe_tunnel_by_key, &key4);
     }
diff --git a/src/plugins/ioam/lib-vxlan-gpe/vxlan_gpe_ioam.c b/src/plugins/ioam/lib-vxlan-gpe/vxlan_gpe_ioam.c
index 108b0c0..59798dc 100644
--- a/src/plugins/ioam/lib-vxlan-gpe/vxlan_gpe_ioam.c
+++ b/src/plugins/ioam/lib-vxlan-gpe/vxlan_gpe_ioam.c
@@ -19,6 +19,7 @@
 #include <vnet/dpo/load_balance.h>
 #include <vnet/fib/ip4_fib.h>
 #include <vnet/fib/fib_entry.h>
+#include <vnet/udp/udp_local.h>
 
 vxlan_gpe_ioam_main_t vxlan_gpe_ioam_main;
 
@@ -478,7 +479,7 @@
       key4.local = local.ip4.as_u32;
       key4.remote = remote.ip4.as_u32;
       key4.vni = clib_host_to_net_u32 (vni << 8);
-      key4.pad = 0;
+      key4.port = clib_host_to_net_u16 (UDP_DST_PORT_VXLAN_GPE);
       p = hash_get_mem (gm->vxlan4_gpe_tunnel_by_key, &key4);
     }
   else
@@ -488,6 +489,7 @@
       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);
+      key6.port = clib_host_to_net_u16 (UDP_DST_PORT_VXLAN6_GPE);
       p = hash_get_mem (gm->vxlan6_gpe_tunnel_by_key, &key6);
     }
 
diff --git a/src/vnet/vxlan-gpe/decap.c b/src/vnet/vxlan-gpe/decap.c
index 035e8a3..6251361 100644
--- a/src/vnet/vxlan-gpe/decap.c
+++ b/src/vnet/vxlan-gpe/decap.c
@@ -79,10 +79,106 @@
   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
 
-
   return s;
 }
 
+typedef struct
+{
+  vxlan4_gpe_tunnel_key_t key;
+  vxlan_gpe_decap_info_t val;
+} vxlan4_gpe_tunnel_cache_t;
+
+static const vxlan_gpe_decap_info_t decap_not_found = {
+  .tunnel_index = ~0,
+  .next_index = VXLAN_GPE_INPUT_NEXT_DROP,
+  .error = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL
+};
+
+always_inline vxlan_gpe_decap_info_t
+vxlan4_gpe_find_tunnel (vxlan_gpe_main_t *nngm,
+			vxlan4_gpe_tunnel_cache_t *cache,
+			ip4_vxlan_gpe_header_t *iuvn4_0)
+{
+  /* Make sure VXLAN GPE tunnel exist according to packet S/D IP, UDP port and
+   * VNI */
+  vxlan4_gpe_tunnel_key_t key4 = {
+    .local = iuvn4_0->ip4.dst_address.as_u32,
+    .remote = iuvn4_0->ip4.src_address.as_u32,
+    .vni = iuvn4_0->vxlan.vni_res,
+    .port = (u32) iuvn4_0->udp.dst_port,
+  };
+
+  if (PREDICT_TRUE (key4.as_u64[0] == cache->key.as_u64[0] &&
+		    key4.as_u64[1] == cache->key.as_u64[1]))
+    {
+      /* cache hit */
+      return cache->val;
+    }
+
+  uword *p = hash_get_mem (nngm->vxlan4_gpe_tunnel_by_key, &key4);
+  if (PREDICT_TRUE (p != 0))
+    {
+      u32 next = (iuvn4_0->vxlan.protocol < VXLAN_GPE_PROTOCOL_MAX) ?
+		   nngm->decap_next_node_list[iuvn4_0->vxlan.protocol] :
+		   VXLAN_GPE_INPUT_NEXT_DROP;
+
+      cache->key.as_u64[0] = key4.as_u64[0];
+      cache->key.as_u64[1] = key4.as_u64[1];
+
+      cache->val.error = 0;
+      cache->val.tunnel_index = p[0];
+      cache->val.next_index = next;
+
+      return cache->val;
+    }
+
+  return decap_not_found;
+}
+
+typedef struct
+{
+  vxlan6_gpe_tunnel_key_t key;
+  vxlan_gpe_decap_info_t val;
+} vxlan6_gpe_tunnel_cache_t;
+
+always_inline vxlan_gpe_decap_info_t
+vxlan6_gpe_find_tunnel (vxlan_gpe_main_t *nngm,
+			vxlan6_gpe_tunnel_cache_t *cache,
+			ip6_vxlan_gpe_header_t *iuvn6_0)
+{
+  /* Make sure VXLAN GPE tunnel exist according to packet S/D IP, UDP port and
+   * VNI */
+  vxlan6_gpe_tunnel_key_t key6;
+
+  ip6_address_copy (&key6.local, &iuvn6_0->ip6.dst_address);
+  ip6_address_copy (&key6.remote, &iuvn6_0->ip6.src_address);
+  key6.vni = iuvn6_0->vxlan.vni_res;
+  key6.port = iuvn6_0->udp.dst_port;
+
+  if (PREDICT_TRUE (memcmp (&key6, &cache->key, sizeof (cache->key)) == 0))
+    {
+      /* cache hit */
+      return cache->val;
+    }
+
+  uword *p = hash_get_mem (nngm->vxlan6_gpe_tunnel_by_key, &key6);
+  if (PREDICT_TRUE (p != 0))
+    {
+      u32 next = (iuvn6_0->vxlan.protocol < VXLAN_GPE_PROTOCOL_MAX) ?
+		   nngm->decap_next_node_list[iuvn6_0->vxlan.protocol] :
+		   VXLAN_GPE_INPUT_NEXT_DROP;
+
+      clib_memcpy_fast (&cache->key, &key6, sizeof (key6));
+      cache->val.error = 0;
+      cache->val.tunnel_index = p[0];
+      cache->val.next_index = next;
+
+      return cache->val;
+    }
+
+  return decap_not_found;
+}
+
 /**
  * @brief Common processing for IPv4 and IPv6 VXLAN GPE decap dispatch functions
  *
@@ -111,17 +207,16 @@
   vxlan_gpe_main_t *nngm = &vxlan_gpe_main;
   vnet_main_t *vnm = nngm->vnet_main;
   vnet_interface_main_t *im = &vnm->interface_main;
-  u32 last_tunnel_index = ~0;
-  vxlan4_gpe_tunnel_key_t last_key4;
-  vxlan6_gpe_tunnel_key_t last_key6;
+  vxlan4_gpe_tunnel_cache_t last4;
+  vxlan6_gpe_tunnel_cache_t last6;
   u32 pkts_decapsulated = 0;
   u32 thread_index = vm->thread_index;
   u32 stats_sw_if_index, stats_n_packets, stats_n_bytes;
 
   if (is_ip4)
-    clib_memset (&last_key4, 0xff, sizeof (last_key4));
+    clib_memset (&last4, 0xff, sizeof (last4));
   else
-    clib_memset (&last_key6, 0xff, sizeof (last_key6));
+    clib_memset (&last6, 0xff, sizeof (last6));
 
   from = vlib_frame_vector_args (from_frame);
   n_left_from = from_frame->n_vectors;
@@ -143,11 +238,8 @@
 	  u32 next0, next1;
 	  ip4_vxlan_gpe_header_t *iuvn4_0, *iuvn4_1;
 	  ip6_vxlan_gpe_header_t *iuvn6_0, *iuvn6_1;
-	  uword *p0, *p1;
-	  u32 tunnel_index0, tunnel_index1;
+	  vxlan_gpe_decap_info_t di0, di1;
 	  vxlan_gpe_tunnel_t *t0, *t1;
-	  vxlan4_gpe_tunnel_key_t key4_0, key4_1;
-	  vxlan6_gpe_tunnel_key_t key6_0, key6_1;
 	  u32 error0, error1;
 	  u32 sw_if_index0, sw_if_index1, len0, len1;
 
@@ -193,6 +285,9 @@
 	      /* pop (ip, udp, vxlan) */
 	      vlib_buffer_advance (b0, sizeof (*iuvn4_0));
 	      vlib_buffer_advance (b1, sizeof (*iuvn4_1));
+
+	      di0 = vxlan4_gpe_find_tunnel (nngm, &last4, iuvn4_0);
+	      di1 = vxlan4_gpe_find_tunnel (nngm, &last4, iuvn4_1);
 	    }
 	  else
 	    {
@@ -210,125 +305,20 @@
 	      /* pop (ip, udp, vxlan) */
 	      vlib_buffer_advance (b0, sizeof (*iuvn6_0));
 	      vlib_buffer_advance (b1, sizeof (*iuvn6_1));
+
+	      di0 = vxlan6_gpe_find_tunnel (nngm, &last6, iuvn6_0);
+	      di1 = vxlan6_gpe_find_tunnel (nngm, &last6, iuvn6_1);
 	    }
 
-	  tunnel_index0 = ~0;
-	  tunnel_index1 = ~0;
-	  error0 = 0;
-	  error1 = 0;
-
-	  if (is_ip4)
+	  /* Process packet 0 */
+	  next0 = di0.next_index;
+	  error0 = di0.error;
+	  if (error0 != 0)
 	    {
-	      next0 =
-		(iuvn4_0->vxlan.protocol < VXLAN_GPE_PROTOCOL_MAX) ?
-		nngm->decap_next_node_list[iuvn4_0->vxlan.protocol] :
-		VXLAN_GPE_INPUT_NEXT_DROP;
-	      next1 =
-		(iuvn4_1->vxlan.protocol < VXLAN_GPE_PROTOCOL_MAX) ?
-		nngm->decap_next_node_list[iuvn4_1->vxlan.protocol] :
-		VXLAN_GPE_INPUT_NEXT_DROP;
-
-	      key4_0.local = iuvn4_0->ip4.dst_address.as_u32;
-	      key4_1.local = iuvn4_1->ip4.dst_address.as_u32;
-
-	      key4_0.remote = iuvn4_0->ip4.src_address.as_u32;
-	      key4_1.remote = iuvn4_1->ip4.src_address.as_u32;
-
-	      key4_0.vni = iuvn4_0->vxlan.vni_res;
-	      key4_1.vni = iuvn4_1->vxlan.vni_res;
-
-	      key4_0.pad = 0;
-	      key4_1.pad = 0;
-	    }
-	  else			/* is_ip6 */
-	    {
-	      next0 = (iuvn6_0->vxlan.protocol < node->n_next_nodes) ?
-		iuvn6_0->vxlan.protocol : VXLAN_GPE_INPUT_NEXT_DROP;
-	      next1 = (iuvn6_1->vxlan.protocol < node->n_next_nodes) ?
-		iuvn6_1->vxlan.protocol : VXLAN_GPE_INPUT_NEXT_DROP;
-
-	      key6_0.local.as_u64[0] = iuvn6_0->ip6.dst_address.as_u64[0];
-	      key6_0.local.as_u64[1] = iuvn6_0->ip6.dst_address.as_u64[1];
-	      key6_1.local.as_u64[0] = iuvn6_1->ip6.dst_address.as_u64[0];
-	      key6_1.local.as_u64[1] = iuvn6_1->ip6.dst_address.as_u64[1];
-
-	      key6_0.remote.as_u64[0] = iuvn6_0->ip6.src_address.as_u64[0];
-	      key6_0.remote.as_u64[1] = iuvn6_0->ip6.src_address.as_u64[1];
-	      key6_1.remote.as_u64[0] = iuvn6_1->ip6.src_address.as_u64[0];
-	      key6_1.remote.as_u64[1] = iuvn6_1->ip6.src_address.as_u64[1];
-
-	      key6_0.vni = iuvn6_0->vxlan.vni_res;
-	      key6_1.vni = iuvn6_1->vxlan.vni_res;
+	      goto trace0;
 	    }
 
-	  /* Processing packet 0 */
-	  if (is_ip4)
-	    {
-	      /* Processing for key4_0 */
-	      if (PREDICT_FALSE ((key4_0.as_u64[0] != last_key4.as_u64[0])
-				 || (key4_0.as_u64[1] !=
-				     last_key4.as_u64[1])))
-		{
-		  p0 = hash_get_mem (nngm->vxlan4_gpe_tunnel_by_key, &key4_0);
-
-		  if (p0 == 0)
-		    {
-		      error0 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL;
-		      goto trace0;
-		    }
-
-		  last_key4.as_u64[0] = key4_0.as_u64[0];
-		  last_key4.as_u64[1] = key4_0.as_u64[1];
-		  tunnel_index0 = last_tunnel_index = p0[0];
-		}
-	      else
-		tunnel_index0 = last_tunnel_index;
-	    }
-	  else			/* is_ip6 */
-	    {
-	      next0 =
-		(iuvn6_0->vxlan.protocol < VXLAN_GPE_PROTOCOL_MAX) ?
-		nngm->decap_next_node_list[iuvn6_0->vxlan.protocol] :
-		VXLAN_GPE_INPUT_NEXT_DROP;
-	      next1 =
-		(iuvn6_1->vxlan.protocol < VXLAN_GPE_PROTOCOL_MAX) ?
-		nngm->decap_next_node_list[iuvn6_1->vxlan.protocol] :
-		VXLAN_GPE_INPUT_NEXT_DROP;
-
-	      key6_0.local.as_u64[0] = iuvn6_0->ip6.dst_address.as_u64[0];
-	      key6_0.local.as_u64[1] = iuvn6_0->ip6.dst_address.as_u64[1];
-	      key6_1.local.as_u64[0] = iuvn6_1->ip6.dst_address.as_u64[0];
-	      key6_1.local.as_u64[1] = iuvn6_1->ip6.dst_address.as_u64[1];
-
-	      key6_0.remote.as_u64[0] = iuvn6_0->ip6.src_address.as_u64[0];
-	      key6_0.remote.as_u64[1] = iuvn6_0->ip6.src_address.as_u64[1];
-	      key6_1.remote.as_u64[0] = iuvn6_1->ip6.src_address.as_u64[0];
-	      key6_1.remote.as_u64[1] = iuvn6_1->ip6.src_address.as_u64[1];
-
-	      key6_0.vni = iuvn6_0->vxlan.vni_res;
-	      key6_1.vni = iuvn6_1->vxlan.vni_res;
-
-	      /* Processing for key6_0 */
-	      if (PREDICT_FALSE
-		  (memcmp (&key6_0, &last_key6, sizeof (last_key6)) != 0))
-		{
-		  p0 = hash_get_mem (nngm->vxlan6_gpe_tunnel_by_key, &key6_0);
-
-		  if (p0 == 0)
-		    {
-		      error0 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL;
-		      goto trace0;
-		    }
-
-		  memcpy (&last_key6, &key6_0, sizeof (key6_0));
-		  tunnel_index0 = last_tunnel_index = p0[0];
-		}
-	      else
-		tunnel_index0 = last_tunnel_index;
-	    }
-
-	  t0 = pool_elt_at_index (nngm->tunnels, tunnel_index0);
-
+	  t0 = pool_elt_at_index (nngm->tunnels, di0.tunnel_index);
 
 	  sw_if_index0 = t0->sw_if_index;
 	  len0 = vlib_buffer_length_in_chain (vm, b0);
@@ -372,54 +362,18 @@
 		vlib_add_trace (vm, node, b0, sizeof (*tr));
 	      tr->next_index = next0;
 	      tr->error = error0;
-	      tr->tunnel_index = tunnel_index0;
+	      tr->tunnel_index = di0.tunnel_index;
 	    }
 
 	  /* Process packet 1 */
-	  if (is_ip4)
+	  next1 = di1.next_index;
+	  error1 = di1.error;
+	  if (error1 != 0)
 	    {
-	      /* Processing for key4_1 */
-	      if (PREDICT_FALSE ((key4_1.as_u64[0] != last_key4.as_u64[0])
-				 || (key4_1.as_u64[1] !=
-				     last_key4.as_u64[1])))
-		{
-		  p1 = hash_get_mem (nngm->vxlan4_gpe_tunnel_by_key, &key4_1);
-
-		  if (p1 == 0)
-		    {
-		      error1 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL;
-		      goto trace1;
-		    }
-
-		  last_key4.as_u64[0] = key4_1.as_u64[0];
-		  last_key4.as_u64[1] = key4_1.as_u64[1];
-		  tunnel_index1 = last_tunnel_index = p1[0];
-		}
-	      else
-		tunnel_index1 = last_tunnel_index;
-	    }
-	  else			/* is_ip6 */
-	    {
-	      /* Processing for key6_1 */
-	      if (PREDICT_FALSE
-		  (memcmp (&key6_1, &last_key6, sizeof (last_key6)) != 0))
-		{
-		  p1 = hash_get_mem (nngm->vxlan6_gpe_tunnel_by_key, &key6_1);
-
-		  if (p1 == 0)
-		    {
-		      error1 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL;
-		      goto trace1;
-		    }
-
-		  memcpy (&last_key6, &key6_1, sizeof (key6_1));
-		  tunnel_index1 = last_tunnel_index = p1[0];
-		}
-	      else
-		tunnel_index1 = last_tunnel_index;
+	      goto trace1;
 	    }
 
-	  t1 = pool_elt_at_index (nngm->tunnels, tunnel_index1);
+	  t1 = pool_elt_at_index (nngm->tunnels, di1.tunnel_index);
 
 	  sw_if_index1 = t1->sw_if_index;
 	  len1 = vlib_buffer_length_in_chain (vm, b1);
@@ -466,7 +420,7 @@
 		vlib_add_trace (vm, node, b1, sizeof (*tr));
 	      tr->next_index = next1;
 	      tr->error = error1;
-	      tr->tunnel_index = tunnel_index1;
+	      tr->tunnel_index = di1.tunnel_index;
 	    }
 
 	  vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
@@ -481,11 +435,8 @@
 	  u32 next0;
 	  ip4_vxlan_gpe_header_t *iuvn4_0;
 	  ip6_vxlan_gpe_header_t *iuvn6_0;
-	  uword *p0;
-	  u32 tunnel_index0;
+	  vxlan_gpe_decap_info_t di0;
 	  vxlan_gpe_tunnel_t *t0;
-	  vxlan4_gpe_tunnel_key_t key4_0;
-	  vxlan6_gpe_tunnel_key_t key6_0;
 	  u32 error0;
 	  u32 sw_if_index0, len0;
 
@@ -509,6 +460,8 @@
 
 	      /* pop (ip, udp, vxlan) */
 	      vlib_buffer_advance (b0, sizeof (*iuvn4_0));
+
+	      di0 = vxlan4_gpe_find_tunnel (nngm, &last4, iuvn4_0);
 	    }
 	  else
 	    {
@@ -521,77 +474,18 @@
 
 	      /* pop (ip, udp, vxlan) */
 	      vlib_buffer_advance (b0, sizeof (*iuvn6_0));
-	    }
 
-	  tunnel_index0 = ~0;
-	  error0 = 0;
-
-	  if (is_ip4)
-	    {
-	      next0 =
-		(iuvn4_0->vxlan.protocol < VXLAN_GPE_PROTOCOL_MAX) ?
-		nngm->decap_next_node_list[iuvn4_0->vxlan.protocol] :
-		VXLAN_GPE_INPUT_NEXT_DROP;
-
-	      key4_0.local = iuvn4_0->ip4.dst_address.as_u32;
-	      key4_0.remote = iuvn4_0->ip4.src_address.as_u32;
-	      key4_0.vni = iuvn4_0->vxlan.vni_res;
-	      key4_0.pad = 0;
-
-	      /* Processing for key4_0 */
-	      if (PREDICT_FALSE ((key4_0.as_u64[0] != last_key4.as_u64[0])
-				 || (key4_0.as_u64[1] !=
-				     last_key4.as_u64[1])))
-		{
-		  p0 = hash_get_mem (nngm->vxlan4_gpe_tunnel_by_key, &key4_0);
-
-		  if (p0 == 0)
-		    {
-		      error0 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL;
-		      goto trace00;
-		    }
-
-		  last_key4.as_u64[0] = key4_0.as_u64[0];
-		  last_key4.as_u64[1] = key4_0.as_u64[1];
-		  tunnel_index0 = last_tunnel_index = p0[0];
+	      di0 = vxlan6_gpe_find_tunnel (nngm, &last6, iuvn6_0);
 		}
-	      else
-		tunnel_index0 = last_tunnel_index;
-	    }
-	  else			/* is_ip6 */
+
+	  next0 = di0.next_index;
+	  error0 = di0.error;
+	  if (error0 != 0)
 	    {
-	      next0 =
-		(iuvn6_0->vxlan.protocol < VXLAN_GPE_PROTOCOL_MAX) ?
-		nngm->decap_next_node_list[iuvn6_0->vxlan.protocol] :
-		VXLAN_GPE_INPUT_NEXT_DROP;
-
-	      key6_0.local.as_u64[0] = iuvn6_0->ip6.dst_address.as_u64[0];
-	      key6_0.local.as_u64[1] = iuvn6_0->ip6.dst_address.as_u64[1];
-	      key6_0.remote.as_u64[0] = iuvn6_0->ip6.src_address.as_u64[0];
-	      key6_0.remote.as_u64[1] = iuvn6_0->ip6.src_address.as_u64[1];
-	      key6_0.vni = iuvn6_0->vxlan.vni_res;
-
-	      /* Processing for key6_0 */
-	      if (PREDICT_FALSE
-		  (memcmp (&key6_0, &last_key6, sizeof (last_key6)) != 0))
-		{
-		  p0 = hash_get_mem (nngm->vxlan6_gpe_tunnel_by_key, &key6_0);
-
-		  if (p0 == 0)
-		    {
-		      error0 = VXLAN_GPE_ERROR_NO_SUCH_TUNNEL;
-		      goto trace00;
-		    }
-
-		  memcpy (&last_key6, &key6_0, sizeof (key6_0));
-		  tunnel_index0 = last_tunnel_index = p0[0];
-		}
-	      else
-		tunnel_index0 = last_tunnel_index;
+	      goto trace00;
 	    }
 
-	  t0 = pool_elt_at_index (nngm->tunnels, tunnel_index0);
-
+	  t0 = pool_elt_at_index (nngm->tunnels, di0.tunnel_index);
 
 	  sw_if_index0 = t0->sw_if_index;
 	  len0 = vlib_buffer_length_in_chain (vm, b0);
@@ -637,7 +531,7 @@
 		vlib_add_trace (vm, node, b0, sizeof (*tr));
 	      tr->next_index = next0;
 	      tr->error = error0;
-	      tr->tunnel_index = tunnel_index0;
+	      tr->tunnel_index = di0.tunnel_index;
 	    }
 	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
 					   n_left_to_next, bi0, next0);
@@ -794,6 +688,9 @@
 				   matching a local VTEP address */
   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
 
+  vxlan4_gpe_tunnel_cache_t last4;
+  vxlan6_gpe_tunnel_cache_t last6;
+
   from = vlib_frame_vector_args (frame);
   n_left_from = frame->n_vectors;
   next_index = node->cached_next_index;
@@ -804,9 +701,15 @@
     ip4_forward_next_trace (vm, node, frame, VLIB_TX);
 
   if (is_ip4)
-    vtep4_key_init (&last_vtep4);
+    {
+      vtep4_key_init (&last_vtep4);
+      clib_memset (&last4, 0xff, sizeof last4);
+    }
   else
-    vtep6_key_init (&last_vtep6);
+    {
+      vtep6_key_init (&last_vtep6);
+      clib_memset (&last6, 0xff, sizeof last6);
+    }
 
   while (n_left_from > 0)
     {
@@ -818,6 +721,9 @@
 	  ip4_header_t *ip40, *ip41;
 	  ip6_header_t *ip60, *ip61;
 	  udp_header_t *udp0, *udp1;
+	  ip4_vxlan_gpe_header_t *iuvn4_0, *iuvn4_1;
+	  ip6_vxlan_gpe_header_t *iuvn6_0, *iuvn6_1;
+	  vxlan_gpe_decap_info_t di0, di1;
 	  u32 bi0, ip_len0, udp_len0, flags0, next0;
 	  u32 bi1, ip_len1, udp_len1, flags1, next1;
 	  i32 len_diff0, len_diff1;
@@ -874,12 +780,20 @@
 	    goto exit0;		/* not UDP packet */
 
 	  if (is_ip4)
-	    udp0 = ip4_next_header (ip40);
+	    {
+	      udp0 = ip4_next_header (ip40);
+	      iuvn4_0 = vlib_buffer_get_current (b0);
+	      di0 = vxlan4_gpe_find_tunnel (ngm, &last4, iuvn4_0);
+	    }
 	  else
-	    udp0 = ip6_next_header (ip60);
+	    {
+	      udp0 = ip6_next_header (ip60);
+	      iuvn6_0 = vlib_buffer_get_current (b0);
+	      di0 = vxlan6_gpe_find_tunnel (ngm, &last6, iuvn6_0);
+	    }
 
-	  if (udp0->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_VXLAN_GPE))
-	    goto exit0;		/* not VXLAN packet */
+	  if (PREDICT_FALSE (di0.tunnel_index == ~0))
+	    goto exit0; /* unknown interface */
 
 	  /* Validate DIP against VTEPs */
 	  if (is_ip4)
@@ -957,12 +871,20 @@
 	    goto exit1;		/* not UDP packet */
 
 	  if (is_ip4)
-	    udp1 = ip4_next_header (ip41);
+	    {
+	      udp1 = ip4_next_header (ip41);
+	      iuvn4_1 = vlib_buffer_get_current (b1);
+	      di1 = vxlan4_gpe_find_tunnel (ngm, &last4, iuvn4_1);
+	    }
 	  else
-	    udp1 = ip6_next_header (ip61);
+	    {
+	      udp1 = ip6_next_header (ip61);
+	      iuvn6_1 = vlib_buffer_get_current (b1);
+	      di1 = vxlan6_gpe_find_tunnel (ngm, &last6, iuvn6_1);
+	    }
 
-	  if (udp1->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_VXLAN_GPE))
-	    goto exit1;		/* not VXLAN packet */
+	  if (PREDICT_FALSE (di1.tunnel_index == ~0))
+	    goto exit1; /* unknown interface */
 
 	  /* Validate DIP against VTEPs */
 	  if (is_ip4)
@@ -1046,6 +968,9 @@
 	  ip4_header_t *ip40;
 	  ip6_header_t *ip60;
 	  udp_header_t *udp0;
+	  ip4_vxlan_gpe_header_t *iuvn4_0;
+	  ip6_vxlan_gpe_header_t *iuvn6_0;
+	  vxlan_gpe_decap_info_t di0;
 	  u32 bi0, ip_len0, udp_len0, flags0, next0;
 	  i32 len_diff0;
 	  u8 error0, good_udp0, proto0;
@@ -1075,12 +1000,20 @@
 	    goto exit;		/* not UDP packet */
 
 	  if (is_ip4)
-	    udp0 = ip4_next_header (ip40);
+	    {
+	      udp0 = ip4_next_header (ip40);
+	      iuvn4_0 = vlib_buffer_get_current (b0);
+	      di0 = vxlan4_gpe_find_tunnel (ngm, &last4, iuvn4_0);
+	    }
 	  else
-	    udp0 = ip6_next_header (ip60);
+	    {
+	      udp0 = ip6_next_header (ip60);
+	      iuvn6_0 = vlib_buffer_get_current (b0);
+	      di0 = vxlan6_gpe_find_tunnel (ngm, &last6, iuvn6_0);
+	    }
 
-	  if (udp0->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_VXLAN_GPE))
-	    goto exit;		/* not VXLAN packet */
+	  if (PREDICT_FALSE (di0.tunnel_index == ~0))
+	    goto exit; /* unknown interface */
 
 	  /* Validate DIP against VTEPs */
 
diff --git a/src/vnet/vxlan-gpe/vxlan_gpe.api b/src/vnet/vxlan-gpe/vxlan_gpe.api
index 35d8c64..3cbd7ab 100644
--- a/src/vnet/vxlan-gpe/vxlan_gpe.api
+++ b/src/vnet/vxlan-gpe/vxlan_gpe.api
@@ -13,7 +13,7 @@
  * limitations under the License.
  */
 
-option version = "2.0.0";
+option version = "2.1.0";
 
 import "vnet/interface_types.api";
 import "vnet/ip/ip_types.api";
@@ -32,12 +32,48 @@
   bool is_add [default=true];
 };
 
+/** \brief Create or delete a VXLAN-GPE tunnel
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param local - Source IP address
+    @param remote - Destination IP address, can be multicast
+    @param local_port - Source UDP port. It is not included in sent packets. Used only for port registration
+    @param remote_port - Destination UDP port
+    @param mcast_sw_if_index - Interface for multicast destination
+    @param encap_vrf_id - Encap route table FIB index
+    @param decap_vrf_id - Decap route table FIB index
+    @param protocol - Encapsulated protocol
+    @param vni - The VXLAN Network Identifier, uint24
+    @param is_add - Use 1 to create the tunnel, 0 to remove it
+*/
+define vxlan_gpe_add_del_tunnel_v2
+{
+  u32 client_index;
+  u32 context;
+  vl_api_address_t local;
+  vl_api_address_t remote;
+  u16 local_port;
+  u16 remote_port;
+  vl_api_interface_index_t mcast_sw_if_index;
+  u32 encap_vrf_id;
+  u32 decap_vrf_id;
+  vl_api_ip_proto_t protocol;
+  u32 vni;
+  bool is_add [default=true];
+};
+
 define vxlan_gpe_add_del_tunnel_reply
 {
   u32 context;
   i32 retval;
   vl_api_interface_index_t sw_if_index;
 };
+define vxlan_gpe_add_del_tunnel_v2_reply
+{
+  u32 context;
+  i32 retval;
+  vl_api_interface_index_t sw_if_index;
+};
 
 define vxlan_gpe_tunnel_dump
 {
@@ -45,6 +81,12 @@
   u32 context;
   vl_api_interface_index_t sw_if_index;
 };
+define vxlan_gpe_tunnel_v2_dump
+{
+  u32 client_index;
+  u32 context;
+  vl_api_interface_index_t sw_if_index;
+};
 
 define vxlan_gpe_tunnel_details
 {
@@ -59,6 +101,21 @@
   u32 decap_vrf_id;
   bool is_ipv6;
 };
+define vxlan_gpe_tunnel_v2_details
+{
+  u32 context;
+  vl_api_interface_index_t sw_if_index;
+  vl_api_address_t local;
+  vl_api_address_t remote;
+  u16 local_port;
+  u16 remote_port;
+  u32 vni;
+  vl_api_ip_proto_t protocol;
+  vl_api_interface_index_t mcast_sw_if_index;
+  u32 encap_vrf_id;
+  u32 decap_vrf_id;
+  bool is_ipv6;
+};
 
 /** \brief Interface set vxlan-gpe-bypass request
     @param client_index - opaque cookie to identify the sender
diff --git a/src/vnet/vxlan-gpe/vxlan_gpe.c b/src/vnet/vxlan-gpe/vxlan_gpe.c
index 6fd0561..17ffcad 100644
--- a/src/vnet/vxlan-gpe/vxlan_gpe.c
+++ b/src/vnet/vxlan-gpe/vxlan_gpe.c
@@ -87,11 +87,12 @@
   vxlan_gpe_tunnel_t *t = va_arg (*args, vxlan_gpe_tunnel_t *);
   vxlan_gpe_main_t *ngm = &vxlan_gpe_main;
 
-  s = format (s, "[%d] lcl %U rmt %U vni %d fib-idx %d sw-if-idx %d ",
-	      t - ngm->tunnels,
-	      format_ip46_address, &t->local, IP46_TYPE_ANY,
-	      format_ip46_address, &t->remote, IP46_TYPE_ANY,
-	      t->vni, t->encap_fib_index, t->sw_if_index);
+  s = format (s,
+	      "[%d] lcl %U rmt %U lcl_port %d rmt_port %d vni %d "
+	      "fib-idx %d sw-if-idx %d ",
+	      t - ngm->tunnels, format_ip46_address, &t->local, IP46_TYPE_ANY,
+	      format_ip46_address, &t->remote, IP46_TYPE_ANY, t->local_port,
+	      t->remote_port, t->vni, t->encap_fib_index, t->sw_if_index);
 
 #if 0
   /* next_dpo not yet used by vxlan-gpe-encap node */
@@ -248,12 +249,14 @@
   .fnv_back_walk = vxlan_gpe_tunnel_back_walk,
 };
 
-#define foreach_gpe_copy_field                  \
-_(vni)                                          \
-_(protocol)                                     \
-_(mcast_sw_if_index)                            \
-_(encap_fib_index)                              \
-_(decap_fib_index)
+#define foreach_gpe_copy_field                                                \
+  _ (vni)                                                                     \
+  _ (protocol)                                                                \
+  _ (mcast_sw_if_index)                                                       \
+  _ (encap_fib_index)                                                         \
+  _ (decap_fib_index)                                                         \
+  _ (local_port)                                                              \
+  _ (remote_port)
 
 #define foreach_copy_ipv4 {                     \
   _(local.ip4.as_u32)                           \
@@ -304,8 +307,8 @@
   ip0->checksum = ip4_header_checksum (ip0);
 
   /* UDP header, randomize src port on something, maybe? */
-  h0->udp.src_port = clib_host_to_net_u16 (4790);
-  h0->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_VXLAN_GPE);
+  h0->udp.src_port = clib_host_to_net_u16 (t->local_port);
+  h0->udp.dst_port = clib_host_to_net_u16 (t->remote_port);
 
   /* VXLAN header. Are we having fun yet? */
   h0->vxlan.flags = VXLAN_GPE_FLAGS_I | VXLAN_GPE_FLAGS_P;
@@ -363,8 +366,8 @@
   ip0->dst_address.as_u64[1] = t->remote.ip6.as_u64[1];
 
   /* UDP header, randomize src port on something, maybe? */
-  h0->udp.src_port = clib_host_to_net_u16 (4790);
-  h0->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_VXLAN_GPE);
+  h0->udp.src_port = clib_host_to_net_u16 (t->local_port);
+  h0->udp.dst_port = clib_host_to_net_u16 (t->remote_port);
 
   /* VXLAN header. Are we having fun yet? */
   h0->vxlan.flags = VXLAN_GPE_FLAGS_I | VXLAN_GPE_FLAGS_P;
@@ -453,12 +456,19 @@
   vxlan6_gpe_tunnel_key_t key6, *key6_copy;
   u32 is_ip6 = a->is_ip6;
 
+  /* Set udp-ports */
+  if (a->local_port == 0)
+    a->local_port = is_ip6 ? UDP_DST_PORT_VXLAN6_GPE : UDP_DST_PORT_VXLAN_GPE;
+
+  if (a->remote_port == 0)
+    a->remote_port = is_ip6 ? UDP_DST_PORT_VXLAN6_GPE : UDP_DST_PORT_VXLAN_GPE;
+
   if (!is_ip6)
     {
       key4.local = a->local.ip4.as_u32;
       key4.remote = a->remote.ip4.as_u32;
       key4.vni = clib_host_to_net_u32 (a->vni << 8);
-      key4.pad = 0;
+      key4.port = (u32) clib_host_to_net_u16 (a->local_port);
 
       p = hash_get_mem (ngm->vxlan4_gpe_tunnel_by_key, &key4);
     }
@@ -469,6 +479,7 @@
       key6.remote.as_u64[0] = a->remote.ip6.as_u64[0];
       key6.remote.as_u64[1] = a->remote.ip6.as_u64[1];
       key6.vni = clib_host_to_net_u32 (a->vni << 8);
+      key6.port = (u32) clib_host_to_net_u16 (a->local_port);
 
       p = hash_get_mem (ngm->vxlan6_gpe_tunnel_by_key, &key6);
     }
@@ -719,12 +730,12 @@
   if (a->is_add)
     {
       /* register udp ports */
-      if (!is_ip6 && !udp_is_valid_dst_port (UDP_DST_PORT_VXLAN_GPE, 1))
-	udp_register_dst_port (ngm->vlib_main, UDP_DST_PORT_VXLAN_GPE,
-			       vxlan4_gpe_input_node.index, 1 /* is_ip4 */ );
-      if (is_ip6 && !udp_is_valid_dst_port (UDP_DST_PORT_VXLAN6_GPE, 0))
-	udp_register_dst_port (ngm->vlib_main, UDP_DST_PORT_VXLAN6_GPE,
-			       vxlan6_gpe_input_node.index, 0 /* is_ip4 */ );
+      if (!is_ip6 && !udp_is_valid_dst_port (a->local_port, 1))
+	udp_register_dst_port (ngm->vlib_main, a->local_port,
+			       vxlan4_gpe_input_node.index, 1 /* is_ip4 */);
+      if (is_ip6 && !udp_is_valid_dst_port (a->remote_port, 0))
+	udp_register_dst_port (ngm->vlib_main, a->remote_port,
+			       vxlan6_gpe_input_node.index, 0 /* is_ip4 */);
     }
 
   return 0;
@@ -749,6 +760,8 @@
   u8 protocol = VXLAN_GPE_PROTOCOL_IP4;
   u32 vni;
   u8 vni_set = 0;
+  u32 local_port = 0;
+  u32 remote_port = 0;
   int rv;
   u32 tmp;
   vnet_vxlan_gpe_add_del_tunnel_args_t _a, *a = &_a;
@@ -833,6 +846,10 @@
 	}
       else if (unformat (line_input, "vni %d", &vni))
 	vni_set = 1;
+      else if (unformat (line_input, "local_port %d", &local_port))
+	;
+      else if (unformat (line_input, "remote_port %d", &remote_port))
+	;
       else if (unformat (line_input, "next-ip4"))
 	protocol = VXLAN_GPE_PROTOCOL_IP4;
       else if (unformat (line_input, "next-ip6"))
diff --git a/src/vnet/vxlan-gpe/vxlan_gpe.h b/src/vnet/vxlan-gpe/vxlan_gpe.h
index 0f8250a..5d21ee6 100644
--- a/src/vnet/vxlan-gpe/vxlan_gpe.h
+++ b/src/vnet/vxlan-gpe/vxlan_gpe.h
@@ -64,7 +64,7 @@
 
 /**
  * @brief Key struct for IPv4 VXLAN GPE tunnel.
- * Key fields: local remote, vni
+ * Key fields: local remote, vni, udp-port
  * all fields in NET byte order
  * VNI shifted 8 bits
  */
@@ -76,7 +76,7 @@
       u32 remote;
 
       u32 vni;
-      u32 pad;
+      u32 port;
     };
     u64 as_u64[2];
   };
@@ -85,7 +85,7 @@
 
 /**
  * @brief Key struct for IPv6 VXLAN GPE tunnel.
- * Key fields: local remote, vni
+ * Key fields: local remote, vni, udp-port
  * all fields in NET byte order
  * VNI shifted 8 bits
  */
@@ -94,9 +94,21 @@
   ip6_address_t local;
   ip6_address_t remote;
   u32 vni;
+  u32 port;
 }) vxlan6_gpe_tunnel_key_t;
 /* *INDENT-ON* */
 
+typedef union
+{
+  struct
+  {
+    u32 tunnel_index;
+    u16 next_index;
+    u8 error;
+  };
+  u64 as_u64;
+} vxlan_gpe_decap_info_t;
+
 /**
  * @brief Struct for VXLAN GPE tunnel
  */
@@ -117,6 +129,10 @@
   ip46_address_t local;
   /** tunnel remote address */
   ip46_address_t remote;
+  /** local udp-port **/
+  u16 local_port;
+  /** remote udp-port **/
+  u16 remote_port;
 
   /* mcast packet output intfc index (used only if dst is mcast) */
   u32 mcast_sw_if_index;
@@ -248,6 +264,8 @@
   u32 encap_fib_index;
   u32 decap_fib_index;
   u32 vni;
+  u16 local_port;
+  u16 remote_port;
 } vnet_vxlan_gpe_add_del_tunnel_args_t;
 
 
diff --git a/src/vnet/vxlan-gpe/vxlan_gpe_api.c b/src/vnet/vxlan-gpe/vxlan_gpe_api.c
index 243ddfb..9423b27 100644
--- a/src/vnet/vxlan-gpe/vxlan_gpe_api.c
+++ b/src/vnet/vxlan-gpe/vxlan_gpe_api.c
@@ -122,6 +122,73 @@
   /* *INDENT-ON* */
 }
 
+static void
+vl_api_vxlan_gpe_add_del_tunnel_v2_t_handler (
+  vl_api_vxlan_gpe_add_del_tunnel_v2_t *mp)
+{
+  vl_api_vxlan_gpe_add_del_tunnel_v2_reply_t *rmp;
+  int rv = 0;
+  vnet_vxlan_gpe_add_del_tunnel_args_t _a, *a = &_a;
+  u32 encap_fib_index, decap_fib_index;
+  u8 protocol;
+  uword *p;
+  ip4_main_t *im = &ip4_main;
+  u32 sw_if_index = ~0;
+
+  p = hash_get (im->fib_index_by_table_id, ntohl (mp->encap_vrf_id));
+  if (!p)
+    {
+      rv = VNET_API_ERROR_NO_SUCH_FIB;
+      goto out;
+    }
+  encap_fib_index = p[0];
+
+  protocol = mp->protocol;
+
+  /* Interpret decap_vrf_id as an opaque if sending to other-than-ip4-input */
+  if (protocol == VXLAN_GPE_INPUT_NEXT_IP4_INPUT)
+    {
+      p = hash_get (im->fib_index_by_table_id, ntohl (mp->decap_vrf_id));
+      if (!p)
+	{
+	  rv = VNET_API_ERROR_NO_SUCH_INNER_FIB;
+	  goto out;
+	}
+      decap_fib_index = p[0];
+    }
+  else
+    {
+      decap_fib_index = ntohl (mp->decap_vrf_id);
+    }
+
+  clib_memset (a, 0, sizeof (*a));
+
+  a->is_add = mp->is_add;
+  ip_address_decode (&mp->local, &a->local);
+  ip_address_decode (&mp->remote, &a->remote);
+
+  /* Check src & dst are different */
+  if (ip46_address_is_equal (&a->local, &a->remote))
+    {
+      rv = VNET_API_ERROR_SAME_SRC_DST;
+      goto out;
+    }
+
+  a->local_port = ntohs (mp->local_port);
+  a->remote_port = ntohs (mp->remote_port);
+  a->is_ip6 = !ip46_address_is_ip4 (&a->local);
+  a->mcast_sw_if_index = ntohl (mp->mcast_sw_if_index);
+  a->encap_fib_index = encap_fib_index;
+  a->decap_fib_index = decap_fib_index;
+  a->protocol = protocol;
+  a->vni = ntohl (mp->vni);
+  rv = vnet_vxlan_gpe_add_del_tunnel (a, &sw_if_index);
+
+out:
+  REPLY_MACRO2 (VL_API_VXLAN_GPE_ADD_DEL_TUNNEL_V2_REPLY,
+		({ rmp->sw_if_index = ntohl (sw_if_index); }));
+}
+
 static void send_vxlan_gpe_tunnel_details
   (vxlan_gpe_tunnel_t * t, vl_api_registration_t * reg, u32 context)
 {
@@ -177,9 +244,9 @@
     {
       /* *INDENT-OFF* */
       pool_foreach (t, vgm->tunnels)
-       {
-        send_vxlan_gpe_tunnel_details(t, reg, mp->context);
-      }
+	{
+	  send_vxlan_gpe_tunnel_details (t, reg, mp->context);
+	}
       /* *INDENT-ON* */
     }
   else
@@ -194,6 +261,80 @@
     }
 }
 
+static void
+send_vxlan_gpe_tunnel_v2_details (vxlan_gpe_tunnel_t *t,
+				  vl_api_registration_t *reg, u32 context)
+{
+  vl_api_vxlan_gpe_tunnel_v2_details_t *rmp;
+  ip4_main_t *im4 = &ip4_main;
+  ip6_main_t *im6 = &ip6_main;
+  u8 is_ipv6 = !(t->flags & VXLAN_GPE_TUNNEL_IS_IPV4);
+
+  rmp = vl_msg_api_alloc (sizeof (*rmp));
+  clib_memset (rmp, 0, sizeof (*rmp));
+  rmp->_vl_msg_id =
+    ntohs (REPLY_MSG_ID_BASE + VL_API_VXLAN_GPE_TUNNEL_V2_DETAILS);
+
+  ip_address_encode (&t->local, is_ipv6 ? IP46_TYPE_IP6 : IP46_TYPE_IP4,
+		     &rmp->local);
+  ip_address_encode (&t->remote, is_ipv6 ? IP46_TYPE_IP6 : IP46_TYPE_IP4,
+		     &rmp->remote);
+  rmp->local_port = htons (t->local_port);
+  rmp->remote_port = htons (t->remote_port);
+
+  if (ip46_address_is_ip4 (&t->local))
+    {
+      rmp->encap_vrf_id = htonl (im4->fibs[t->encap_fib_index].ft_table_id);
+      rmp->decap_vrf_id = htonl (im4->fibs[t->decap_fib_index].ft_table_id);
+    }
+  else
+    {
+      rmp->encap_vrf_id = htonl (im6->fibs[t->encap_fib_index].ft_table_id);
+      rmp->decap_vrf_id = htonl (im6->fibs[t->decap_fib_index].ft_table_id);
+    }
+  rmp->mcast_sw_if_index = htonl (t->mcast_sw_if_index);
+  rmp->vni = htonl (t->vni);
+  rmp->protocol = t->protocol;
+  rmp->sw_if_index = htonl (t->sw_if_index);
+  rmp->context = context;
+
+  vl_api_send_msg (reg, (u8 *) rmp);
+}
+
+static void
+vl_api_vxlan_gpe_tunnel_v2_dump_t_handler (
+  vl_api_vxlan_gpe_tunnel_v2_dump_t *mp)
+{
+  vl_api_registration_t *reg;
+  vxlan_gpe_main_t *vgm = &vxlan_gpe_main;
+  vxlan_gpe_tunnel_t *t;
+  u32 sw_if_index;
+
+  reg = vl_api_client_index_to_registration (mp->client_index);
+  if (!reg)
+    return;
+
+  sw_if_index = ntohl (mp->sw_if_index);
+
+  if (~0 == sw_if_index)
+    {
+      pool_foreach (t, vgm->tunnels)
+	{
+	  send_vxlan_gpe_tunnel_v2_details (t, reg, mp->context);
+	}
+    }
+  else
+    {
+      if ((sw_if_index >= vec_len (vgm->tunnel_index_by_sw_if_index)) ||
+	  (~0 == vgm->tunnel_index_by_sw_if_index[sw_if_index]))
+	{
+	  return;
+	}
+      t = &vgm->tunnels[vgm->tunnel_index_by_sw_if_index[sw_if_index]];
+      send_vxlan_gpe_tunnel_v2_details (t, reg, mp->context);
+    }
+}
+
 #include <vxlan-gpe/vxlan_gpe.api.c>
 
 static clib_error_t *
diff --git a/test/test_vxlan_gpe.py b/test/test_vxlan_gpe.py
index c5d6bf0..28c31cc 100644
--- a/test/test_vxlan_gpe.py
+++ b/test/test_vxlan_gpe.py
@@ -7,13 +7,13 @@
 from template_bd import BridgeDomain
 
 from scapy.layers.l2 import Ether
-from scapy.packet import Raw
+from scapy.packet import Raw, bind_layers
 from scapy.layers.inet import IP, UDP
 from scapy.layers.vxlan import VXLAN
 
 import util
 from vpp_ip_route import VppIpRoute, VppRoutePath
-
+from vpp_vxlan_gpe_tunnel import VppVxlanGpeTunnel
 from vpp_ip import INVALID_INDEX
 
 
@@ -79,14 +79,14 @@
                 self.assertEqual(pkt[IP].dst, type(self).mcast_ip4)
         # Verify UDP destination port is VXLAN-GPE 4790, source UDP port
         #  could be arbitrary.
-        self.assertEqual(pkt[UDP].dport, type(self).dport)
+        self.assertEqual(pkt[UDP].dport, self.dport)
         # Verify UDP checksum
         self.assert_udp_checksum_valid(pkt)
         # Verify VNI
         self.assertEqual(pkt[VXLAN].vni, vni)
 
     @classmethod
-    def create_vxlan_gpe_flood_test_bd(cls, vni, n_ucast_tunnels):
+    def create_vxlan_gpe_flood_test_bd(cls, vni, n_ucast_tunnels, port):
         # Create 10 ucast vxlan tunnels under bd
         ip_range_start = 10
         ip_range_end = ip_range_start + n_ucast_tunnels
@@ -100,15 +100,18 @@
                              register=False)
             rip.add_vpp_config()
 
-            r = cls.vapi.vxlan_gpe_add_del_tunnel(
-                src_addr=cls.pg0.local_ip4,
-                dst_addr=dest_ip4,
-                vni=vni)
+            r = VppVxlanGpeTunnel(cls,
+                                  src_addr=cls.pg0.local_ip4,
+                                  dst_addr=dest_ip4,
+                                  src_port=port,
+                                  dst_port=port,
+                                  vni=vni)
+            r.add_vpp_config()
             cls.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=r.sw_if_index,
                                                 bd_id=vni)
 
     @classmethod
-    def add_del_shared_mcast_dst_load(cls, is_add):
+    def add_del_shared_mcast_dst_load(cls, port, is_add):
         """
         add or del tunnels sharing the same mcast dst
         to test vxlan_gpe ref_count mechanism
@@ -117,25 +120,30 @@
         vni_start = 1000
         vni_end = vni_start + n_shared_dst_tunnels
         for vni in range(vni_start, vni_end):
-            r = cls.vapi.vxlan_gpe_add_del_tunnel(
-                local=cls.pg0.local_ip4,
-                remote=cls.mcast_ip4,
-                mcast_sw_if_index=1,
-                vni=vni,
-                is_add=is_add)
-            if r.sw_if_index == 0xffffffff:
-                raise ValueError("bad sw_if_index: ~0")
+            r = VppVxlanGpeTunnel(cls,
+                                  src_addr=cls.pg0.local_ip4,
+                                  dst_addr=cls.mcast_ip4,
+                                  src_port=port,
+                                  dst_port=port,
+                                  mcast_sw_if_index=1,
+                                  vni=vni)
+            if is_add:
+                r.add_vpp_config()
+                if r.sw_if_index == 0xffffffff:
+                    raise ValueError("bad sw_if_index: ~0")
+            else:
+                r.remove_vpp_config()
 
     @classmethod
-    def add_shared_mcast_dst_load(cls):
-        cls.add_del_shared_mcast_dst_load(is_add=1)
+    def add_shared_mcast_dst_load(cls, port):
+        cls.add_del_shared_mcast_dst_load(port=port, is_add=1)
 
     @classmethod
-    def del_shared_mcast_dst_load(cls):
-        cls.add_del_shared_mcast_dst_load(is_add=0)
+    def del_shared_mcast_dst_load(cls, port):
+        cls.add_del_shared_mcast_dst_load(port=port, is_add=0)
 
     @classmethod
-    def add_del_mcast_tunnels_load(cls, is_add):
+    def add_del_mcast_tunnels_load(cls, port, is_add):
         """
         add or del tunnels to test vxlan_gpe stability
         """
@@ -145,20 +153,25 @@
         for dest_ip4 in ip4_range(cls.mcast_ip4, ip_range_start,
                                   ip_range_end):
             vni = int(dest_ip4.split(".")[3])
-            cls.vapi.vxlan_gpe_add_del_tunnel(
-                src_addr=cls.pg0.local_ip4,
-                dst_addr=dest_ip4,
-                mcast_sw_if_index=1,
-                vni=vni,
-                is_add=is_add)
+            r = VppVxlanGpeTunnel(cls,
+                                  src_addr=cls.pg0.local_ip4,
+                                  dst_addr=dest_ip4,
+                                  src_port=port,
+                                  dst_port=port,
+                                  mcast_sw_if_index=1,
+                                  vni=vni)
+            if is_add:
+                r.add_vpp_config()
+            else:
+                r.remove_vpp_config()
 
     @classmethod
-    def add_mcast_tunnels_load(cls):
-        cls.add_del_mcast_tunnels_load(is_add=1)
+    def add_mcast_tunnels_load(cls, port):
+        cls.add_del_mcast_tunnels_load(port=port, is_add=1)
 
     @classmethod
-    def del_mcast_tunnels_load(cls):
-        cls.add_del_mcast_tunnels_load(is_add=0)
+    def del_mcast_tunnels_load(cls, port):
+        cls.add_del_mcast_tunnels_load(port=port, is_add=0)
 
     # Class method to start the VXLAN-GPE test case.
     #  Overrides setUpClass method in VppTestCase class.
@@ -170,7 +183,6 @@
         super(TestVxlanGpe, cls).setUpClass()
 
         try:
-            cls.dport = 4790
             cls.flags = 0x0c
 
             # Create 2 pg interfaces.
@@ -187,55 +199,120 @@
             # Our Multicast address
             cls.mcast_ip4 = '239.1.1.1'
             cls.mcast_mac = util.mcast_ip_to_mac(cls.mcast_ip4)
-
-            # Create VXLAN-GPE VTEP on VPP pg0, and put vxlan_gpe_tunnel0
-            # and pg1 into BD.
-            cls.single_tunnel_vni = 0xabcde
-            cls.single_tunnel_bd = 11
-            r = cls.vapi.vxlan_gpe_add_del_tunnel(
-                src_addr=cls.pg0.local_ip4,
-                dst_addr=cls.pg0.remote_ip4,
-                vni=cls.single_tunnel_vni)
-            cls.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=r.sw_if_index,
-                                                bd_id=cls.single_tunnel_bd)
-            cls.vapi.sw_interface_set_l2_bridge(
-                rx_sw_if_index=cls.pg1.sw_if_index, bd_id=cls.single_tunnel_bd)
-
-            # Setup vni 2 to test multicast flooding
-            cls.n_ucast_tunnels = 10
-            cls.mcast_flood_bd = 12
-            cls.create_vxlan_gpe_flood_test_bd(cls.mcast_flood_bd,
-                                               cls.n_ucast_tunnels)
-            r = cls.vapi.vxlan_gpe_add_del_tunnel(
-                src_addr=cls.pg0.local_ip4,
-                dst_addr=cls.mcast_ip4,
-                mcast_sw_if_index=1,
-                vni=cls.mcast_flood_bd)
-            cls.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=r.sw_if_index,
-                                                bd_id=cls.mcast_flood_bd)
-            cls.vapi.sw_interface_set_l2_bridge(
-                rx_sw_if_index=cls.pg2.sw_if_index, bd_id=cls.mcast_flood_bd)
-
-            # Add and delete mcast tunnels to check stability
-            cls.add_shared_mcast_dst_load()
-            cls.add_mcast_tunnels_load()
-            cls.del_shared_mcast_dst_load()
-            cls.del_mcast_tunnels_load()
-
-            # Setup vni 3 to test unicast flooding
-            cls.ucast_flood_bd = 13
-            cls.create_vxlan_gpe_flood_test_bd(cls.ucast_flood_bd,
-                                               cls.n_ucast_tunnels)
-            cls.vapi.sw_interface_set_l2_bridge(
-                rx_sw_if_index=cls.pg3.sw_if_index, bd_id=cls.ucast_flood_bd)
         except Exception:
-            super(TestVxlanGpe, cls).tearDownClass()
+            cls.tearDownClass()
             raise
 
     @classmethod
     def tearDownClass(cls):
         super(TestVxlanGpe, cls).tearDownClass()
 
+    def setUp(self):
+        super(TestVxlanGpe, self).setUp()
+
+    def createVxLANInterfaces(self, port=4790):
+        # Create VXLAN-GPE VTEP on VPP pg0, and put vxlan_gpe_tunnel0
+        # and pg1 into BD.
+        self.dport = port
+
+        self.single_tunnel_vni = 0xabcde
+        self.single_tunnel_bd = 11
+        r = VppVxlanGpeTunnel(self,
+                              src_addr=self.pg0.local_ip4,
+                              dst_addr=self.pg0.remote_ip4,
+                              src_port=port,
+                              dst_port=port,
+                              vni=self.single_tunnel_vni)
+        r.add_vpp_config()
+        self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=r.sw_if_index,
+                                             bd_id=self.single_tunnel_bd)
+        self.vapi.sw_interface_set_l2_bridge(
+            rx_sw_if_index=self.pg1.sw_if_index, bd_id=self.single_tunnel_bd)
+
+        # Setup vni 2 to test multicast flooding
+        self.n_ucast_tunnels = 10
+        self.mcast_flood_bd = 12
+        self.create_vxlan_gpe_flood_test_bd(self.mcast_flood_bd,
+                                            self.n_ucast_tunnels,
+                                            self.dport)
+        r = VppVxlanGpeTunnel(self,
+                              src_addr=self.pg0.local_ip4,
+                              dst_addr=self.mcast_ip4,
+                              src_port=port,
+                              dst_port=port,
+                              mcast_sw_if_index=1,
+                              vni=self.mcast_flood_bd)
+        r.add_vpp_config()
+        self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=r.sw_if_index,
+                                             bd_id=self.mcast_flood_bd)
+        self.vapi.sw_interface_set_l2_bridge(
+            rx_sw_if_index=self.pg2.sw_if_index, bd_id=self.mcast_flood_bd)
+
+        # Add and delete mcast tunnels to check stability
+        self.add_shared_mcast_dst_load(self.dport)
+        self.add_mcast_tunnels_load(self.dport)
+        self.del_shared_mcast_dst_load(self.dport)
+        self.del_mcast_tunnels_load(self.dport)
+
+        # Setup vni 3 to test unicast flooding
+        self.ucast_flood_bd = 13
+        self.create_vxlan_gpe_flood_test_bd(self.ucast_flood_bd,
+                                            self.n_ucast_tunnels,
+                                            self.dport)
+        self.vapi.sw_interface_set_l2_bridge(
+            rx_sw_if_index=self.pg3.sw_if_index, bd_id=self.ucast_flood_bd)
+
+        # Set scapy listen custom port for VxLAN
+        bind_layers(UDP, VXLAN, dport=self.dport)
+
+    """
+    Tests with default port (4790)
+    """
+    def test_decap(self):
+        """ Decapsulation test
+        from BridgeDoman
+        """
+        self.createVxLANInterfaces()
+        super(TestVxlanGpe, self).test_decap()
+
+    def test_encap(self):
+        """ Encapsulation test
+        from BridgeDoman
+        """
+        self.createVxLANInterfaces()
+        super(TestVxlanGpe, self).test_encap()
+
+    def test_ucast_flood(self):
+        """ Unicast flood test
+        from BridgeDoman
+        """
+        self.createVxLANInterfaces()
+        super(TestVxlanGpe, self).test_ucast_flood()
+
+    """
+    Tests with custom port (1112)
+    """
+    def test_decap_custom_port(self):
+        """ Decapsulation test custom port
+        from BridgeDoman
+        """
+        self.createVxLANInterfaces(1112)
+        super(TestVxlanGpe, self).test_decap()
+
+    def test_encap_custom_port(self):
+        """ Encapsulation test custom port
+        from BridgeDoman
+        """
+        self.createVxLANInterfaces(1112)
+        super(TestVxlanGpe, self).test_encap()
+
+    def test_ucast_flood_custom_port(self):
+        """ Unicast flood test custom port
+        from BridgeDoman
+        """
+        self.createVxLANInterfaces(1112)
+        super(TestVxlanGpe, self).test_ucast_flood()
+
     @unittest.skip("test disabled for vxlan-gpe")
     def test_mcast_flood(self):
         """ inherited from BridgeDomain """
diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py
index 7e87b2c..0d52145 100644
--- a/test/vpp_papi_provider.py
+++ b/test/vpp_papi_provider.py
@@ -93,8 +93,6 @@
     'vxlan_add_del_tunnel': {'mcast_sw_if_index': 4294967295, 'is_add': 1,
                              'decap_next_index': 4294967295,
                              'instance': 4294967295, },
-    'vxlan_gpe_add_del_tunnel': {'mcast_sw_if_index': 4294967295, 'is_add': 1,
-                                 'protocol': 3, },
     'want_bfd_events': {'enable_disable': 1, },
     'want_igmp_events': {'enable': 1, },
     'want_interface_events': {'enable_disable': 1, },
@@ -654,41 +652,6 @@
                             'is_ip6': is_ip6
                         }})
 
-    def vxlan_gpe_add_del_tunnel(
-            self,
-            src_addr,
-            dst_addr,
-            mcast_sw_if_index=0xFFFFFFFF,
-            is_add=1,
-            is_ipv6=0,
-            encap_vrf_id=0,
-            decap_vrf_id=0,
-            protocol=3,
-            vni=0):
-        """
-
-        :param local:
-        :param remote:
-        :param is_add:  (Default value = 1)
-        :param is_ipv6:  (Default value = 0)
-        :param encap_vrf_id:  (Default value = 0)
-        :param decap_vrf_id:  (Default value = 0)
-        :param mcast_sw_if_index:  (Default value = 0xFFFFFFFF)
-        :param protocol:  (Default value = 3)
-        :param vni:  (Default value = 0)
-
-        """
-        return self.api(self.papi.vxlan_gpe_add_del_tunnel,
-                        {'is_add': is_add,
-                         'is_ipv6': is_ipv6,
-                         'local': src_addr,
-                         'remote': dst_addr,
-                         'mcast_sw_if_index': mcast_sw_if_index,
-                         'encap_vrf_id': encap_vrf_id,
-                         'decap_vrf_id': decap_vrf_id,
-                         'protocol': protocol,
-                         'vni': vni})
-
     def vxlan_gbp_tunnel_dump(self, sw_if_index=0xffffffff):
         return self.api(self.papi.vxlan_gbp_tunnel_dump,
                         {'sw_if_index': sw_if_index})
diff --git a/test/vpp_vxlan_gpe_tunnel.py b/test/vpp_vxlan_gpe_tunnel.py
new file mode 100644
index 0000000..cff5e45
--- /dev/null
+++ b/test/vpp_vxlan_gpe_tunnel.py
@@ -0,0 +1,83 @@
+from vpp_interface import VppInterface
+from vpp_papi import VppEnum
+
+
+INDEX_INVALID = 0xffffffff
+DEFAULT_PORT = 4790
+UNDEFINED_PORT = 0
+
+
+def find_vxlan_gpe_tunnel(test, src, dst, s_port, d_port, vni):
+    ts = test.vapi.vxlan_gpe_tunnel_v2_dump(INDEX_INVALID)
+
+    src_port = DEFAULT_PORT
+    if s_port != UNDEFINED_PORT:
+        src_port = s_port
+
+    dst_port = DEFAULT_PORT
+    if d_port != UNDEFINED_PORT:
+        dst_port = d_port
+
+    for t in ts:
+        if src == str(t.local) and \
+           dst == str(t.remote) and \
+           src_port == t.local_port and \
+           dst_port == t.remote_port and \
+           t.vni == vni:
+            return t.sw_if_index
+    return INDEX_INVALID
+
+
+class VppVxlanGpeTunnel(VppInterface):
+    """
+    VPP VXLAN GPE interface
+    """
+
+    def __init__(self, test, src_addr, dst_addr, vni,
+                 src_port=UNDEFINED_PORT, dst_port=UNDEFINED_PORT,
+                 mcast_sw_if_index=INDEX_INVALID,
+                 encap_vrf_id=None,
+                 decap_vrf_id=None, protocol=3):
+        """ Create VXLAN GPE Tunnel interface """
+        super(VppVxlanGpeTunnel, self).__init__(test)
+        self.src = src_addr
+        self.dst = dst_addr
+        self.vni = vni
+        self.src_port = src_port
+        self.dst_port = dst_port
+        self.mcast_sw_if_index = mcast_sw_if_index
+        self.encap_vrf_id = encap_vrf_id
+        self.decap_vrf_id = decap_vrf_id
+        self.protocol = 3
+
+    def add_vpp_config(self):
+        reply = self.test.vapi.vxlan_gpe_add_del_tunnel_v2(
+            is_add=1, local=self.src, remote=self.dst, vni=self.vni,
+            local_port=self.src_port, remote_port=self.dst_port,
+            mcast_sw_if_index=self.mcast_sw_if_index,
+            encap_vrf_id=self.encap_vrf_id,
+            decap_vrf_id=self.decap_vrf_id,
+            protocol=self.protocol)
+        self.set_sw_if_index(reply.sw_if_index)
+        self._test.registry.register(self, self._test.logger)
+
+    def remove_vpp_config(self):
+        self.test.vapi.vxlan_gpe_add_del_tunnel_v2(
+            is_add=0, local=self.src, remote=self.dst, vni=self.vni,
+            local_port=self.src_port, remote_port=self.dst_port,
+            mcast_sw_if_index=self.mcast_sw_if_index,
+            encap_vrf_id=self.encap_vrf_id,
+            decap_vrf_id=self.decap_vrf_id,
+            protocol=self.protocol)
+
+    def query_vpp_config(self):
+        return (INDEX_INVALID != find_vxlan_gpe_tunnel(self._test,
+                                                       self.src,
+                                                       self.dst,
+                                                       self.src_port,
+                                                       self.dst_port,
+                                                       self.vni))
+
+    def object_id(self):
+        return "vxlan-%d-%d-%s-%s" % (self.sw_if_index, self.vni,
+                                      self.src, self.dst)