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/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 *